From 8975161edb3f41228256fdde69c94a8787434b07 Mon Sep 17 00:00:00 2001 From: Eoghanmc22 Date: Fri, 19 Mar 2021 22:50:28 -0400 Subject: [PATCH 001/364] use a ConcurrentHashMap key set instead of a CopyOnWriteArraySet for viewers so .contains() is O(1) --- src/main/java/net/minestom/server/entity/Entity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index b6e8aa639..2a175e6a3 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -81,7 +81,7 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission private boolean autoViewable; private final int id; - protected final Set viewers = new CopyOnWriteArraySet<>(); + protected final Set viewers = ConcurrentHashMap.newKeySet(); private final Set unmodifiableViewers = Collections.unmodifiableSet(viewers); private Data data; private final Set permissions = new CopyOnWriteArraySet<>(); From cdfa0dcfde2e4c510e01e70c5ec37090fbd69abe Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 20 Mar 2021 08:38:50 +0100 Subject: [PATCH 002/364] Use pooled buffer to prevent reallocation when copying --- .../minestom/server/network/player/NettyPlayerConnection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 19318e441..036dfe368 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -16,6 +16,7 @@ import net.minestom.server.network.netty.codec.PacketCompressor; import net.minestom.server.network.netty.packet.FramedPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.login.SetCompressionPacket; +import net.minestom.server.utils.BufUtils; import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.cache.CacheablePacket; import net.minestom.server.utils.cache.TemporaryCache; @@ -61,7 +62,7 @@ public class NettyPlayerConnection extends PlayerConnection { private PlayerSkin bungeeSkin; private static final int FLUSH_SIZE = 20000; - private final ByteBuf tickBuffer = Unpooled.directBuffer(); + private final ByteBuf tickBuffer = BufUtils.getBuffer(true); public NettyPlayerConnection(@NotNull SocketChannel channel) { super(); From a9a0a672f0e751f3df5c8ae757ad57f9d2b5e155 Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 20 Mar 2021 10:20:45 +0100 Subject: [PATCH 003/364] Reduce deflater level, prevent race condition when releasing tick buffer --- .../minestom/server/network/netty/channel/ClientChannel.java | 5 ++++- .../server/network/netty/codec/PacketCompressor.java | 2 +- src/main/java/net/minestom/server/utils/PacketUtils.java | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java b/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java index ad9abe2d9..f915afccf 100644 --- a/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java +++ b/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java @@ -71,7 +71,10 @@ public class ClientChannel extends SimpleChannelInboundHandler { // Release tick buffer if (playerConnection instanceof NettyPlayerConnection) { - ((NettyPlayerConnection) playerConnection).getTickBuffer().release(); + final ByteBuf tickBuffer = ((NettyPlayerConnection) playerConnection).getTickBuffer(); + synchronized (tickBuffer) { + tickBuffer.release(); + } } } } diff --git a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java b/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java index 514f9f90d..ebee11ebc 100644 --- a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java +++ b/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java @@ -36,7 +36,7 @@ public class PacketCompressor extends ByteToMessageCodec { private final byte[] buffer = new byte[8192]; - private final Deflater deflater = new Deflater(); + private final Deflater deflater = new Deflater(3); private final Inflater inflater = new Inflater(); public PacketCompressor(int threshold) { diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index b391f96c2..bf71ecb88 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -25,7 +25,7 @@ import java.util.zip.Deflater; public final class PacketUtils { private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager(); - private static final ThreadLocal DEFLATER = ThreadLocal.withInitial(Deflater::new); + private static final ThreadLocal DEFLATER = ThreadLocal.withInitial(() -> new Deflater(3)); private PacketUtils() { From cd273b4d99954cc624794d16aecc8d51e4a53705 Mon Sep 17 00:00:00 2001 From: themode Date: Sat, 20 Mar 2021 11:59:02 +0100 Subject: [PATCH 004/364] Use direct buffer for framed packets --- .../server/network/player/NettyPlayerConnection.java | 8 +++++--- src/main/java/net/minestom/server/utils/PacketUtils.java | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 036dfe368..f102035b4 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -1,7 +1,6 @@ package net.minestom.server.network.player; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.socket.SocketChannel; @@ -153,10 +152,13 @@ public class NettyPlayerConnection extends PlayerConnection { write(new FramedPacket(timedBuffer.getBuffer())); } - } else + } else { write(serverPacket); - } else + } + } else { + // Player is probably not logged yet writeAndFlush(serverPacket); + } } } diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index bf71ecb88..629577816 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -49,7 +49,7 @@ public final class PacketUtils { // Send grouped packet... final boolean success = PACKET_LISTENER_MANAGER.processServerPacket(packet, players); if (success) { - final ByteBuf finalBuffer = createFramedPacket(packet, false); + final ByteBuf finalBuffer = createFramedPacket(packet, true); final FramedPacket framedPacket = new FramedPacket(finalBuffer); // Send packet to all players @@ -62,6 +62,8 @@ public final class PacketUtils { if (playerValidator != null && !playerValidator.isValid(player)) continue; + finalBuffer.retain(); + final PlayerConnection playerConnection = player.getPlayerConnection(); if (playerConnection instanceof NettyPlayerConnection) { final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) playerConnection; @@ -69,7 +71,10 @@ public final class PacketUtils { } else { playerConnection.sendPacket(packet); } + + finalBuffer.release(); } + finalBuffer.release(); // Release last reference } } else { // Write the same packet for each individual players From a9577f084aed968f3f9f82d2636a53d7ff0648c8 Mon Sep 17 00:00:00 2001 From: Archy-X Date: Sat, 20 Mar 2021 18:09:14 -0700 Subject: [PATCH 005/364] Add ItemStack constructor without amount parameter --- src/main/java/net/minestom/server/item/ItemStack.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/minestom/server/item/ItemStack.java b/src/main/java/net/minestom/server/item/ItemStack.java index ef1ee8d99..01f0ae172 100644 --- a/src/main/java/net/minestom/server/item/ItemStack.java +++ b/src/main/java/net/minestom/server/item/ItemStack.java @@ -96,6 +96,10 @@ public class ItemStack implements DataContainer, PublicCloneable { this(material, amount, (short) 0); } + public ItemStack(@NotNull Material material) { + this(material, (byte) 1, (short) 0); + } + /** * Gets a new {@link ItemStack} with the material sets to {@link Material#AIR}. *

From 3e3cb4b21389c489e257fbeebb16578256972530 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 09:09:32 +0100 Subject: [PATCH 006/364] Fix command name recursion --- .../command/builder/parser/CommandParser.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java b/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java index fb8cfe713..124affb33 100644 --- a/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java +++ b/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java @@ -20,8 +20,8 @@ public class CommandParser { private static final CommandManager COMMAND_MANAGER = MinecraftServer.getCommandManager(); @Nullable - public static CommandQueryResult findCommand(@NotNull String commandName, @NotNull String[] args) { - Command command = COMMAND_MANAGER.getDispatcher().findCommand(commandName); + public static CommandQueryResult findCommand(@Nullable Command parentCommand, @NotNull String commandName, @NotNull String[] args) { + Command command = parentCommand == null ? COMMAND_MANAGER.getDispatcher().findCommand(commandName) : parentCommand; if (command == null) { return null; } @@ -31,21 +31,18 @@ public class CommandParser { commandQueryResult.commandName = commandName; commandQueryResult.args = args; - boolean correct; - do { - correct = false; - - if (commandQueryResult.args.length > 0) { - final String firstArgument = commandQueryResult.args[0]; - for (Command subcommand : command.getSubcommands()) { - if ((correct = Command.isValidName(subcommand, firstArgument))) { - commandQueryResult.command = subcommand; - commandQueryResult.commandName = firstArgument; - commandQueryResult.args = Arrays.copyOfRange(args, 1, args.length); - } + if (commandQueryResult.args.length > 0) { + final String subCommandName = commandQueryResult.args[0]; + for (Command subcommand : command.getSubcommands()) { + if (Command.isValidName(subcommand, subCommandName)) { + final String[] subArgs = Arrays.copyOfRange(args, 1, args.length); + commandQueryResult.command = subcommand; + commandQueryResult.commandName = subCommandName; + commandQueryResult.args = subArgs; + return findCommand(subcommand, subCommandName, subArgs); } } - } while (correct); + } return commandQueryResult; } @@ -57,7 +54,7 @@ public class CommandParser { String[] args = new String[parts.length - 1]; System.arraycopy(parts, 1, args, 0, args.length); - return CommandParser.findCommand(commandName, args); + return CommandParser.findCommand(null, commandName, args); } public static void parse(@Nullable CommandSyntax syntax, @NotNull Argument[] commandArguments, @NotNull String[] inputArguments, From 9817919988023328166c5003b446b538609b33ea Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 09:25:22 +0100 Subject: [PATCH 007/364] CommandParser#findCommand style cleanup --- .../server/command/builder/parser/CommandParser.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java b/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java index 124affb33..20cabcfa9 100644 --- a/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java +++ b/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java @@ -31,8 +31,9 @@ public class CommandParser { commandQueryResult.commandName = commandName; commandQueryResult.args = args; - if (commandQueryResult.args.length > 0) { - final String subCommandName = commandQueryResult.args[0]; + // Search for subcommand + if (args.length > 0) { + final String subCommandName = args[0]; for (Command subcommand : command.getSubcommands()) { if (Command.isValidName(subcommand, subCommandName)) { final String[] subArgs = Arrays.copyOfRange(args, 1, args.length); From 0fc8ae953f3680722b95329e5be0db83696212bc Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 11:38:48 +0100 Subject: [PATCH 008/364] Improve entities caching inside instance & chunks --- .../minestom/server/instance/Instance.java | 38 ++++++------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index a311d08a7..f941137ea 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -1,14 +1,14 @@ package net.minestom.server.instance; import com.google.common.collect.Queues; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minestom.server.MinecraftServer; import net.minestom.server.UpdateManager; import net.minestom.server.data.Data; import net.minestom.server.data.DataContainer; -import net.minestom.server.entity.*; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.EntityCreature; +import net.minestom.server.entity.ExperienceOrb; +import net.minestom.server.entity.Player; import net.minestom.server.entity.pathfinding.PFInstanceSpace; import net.minestom.server.event.Event; import net.minestom.server.event.EventCallback; @@ -40,7 +40,6 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Consumer; /** @@ -81,14 +80,13 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta private final Map>> extensionCallbacks = new ConcurrentHashMap<>(); // Entities present in this instance - protected final Set entities = new CopyOnWriteArraySet<>(); - protected final Set players = new CopyOnWriteArraySet<>(); - protected final Set creatures = new CopyOnWriteArraySet<>(); - protected final Set objectEntities = new CopyOnWriteArraySet<>(); - protected final Set experienceOrbs = new CopyOnWriteArraySet<>(); + protected final Set entities = ConcurrentHashMap.newKeySet(); + protected final Set players = ConcurrentHashMap.newKeySet(); + protected final Set creatures = ConcurrentHashMap.newKeySet(); + protected final Set experienceOrbs = ConcurrentHashMap.newKeySet(); // Entities per chunk - protected final Long2ObjectMap> chunkEntities = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); - private Object entitiesLock = new Object(); // Lock used to prevent the entities Set and Map to be subject to race condition + protected final Map> chunkEntities = new ConcurrentHashMap<>(); + private final Object entitiesLock = new Object(); // Lock used to prevent the entities Set and Map to be subject to race condition // the uuid of this instance protected UUID uniqueId; @@ -467,16 +465,6 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta return Collections.unmodifiableSet(creatures); } - /** - * Gets the object entities in the instance; - * - * @return an unmodifiable {@link Set} containing all the object entities in the instance - */ - @NotNull - public Set getObjectEntities() { - return Collections.unmodifiableSet(objectEntities); - } - /** * Gets the experience orbs in the instance. * @@ -957,8 +945,6 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta this.players.add((Player) entity); } else if (entity instanceof EntityCreature) { this.creatures.add((EntityCreature) entity); - } else if (entity instanceof ObjectEntity) { - this.objectEntities.add((ObjectEntity) entity); } else if (entity instanceof ExperienceOrb) { this.experienceOrbs.add((ExperienceOrb) entity); } @@ -984,8 +970,6 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta this.players.remove(entity); } else if (entity instanceof EntityCreature) { this.creatures.remove(entity); - } else if (entity instanceof ObjectEntity) { - this.objectEntities.remove(entity); } else if (entity instanceof ExperienceOrb) { this.experienceOrbs.remove(entity); } @@ -994,7 +978,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta @NotNull private Set getEntitiesInChunk(long index) { - return chunkEntities.computeIfAbsent(index, i -> new CopyOnWriteArraySet<>()); + return chunkEntities.computeIfAbsent(index, i -> ConcurrentHashMap.newKeySet()); } /** From aa2f29bbc26719ecbc116ff95dbafbdb45c40c8c Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 11:45:32 +0100 Subject: [PATCH 009/364] Remove preventive write, only update cache when needed --- .../network/packet/server/play/ChunkDataPacket.java | 3 ++- .../packet/server/play/UpdateLightPacket.java | 3 ++- .../server/network/player/NettyPlayerConnection.java | 12 +----------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java index 35f13a9e9..a3aad80ae 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ChunkDataPacket.java @@ -25,11 +25,12 @@ import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.UUID; +import java.util.concurrent.TimeUnit; public class ChunkDataPacket implements ServerPacket, CacheablePacket { private static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager(); - private static final TemporaryCache CACHE = new TemporaryCache<>(30000L); + private static final TemporaryCache CACHE = new TemporaryCache<>(5, TimeUnit.MINUTES); public boolean fullChunk; public Biome[] biomes; diff --git a/src/main/java/net/minestom/server/network/packet/server/play/UpdateLightPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/UpdateLightPacket.java index b5d793e6c..68fe51a54 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/UpdateLightPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/UpdateLightPacket.java @@ -11,10 +11,11 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.UUID; +import java.util.concurrent.TimeUnit; public class UpdateLightPacket implements ServerPacket, CacheablePacket { - private static final TemporaryCache CACHE = new TemporaryCache<>(30000L); + private static final TemporaryCache CACHE = new TemporaryCache<>(5, TimeUnit.MINUTES); public int chunkX; public int chunkZ; diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index f102035b4..009c85869 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -60,7 +60,6 @@ public class NettyPlayerConnection extends PlayerConnection { private UUID bungeeUuid; private PlayerSkin bungeeSkin; - private static final int FLUSH_SIZE = 20000; private final ByteBuf tickBuffer = BufUtils.getBuffer(true); public NettyPlayerConnection(@NotNull SocketChannel channel) { @@ -146,9 +145,9 @@ public class NettyPlayerConnection extends PlayerConnection { if (shouldUpdate) { final ByteBuf buffer = PacketUtils.createFramedPacket(serverPacket, false); timedBuffer = new TimedBuffer(buffer, timestamp); + temporaryCache.cache(identifier, timedBuffer); } - temporaryCache.cache(identifier, timedBuffer); write(new FramedPacket(timedBuffer.getBuffer())); } @@ -168,7 +167,6 @@ public class NettyPlayerConnection extends PlayerConnection { synchronized (tickBuffer) { final ByteBuf body = framedPacket.getBody(); tickBuffer.writeBytes(body, body.readerIndex(), body.readableBytes()); - preventiveWrite(); } return; } else if (message instanceof ServerPacket) { @@ -176,14 +174,12 @@ public class NettyPlayerConnection extends PlayerConnection { final ByteBuf buffer = PacketUtils.createFramedPacket(serverPacket, true); synchronized (tickBuffer) { tickBuffer.writeBytes(buffer); - preventiveWrite(); } buffer.release(); return; } else if (message instanceof ByteBuf) { synchronized (tickBuffer) { tickBuffer.writeBytes((ByteBuf) message); - preventiveWrite(); } return; } @@ -203,12 +199,6 @@ public class NettyPlayerConnection extends PlayerConnection { } } - private void preventiveWrite() { - if (tickBuffer.writerIndex() > FLUSH_SIZE) { - writeWaitingPackets(); - } - } - private void writeWaitingPackets() { synchronized (tickBuffer) { final ByteBuf copy = tickBuffer.copy(); From cd75740e41bc02d6aa31d6a99a3654fc518b1c9d Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 11:46:04 +0100 Subject: [PATCH 010/364] Fix compilation --- .../server/utils/cache/TemporaryCache.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/minestom/server/utils/cache/TemporaryCache.java b/src/main/java/net/minestom/server/utils/cache/TemporaryCache.java index ee09624c7..ed0b43150 100644 --- a/src/main/java/net/minestom/server/utils/cache/TemporaryCache.java +++ b/src/main/java/net/minestom/server/utils/cache/TemporaryCache.java @@ -16,18 +16,15 @@ import java.util.concurrent.TimeUnit; public class TemporaryCache { private final Cache cache; - private final long keepTime; /** * Creates a new temporary cache. * - * @param keepTime the time before considering an object unused in milliseconds - * @see #getKeepTime() + * @param duration the time before considering an object unused */ - public TemporaryCache(long keepTime) { - this.keepTime = keepTime; + public TemporaryCache(long duration, TimeUnit timeUnit) { this.cache = CacheBuilder.newBuilder() - .expireAfterWrite(keepTime, TimeUnit.MILLISECONDS) + .expireAfterWrite(duration, timeUnit) .softValues() .build(); } @@ -52,13 +49,4 @@ public class TemporaryCache { public T retrieve(@NotNull UUID identifier) { return cache.getIfPresent(identifier); } - - /** - * Gets the time an object will be kept without being retrieved. - * - * @return the keep time in milliseconds - */ - public long getKeepTime() { - return keepTime; - } } From 20280096851ec185ff365c73070f554e3fc71640 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 12:02:23 +0100 Subject: [PATCH 011/364] Temporarily disable traffic handler, improve chunk viewers set --- src/main/java/net/minestom/server/instance/Chunk.java | 4 ++-- .../java/net/minestom/server/network/netty/NettyServer.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java index 81e5e2e0f..39fa03580 100644 --- a/src/main/java/net/minestom/server/instance/Chunk.java +++ b/src/main/java/net/minestom/server/instance/Chunk.java @@ -29,7 +29,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ConcurrentHashMap; // TODO light data & API @@ -72,7 +72,7 @@ public abstract class Chunk implements Viewable, DataContainer { private boolean readOnly; protected volatile boolean loaded = true; - protected final Set viewers = new CopyOnWriteArraySet<>(); + protected final Set viewers = ConcurrentHashMap.newKeySet(); private final Set unmodifiableViewers = Collections.unmodifiableSet(viewers); // Path finding diff --git a/src/main/java/net/minestom/server/network/netty/NettyServer.java b/src/main/java/net/minestom/server/network/netty/NettyServer.java index 9c5ff8733..44541205a 100644 --- a/src/main/java/net/minestom/server/network/netty/NettyServer.java +++ b/src/main/java/net/minestom/server/network/netty/NettyServer.java @@ -167,7 +167,8 @@ public final class NettyServer { ChannelPipeline pipeline = ch.pipeline(); - pipeline.addLast(TRAFFIC_LIMITER_HANDLER_NAME, globalTrafficHandler); + // TODO enable when properly implemented (dynamic limit based on the number of clients) + //pipeline.addLast(TRAFFIC_LIMITER_HANDLER_NAME, globalTrafficHandler); // First check should verify if the packet is a legacy ping (from 1.6 version and earlier) // Removed from the pipeline later in LegacyPingHandler if unnecessary (>1.6) From 126382f55d63eb801e3a7f2b7f4b45fcc4a2c78f Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 13:22:49 +0100 Subject: [PATCH 012/364] Reduce memory allocation --- .../network/player/NettyPlayerConnection.java | 4 +--- .../minestom/server/utils/PacketUtils.java | 24 +++++++++---------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 009c85869..4b5c84f7d 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -171,11 +171,9 @@ public class NettyPlayerConnection extends PlayerConnection { return; } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; - final ByteBuf buffer = PacketUtils.createFramedPacket(serverPacket, true); synchronized (tickBuffer) { - tickBuffer.writeBytes(buffer); + PacketUtils.createFramedPacket(serverPacket, true, tickBuffer); } - buffer.release(); return; } else if (message instanceof ByteBuf) { synchronized (tickBuffer) { diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 629577816..8707efd65 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -229,40 +229,38 @@ public final class PacketUtils { } /** - * Creates a "framed packet" (packet which can be send and understood by a Minecraft client) - * from a server packet. + * Writes a "framed packet" (packet which can be send and understood by a Minecraft client) + * from a server packet, directly into an output buffer. *

* Can be used if you want to store a raw buffer and send it later without the additional writing cost. * Compression is applied if {@link MinecraftServer#getCompressionThreshold()} is greater than 0. * * @param serverPacket the server packet to write - * @return the framed packet from the server one */ - @NotNull - public static ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer) { + public static void createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer, ByteBuf output) { ByteBuf packetBuf = writePacket(serverPacket); if (MinecraftServer.getCompressionThreshold() > 0) { ByteBuf compressedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); - ByteBuf framedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); final Deflater deflater = DEFLATER.get(); compressBuffer(deflater, null, packetBuf, compressedBuf); packetBuf.release(); - frameBuffer(compressedBuf, framedBuf); + frameBuffer(compressedBuf, output); compressedBuf.release(); - - return framedBuf; } else { - ByteBuf framedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); - frameBuffer(packetBuf, framedBuf); + frameBuffer(packetBuf, output); packetBuf.release(); - - return framedBuf; } + } + @NotNull + public static ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer) { + ByteBuf framedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); + createFramedPacket(serverPacket, directBuffer, framedBuf); + return framedBuf; } } From 9893bb72c80e260efee447768adde6ef6a4a15d6 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 13:38:25 +0100 Subject: [PATCH 013/364] don't send a close inventory packet when opening a new one --- src/main/java/net/minestom/server/entity/Player.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index e19eb4a76..b181368a5 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1824,7 +1824,7 @@ public class Player extends LivingEntity implements CommandSender { callCancellableEvent(InventoryOpenEvent.class, inventoryOpenEvent, () -> { if (getOpenInventory() != null) { - closeInventory(); + getOpenInventory().removeViewer(this); } Inventory newInventory = inventoryOpenEvent.getInventory(); From 95eddf47de53bda166c333feb7ba969873708ff9 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 14:26:34 +0100 Subject: [PATCH 014/364] Auto load chunk when spawning entities --- src/main/java/net/minestom/server/instance/Instance.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index f941137ea..0e5982217 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -880,9 +880,11 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta } }); - final Chunk chunk = getChunkAt(entityPosition); - Check.notNull(chunk, "You tried to spawn an entity in an unloaded chunk, " + entityPosition); - addEntityToChunk(entity, chunk); + // Load the chunk if not already (or throw an error if auto chunk load is disabled) + loadOptionalChunk(entityPosition, chunk -> { + Check.notNull(chunk, "You tried to spawn an entity in an unloaded chunk, " + entityPosition); + addEntityToChunk(entity, chunk); + }); }); } From 510f8d7733e99eba191ff7e47bb185495f872e5a Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 15:44:37 +0100 Subject: [PATCH 015/364] Give a name to the tick scheduler thread --- src/main/java/net/minestom/server/UpdateManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/UpdateManager.java b/src/main/java/net/minestom/server/UpdateManager.java index db7ae1947..d16841997 100644 --- a/src/main/java/net/minestom/server/UpdateManager.java +++ b/src/main/java/net/minestom/server/UpdateManager.java @@ -24,7 +24,8 @@ import java.util.function.LongConsumer; */ public final class UpdateManager { - private final ScheduledExecutorService updateExecutionService = Executors.newSingleThreadScheduledExecutor(); + private final ScheduledExecutorService updateExecutionService = Executors.newSingleThreadScheduledExecutor(r -> + new Thread(r, "tick-scheduler")); private volatile boolean stopRequested; From b6d8dd9f73683d02dcfef6e3efddb9a835079e90 Mon Sep 17 00:00:00 2001 From: themode Date: Sun, 21 Mar 2021 19:47:22 +0100 Subject: [PATCH 016/364] Style cleanup + increase food saturation limit to 20 --- .../net/minestom/server/entity/Player.java | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index b181368a5..93d6f8e04 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -123,10 +123,14 @@ public class Player extends LivingEntity implements CommandSender { // CustomBlock break delay private CustomBlock targetCustomBlock; private BlockPosition targetBlockPosition; - private long targetBreakDelay; // The last break delay requested - private long targetBlockBreakCount; // Number of tick since the last stage change - private byte targetStage; // The current stage of the target block, only if multi player breaking is disabled - private final Set targetBreakers = Collections.singleton(this); // Only used if multi player breaking is disabled, contains only this player + // The last break delay requested + private long targetBreakDelay; + // Number of tick since the last stage change + private long targetBlockBreakCount; + // The current stage of the target block, only if multi player breaking is disabled + private byte targetStage; + // Only used if multi player breaking is disabled, contains only this player + private final Set targetBreakers = Collections.singleton(this); // Position synchronization with viewers private long lastPlayerSynchronizationTime; @@ -319,7 +323,7 @@ public class Player extends LivingEntity implements CommandSender { // Should increment the target block stage if (targetCustomBlock.enableMultiPlayerBreaking()) { // Let the custom block object manages the breaking - final boolean canContinue = this.targetCustomBlock.processStage(instance, targetBlockPosition, this, stageIncrease); + final boolean canContinue = targetCustomBlock.processStage(instance, targetBlockPosition, this, stageIncrease); if (canContinue) { final Set breakers = targetCustomBlock.getBreakers(instance, targetBlockPosition); refreshBreakDelay(breakers); @@ -632,7 +636,8 @@ public class Player extends LivingEntity implements CommandSender { !spawnPosition.inSameChunk(this.position); if (needWorldRefresh) { - final boolean firstSpawn = this.instance == null; // TODO: Handle player reconnections, must be false in that case too + // TODO: Handle player reconnections, must be false in that case too + final boolean firstSpawn = this.instance == null; // Send the new dimension if player isn't in any instance or if the dimension is different final DimensionType instanceDimensionType = instance.getDimensionType(); @@ -811,7 +816,8 @@ public class Player extends LivingEntity implements CommandSender { * @param volume the volume of the sound (1 is 100%) * @param pitch the pitch of the sound, between 0.5 and 2.0 */ - public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) { + public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, + int x, int y, int z, float volume, float pitch) { SoundEffectPacket soundEffectPacket = new SoundEffectPacket(); soundEffectPacket.soundId = sound.getId(); soundEffectPacket.soundCategory = soundCategory; @@ -828,7 +834,8 @@ public class Player extends LivingEntity implements CommandSender { * * @see #playSound(Sound, SoundCategory, int, int, int, float, float) */ - public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, BlockPosition position, float volume, float pitch) { + public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, + BlockPosition position, float volume, float pitch) { playSound(sound, soundCategory, position.getX(), position.getY(), position.getZ(), volume, pitch); } @@ -843,7 +850,8 @@ public class Player extends LivingEntity implements CommandSender { * @param volume the volume of the sound (1 is 100%) * @param pitch the pitch of the sound, between 0.5 and 2.0 */ - public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) { + public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, + int x, int y, int z, float volume, float pitch) { NamedSoundEffectPacket namedSoundEffectPacket = new NamedSoundEffectPacket(); namedSoundEffectPacket.soundName = identifier; namedSoundEffectPacket.soundCategory = soundCategory; @@ -1100,7 +1108,8 @@ public class Player extends LivingEntity implements CommandSender { * @throws IllegalArgumentException if {@code food} is not between 0 and 20 */ public void setFood(int food) { - Check.argCondition(!MathUtils.isBetween(food, 0, 20), "Food has to be between 0 and 20"); + Check.argCondition(!MathUtils.isBetween(food, 0, 20), + "Food has to be between 0 and 20"); this.food = food; sendUpdateHealthPacket(); } @@ -1113,10 +1122,11 @@ public class Player extends LivingEntity implements CommandSender { * Sets and refresh client food saturation. * * @param foodSaturation the food saturation - * @throws IllegalArgumentException if {@code foodSaturation} is not between 0 and 5 + * @throws IllegalArgumentException if {@code foodSaturation} is not between 0 and 20 */ public void setFoodSaturation(float foodSaturation) { - Check.argCondition(!MathUtils.isBetween(foodSaturation, 0, 5), "Food saturation has to be between 0 and 5"); + Check.argCondition(!MathUtils.isBetween(foodSaturation, 0, 20), + "Food saturation has to be between 0 and 20"); this.foodSaturation = foodSaturation; sendUpdateHealthPacket(); } @@ -1547,8 +1557,8 @@ public class Player extends LivingEntity implements CommandSender { } /** - * Refreshes the list of entities that the player should be able to see based on {@link MinecraftServer#getEntityViewDistance()} - * and {@link Entity#isAutoViewable()}. + * Refreshes the list of entities that the player should be able to see based + * on {@link MinecraftServer#getEntityViewDistance()} and {@link Entity#isAutoViewable()}. * * @param newChunk the new chunk of the player (can be the current one) */ @@ -1702,7 +1712,8 @@ public class Player extends LivingEntity implements CommandSender { * @param dimensionType the new player dimension */ protected void sendDimension(@NotNull DimensionType dimensionType) { - Check.argCondition(dimensionType.equals(getDimensionType()), "The dimension needs to be different than the current one!"); + Check.argCondition(dimensionType.equals(getDimensionType()), + "The dimension needs to be different than the current one!"); this.dimensionType = dimensionType; RespawnPacket respawnPacket = new RespawnPacket(); @@ -1772,8 +1783,10 @@ public class Player extends LivingEntity implements CommandSender { public void setTeam(Team team) { super.setTeam(team); - if (team != null) - PacketUtils.sendGroupedPacket(MinecraftServer.getConnectionManager().getOnlinePlayers(), team.createTeamsCreationPacket()); + if (team != null) { + var players = MinecraftServer.getConnectionManager().getOnlinePlayers(); + PacketUtils.sendGroupedPacket(players, team.createTeamsCreationPacket()); + } } /** From 77af63772fa13e55fcc198792d933a813c63ae0c Mon Sep 17 00:00:00 2001 From: KrystilizeNevaDies <57762380+KrystilizeNevaDies@users.noreply.github.com> Date: Mon, 22 Mar 2021 21:23:57 +1000 Subject: [PATCH 017/364] Add getTask in SchedulerManager --- .../server/timer/SchedulerManager.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/net/minestom/server/timer/SchedulerManager.java b/src/main/java/net/minestom/server/timer/SchedulerManager.java index 33e9ca2e8..4c126be8d 100644 --- a/src/main/java/net/minestom/server/timer/SchedulerManager.java +++ b/src/main/java/net/minestom/server/timer/SchedulerManager.java @@ -151,6 +151,16 @@ public final class SchedulerManager implements IExtensionObserver { public ObjectCollection getTasks() { return tasks.values(); } + + /** + * Returns the task associated with this task id + * + * @param id the id of the task + * @return task the task itself + */ + public Task getTask(int id) { + return tasks.get(id); + } /** * Gets a {@link Collection} with all the registered shutdown {@link Task}. @@ -161,6 +171,16 @@ public final class SchedulerManager implements IExtensionObserver { public ObjectCollection getShutdownTasks() { return shutdownTasks.values(); } + + /** + * Returns the shutdown task associated with this task id + * + * @param id the id of the task + * @return task the shutdown task itself + */ + public Task getShutdownTask(int id) { + return shutdownTasks.get(id); + } /** * Gets the execution service for all the registered {@link Task}. From 01e56636da05640aed775288770a8ba97c4974ed Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 22 Mar 2021 14:31:38 +0100 Subject: [PATCH 018/364] Reduce the number of buffer copy --- .../network/player/NettyPlayerConnection.java | 3 +- .../minestom/server/utils/PacketUtils.java | 145 +++++++----------- 2 files changed, 56 insertions(+), 92 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 4b5c84f7d..5347fe5e5 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -172,7 +172,8 @@ public class NettyPlayerConnection extends PlayerConnection { } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; synchronized (tickBuffer) { - PacketUtils.createFramedPacket(serverPacket, true, tickBuffer); + final ByteBuf framedPacket = PacketUtils.createFramedPacket(serverPacket, false); + tickBuffer.writeBytes(framedPacket); } return; } else if (message instanceof ByteBuf) { diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 8707efd65..aff8b65cc 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -7,7 +7,6 @@ import net.minestom.server.entity.Player; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.network.netty.packet.FramedPacket; import net.minestom.server.network.packet.server.ServerPacket; -import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryWriter; @@ -107,65 +106,22 @@ public final class PacketUtils { * @param packet the packet to write into {@code buf} */ public static void writePacket(@NotNull ByteBuf buf, @NotNull ServerPacket packet) { - final ByteBuf packetBuffer = getPacketBuffer(packet); - - writePacket(buf, packetBuffer, packet.getId()); + Utils.writeVarIntBuf(buf, packet.getId()); + writePacketPayload(buf, packet); } /** - * Writes a {@link ServerPacket} into a newly created {@link ByteBuf}. + * Writes a packet payload. * * @param packet the packet to write - * @return a {@link ByteBuf} containing {@code packet} */ - @NotNull - public static ByteBuf writePacket(@NotNull ServerPacket packet) { - final ByteBuf packetBuffer = getPacketBuffer(packet); - - // Add 5 for the packet id and for the packet size - final int size = packetBuffer.writerIndex() + 5 + 5; - ByteBuf buffer = BufUtils.getBuffer(true, size); - - writePacket(buffer, packetBuffer, packet.getId()); - - return buffer; - } - - /** - * Writes a packet buffer into {@code buf}. - * - * @param buf the buffer which will receive the packet id/data - * @param packetBuffer the buffer containing the raw packet data - * @param packetId the packet id - */ - private static void writePacket(@NotNull ByteBuf buf, @NotNull ByteBuf packetBuffer, int packetId) { - Utils.writeVarIntBuf(buf, packetId); - buf.writeBytes(packetBuffer); - packetBuffer.release(); - } - - /** - * Gets the buffer representing the raw packet data. - * - * @param packet the packet to write - * @return the {@link ByteBuf} containing the raw packet data - */ - @NotNull - private static ByteBuf getPacketBuffer(@NotNull ServerPacket packet) { - BinaryWriter writer; - if (packet.getId() == ServerPacketIdentifier.CHUNK_DATA || packet.getId() == ServerPacketIdentifier.UPDATE_LIGHT) { - writer = new BinaryWriter(BufUtils.getBuffer(true, 40_000)); - } else { - writer = new BinaryWriter(BufUtils.getBuffer(true)); - } - + private static void writePacketPayload(@NotNull ByteBuf buffer, @NotNull ServerPacket packet) { + BinaryWriter writer = new BinaryWriter(buffer); try { packet.write(writer); } catch (Exception e) { MinecraftServer.getExceptionManager().handleException(e); } - - return writer.getBuffer(); } /** @@ -205,31 +161,33 @@ public final class PacketUtils { public static void compressBuffer(@NotNull Deflater deflater, @Nullable byte[] buffer, @NotNull ByteBuf packetBuffer, @NotNull ByteBuf compressionTarget) { final int packetLength = packetBuffer.readableBytes(); - - if (packetLength < MinecraftServer.getCompressionThreshold()) { - Utils.writeVarIntBuf(compressionTarget, 0); - compressionTarget.writeBytes(packetBuffer); + final boolean compression = packetLength > MinecraftServer.getCompressionThreshold(); + Utils.writeVarIntBuf(compressionTarget, compression ? packetLength : 0); + if (compression) { + compress(deflater, buffer, packetBuffer, compressionTarget); } else { - - Utils.writeVarIntBuf(compressionTarget, packetLength); - - // Allocate buffer if not already - byte[] output = buffer != null ? buffer : new byte[8192]; - - deflater.setInput(packetBuffer.nioBuffer()); - deflater.finish(); - - while (!deflater.finished()) { - final int length = deflater.deflate(output); - compressionTarget.writeBytes(output, 0, length); - } - - deflater.reset(); + compressionTarget.writeBytes(packetBuffer); } } + private static void compress(@NotNull Deflater deflater, @Nullable byte[] buffer, + @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) { + // Allocate buffer if not already + byte[] output = buffer != null ? buffer : new byte[8192]; + + deflater.setInput(uncompressed.nioBuffer()); + deflater.finish(); + + while (!deflater.finished()) { + final int length = deflater.deflate(output); + compressed.writeBytes(output, 0, length); + } + + deflater.reset(); + } + /** - * Writes a "framed packet" (packet which can be send and understood by a Minecraft client) + * Creates a "framed packet" (packet which can be send and understood by a Minecraft client) * from a server packet, directly into an output buffer. *

* Can be used if you want to store a raw buffer and send it later without the additional writing cost. @@ -237,30 +195,35 @@ public final class PacketUtils { * * @param serverPacket the server packet to write */ - public static void createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer, ByteBuf output) { - ByteBuf packetBuf = writePacket(serverPacket); - - if (MinecraftServer.getCompressionThreshold() > 0) { - ByteBuf compressedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); - - final Deflater deflater = DEFLATER.get(); - compressBuffer(deflater, null, packetBuf, compressedBuf); - - packetBuf.release(); - - frameBuffer(compressedBuf, output); - compressedBuf.release(); - } else { - frameBuffer(packetBuf, output); - packetBuf.release(); - } - } - @NotNull public static ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer) { - ByteBuf framedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); - createFramedPacket(serverPacket, directBuffer, framedBuf); - return framedBuf; + ByteBuf packetBuf = Unpooled.buffer(); + writePacket(packetBuf, serverPacket); + + final int dataLength = packetBuf.readableBytes(); + + final int compressionThreshold = MinecraftServer.getCompressionThreshold(); + final boolean compression = compressionThreshold > 0 && dataLength >= compressionThreshold; + + if (compression) { + // Compression + ByteBuf compressedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); + Utils.writeVarIntBuf(compressedBuf, dataLength); + final Deflater deflater = DEFLATER.get(); + compress(deflater, null, packetBuf, compressedBuf); + packetBuf = compressedBuf; + } else { + // No compression + ByteBuf uncompressedLengthBuffer = Unpooled.buffer(); + Utils.writeVarIntBuf(uncompressedLengthBuffer, 0); + packetBuf = Unpooled.wrappedBuffer(uncompressedLengthBuffer, packetBuf); + } + + // Write the final length of the packet + ByteBuf packetLengthBuffer = Unpooled.buffer(); + Utils.writeVarIntBuf(packetLengthBuffer, packetBuf.readableBytes()); + + return Unpooled.wrappedBuffer(packetLengthBuffer, packetBuf); } } From 5185a3efa6dbb4635767273a81eeb8c2a9dc502c Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 22 Mar 2021 14:39:06 +0100 Subject: [PATCH 019/364] Improve DynamicChunk cloning --- .../net/minestom/server/instance/DynamicChunk.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java index da8d2f641..299077ab0 100644 --- a/src/main/java/net/minestom/server/instance/DynamicChunk.java +++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java @@ -47,15 +47,15 @@ public class DynamicChunk extends Chunk { // Used to get all blocks with data (no null) // Key is still chunk coordinates (see #getBlockIndex) - protected final Int2ObjectMap blocksData = new Int2ObjectOpenHashMap<>(); + protected final Int2ObjectOpenHashMap blocksData = new Int2ObjectOpenHashMap<>(); // Contains CustomBlocks' block index which are updatable - protected final IntSet updatableBlocks = new IntOpenHashSet(); + protected final IntOpenHashSet updatableBlocks = new IntOpenHashSet(); // (block index)/(last update in ms) protected final Int2LongMap updatableBlocksLastUpdate = new Int2LongOpenHashMap(); // Block entities - protected final IntSet blockEntities = new IntOpenHashSet(); + protected final IntOpenHashSet blockEntities = new IntOpenHashSet(); private long lastChangeTime; @@ -389,8 +389,8 @@ public class DynamicChunk extends Chunk { fullDataPacket.chunkZ = chunkZ; fullDataPacket.paletteStorage = blockPalette.clone(); fullDataPacket.customBlockPaletteStorage = customBlockPalette.clone(); - fullDataPacket.blockEntities = new IntOpenHashSet(blockEntities); - fullDataPacket.blocksData = new Int2ObjectOpenHashMap<>(blocksData); + fullDataPacket.blockEntities = blockEntities.clone(); + fullDataPacket.blocksData = blocksData.clone(); return fullDataPacket; } From 169dbedea2d7d9f973dd3ce776f1c5846620f763 Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 22 Mar 2021 14:54:52 +0100 Subject: [PATCH 020/364] Fix entity head position, improve performance for Player#addViewer0 --- src/main/java/net/minestom/server/entity/Entity.java | 9 +++++++++ .../java/net/minestom/server/entity/LivingEntity.java | 5 +++++ src/main/java/net/minestom/server/entity/Player.java | 7 +++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 2a175e6a3..80f6de3ce 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -348,10 +348,19 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission playerConnection.sendPacket(getVelocityPacket()); playerConnection.sendPacket(getMetadataPacket()); + // Passenger if (hasPassenger()) { playerConnection.sendPacket(getPassengersPacket()); } + // Head position + { + EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket(); + entityHeadLookPacket.entityId = getEntityId(); + entityHeadLookPacket.yaw = position.getYaw(); + playerConnection.sendPacket(entityHeadLookPacket); + } + return true; } diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index 4f400d019..c10c4bd26 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -552,6 +552,11 @@ public class LivingEntity extends Entity implements EquipmentHandler { final PlayerConnection playerConnection = player.getPlayerConnection(); playerConnection.sendPacket(getEquipmentsPacket()); playerConnection.sendPacket(getPropertiesPacket()); + + if (getTeam() != null){ + playerConnection.sendPacket(getTeam().createTeamsCreationPacket()); + } + return true; } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 93d6f8e04..050d5eeb7 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -593,13 +593,12 @@ public class Player extends LivingEntity implements CommandSender { @Override public boolean addViewer0(@NotNull Player player) { - if (player == this || !super.addViewer0(player)) { + if (player == this) { return false; } - PlayerConnection viewerConnection = player.getPlayerConnection(); - showPlayer(viewerConnection); - return true; + viewerConnection.sendPacket(getAddPlayerToList()); + return super.addViewer0(player); } @Override From 7cd2d1b896926f7d7d477c6fd630ef683fd0a2a3 Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 22 Mar 2021 15:20:24 +0100 Subject: [PATCH 021/364] Do not fill attributes by default --- .../net/minestom/server/entity/LivingEntity.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index c10c4bd26..0c6aa37d7 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -88,7 +88,6 @@ public class LivingEntity extends Entity implements EquipmentHandler { */ public LivingEntity(@NotNull EntityType entityType, @NotNull UUID uuid) { this(entityType, uuid, new Position()); - setupAttributes(); setGravity(0.02f, 0.08f, 3.92f); initEquipments(); } @@ -103,7 +102,6 @@ public class LivingEntity extends Entity implements EquipmentHandler { @Deprecated public LivingEntity(@NotNull EntityType entityType, @NotNull UUID uuid, @NotNull Position spawnPosition) { super(entityType, uuid, spawnPosition); - setupAttributes(); setGravity(0.02f, 0.08f, 3.92f); initEquipments(); } @@ -641,16 +639,6 @@ public class LivingEntity extends Entity implements EquipmentHandler { return propertiesPacket; } - /** - * Sets all the attributes to {@link Attribute#getDefaultValue()} - */ - private void setupAttributes() { - for (Attribute attribute : Attribute.values()) { - final AttributeInstance attributeInstance = new AttributeInstance(attribute, this::onAttributeChanged); - this.attributeModifiers.put(attribute.getKey(), attributeInstance); - } - } - @Override protected void handleVoid() { // Kill if in void From 4c3215bf1b09a832be65e8cd5e336fe4d7af4d8a Mon Sep 17 00:00:00 2001 From: themode Date: Mon, 22 Mar 2021 19:37:16 +0100 Subject: [PATCH 022/364] Fix packet writing when compression is disabled --- .../minestom/server/utils/PacketUtils.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index aff8b65cc..3fa5eeb07 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -203,20 +203,22 @@ public final class PacketUtils { final int dataLength = packetBuf.readableBytes(); final int compressionThreshold = MinecraftServer.getCompressionThreshold(); - final boolean compression = compressionThreshold > 0 && dataLength >= compressionThreshold; + final boolean compression = compressionThreshold > 0; if (compression) { - // Compression - ByteBuf compressedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); - Utils.writeVarIntBuf(compressedBuf, dataLength); - final Deflater deflater = DEFLATER.get(); - compress(deflater, null, packetBuf, compressedBuf); - packetBuf = compressedBuf; - } else { - // No compression - ByteBuf uncompressedLengthBuffer = Unpooled.buffer(); - Utils.writeVarIntBuf(uncompressedLengthBuffer, 0); - packetBuf = Unpooled.wrappedBuffer(uncompressedLengthBuffer, packetBuf); + if (dataLength >= compressionThreshold) { + // Packet large enough + ByteBuf compressedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); + Utils.writeVarIntBuf(compressedBuf, dataLength); + final Deflater deflater = DEFLATER.get(); + compress(deflater, null, packetBuf, compressedBuf); + packetBuf = compressedBuf; + } else { + // Packet too small + ByteBuf uncompressedLengthBuffer = Unpooled.buffer(); + Utils.writeVarIntBuf(uncompressedLengthBuffer, 0); + packetBuf = Unpooled.wrappedBuffer(uncompressedLengthBuffer, packetBuf); + } } // Write the final length of the packet From a982c4f11e95b74df5353c6b72aa4fa4f6b0819f Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Tue, 23 Mar 2021 08:56:25 -0400 Subject: [PATCH 023/364] Add removePlayerInitialization func --- .../minestom/server/network/ConnectionManager.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 284b39106..f4272b4e2 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -302,6 +302,17 @@ public final class ConnectionManager { this.playerInitializations.add(playerInitialization); } + /** + * Removes an existing player initialization consumer. + *

+ * Removal of player initializations should be done by reference, and not cloning. + * + * @param playerInitialization the {@link Player} initialization consumer + */ + public void removePlayerInitialization(@NotNull Consumer playerInitialization) { + this.playerInitializations.remove(playerInitialization); + } + /** * Gets the kick reason when the server is shutdown using {@link MinecraftServer#stopCleanly()}. * From 701b1cb2e5f4ba3c2708413fceb01d13a6c7fcbc Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Tue, 23 Mar 2021 11:35:52 -0400 Subject: [PATCH 024/364] Move to DiscoveredExtension vs ExtensionDescription For those who are wondering why I replaced some streams: https://stackoverflow.com/questions/16635398/java-8-iterable-foreach-vs-foreach-loop --- .../extensions/DiscoveredExtension.java | 6 +- .../minestom/server/extensions/Extension.java | 85 ++++++------------- .../ExtensionDependencyResolver.java | 10 ++- .../server/extensions/ExtensionManager.java | 84 ++++++++++-------- .../demo/commands/ReloadExtensionCommand.java | 2 +- .../MissingCodeModifiersExtension.java | 6 +- .../UnloadCallbacksExtension.java | 2 +- 7 files changed, 86 insertions(+), 109 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java index 6e6715f56..28945fbfd 100644 --- a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java +++ b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java @@ -10,7 +10,7 @@ import java.net.URL; import java.util.LinkedList; import java.util.List; -final class DiscoveredExtension { +public final class DiscoveredExtension { public final static Logger LOGGER = LoggerFactory.getLogger(DiscoveredExtension.class); @@ -23,7 +23,7 @@ final class DiscoveredExtension { private String[] codeModifiers; private String[] dependencies; private ExternalDependencies externalDependencies; - private List missingCodeModifiers = new LinkedList<>(); + private final List missingCodeModifiers = new LinkedList<>(); private boolean failedToLoadMixin = false; transient List files = new LinkedList<>(); transient LoadStatus loadStatus = LoadStatus.LOAD_SUCCESS; @@ -77,7 +77,7 @@ final class DiscoveredExtension { } @Nullable - File getOriginalJar() { + public File getOriginalJar() { return originalJar; } diff --git a/src/main/java/net/minestom/server/extensions/Extension.java b/src/main/java/net/minestom/server/extensions/Extension.java index fdd626b77..1b6316043 100644 --- a/src/main/java/net/minestom/server/extensions/Extension.java +++ b/src/main/java/net/minestom/server/extensions/Extension.java @@ -13,7 +13,7 @@ import java.util.function.Consumer; public abstract class Extension { // Set by reflection @SuppressWarnings("unused") - private ExtensionDescription description; + private DiscoveredExtension origin; // Set by reflection @SuppressWarnings("unused") private Logger logger; @@ -24,8 +24,13 @@ public abstract class Extension { * this extension holds a reference to it. A WeakReference makes sure this extension does not prevent the memory * from being cleaned up. */ - private Set> observers = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private ReferenceQueue observerReferenceQueue = new ReferenceQueue<>(); + protected final Set> observers = Collections.newSetFromMap(new ConcurrentHashMap<>()); + protected final ReferenceQueue observerReferenceQueue = new ReferenceQueue<>(); + + /** + * List of extensions that depend on this extension. + */ + protected final Set dependents = new HashSet<>(); protected Extension() { @@ -59,10 +64,14 @@ public abstract class Extension { } @NotNull - public ExtensionDescription getDescription() { - return description; + public DiscoveredExtension getOrigin() { + return origin; } + /** + * Gets the logger for the extension + * @return The logger for the extension + */ @NotNull protected Logger getLogger() { return logger; @@ -71,7 +80,8 @@ public abstract class Extension { /** * Adds a new observer to this extension. * Will be kept as a WeakReference. - * @param observer + * + * @param observer The observer to add */ public void observe(IExtensionObserver observer) { observers.add(new WeakReference<>(observer, observerReferenceQueue)); @@ -82,9 +92,9 @@ public abstract class Extension { * @param action code to execute on each observer */ public void triggerChange(Consumer action) { - for(WeakReference weakObserver : observers) { + for (WeakReference weakObserver : observers) { IExtensionObserver observer = weakObserver.get(); - if(observer != null) { + if (observer != null) { action.accept(observer); } } @@ -94,7 +104,7 @@ public abstract class Extension { * If this extension registers code modifiers and/or mixins, are they loaded correctly? */ public boolean areCodeModifiersAllLoadedCorrectly() { - return !getDescription().failedToLoadMixin && getDescription().getMissingCodeModifiers().isEmpty(); + return !getOrigin().hasFailedToLoadMixin() && getOrigin().getMissingCodeModifiers().isEmpty(); } /** @@ -109,57 +119,10 @@ public abstract class Extension { } } - public static class ExtensionDescription { - private final String name; - private final String version; - private final List authors; - private final List dependents = new ArrayList<>(); - private final List missingCodeModifiers = new LinkedList<>(); - private final boolean failedToLoadMixin; - private final DiscoveredExtension origin; - - ExtensionDescription(@NotNull String name, @NotNull String version, @NotNull List authors, @NotNull DiscoveredExtension origin) { - this.name = name; - this.version = version; - this.authors = authors; - this.origin = origin; - failedToLoadMixin = origin.hasFailedToLoadMixin(); - missingCodeModifiers.addAll(origin.getMissingCodeModifiers()); - } - - @NotNull - public String getName() { - return name; - } - - @NotNull - public String getVersion() { - return version; - } - - @NotNull - public List getAuthors() { - return authors; - } - - @NotNull - public List getDependents() { - return dependents; - } - - @NotNull - DiscoveredExtension getOrigin() { - return origin; - } - - @NotNull - public List getMissingCodeModifiers() { - return missingCodeModifiers; - } - - @NotNull - public boolean hasFailedToLoadMixin() { - return failedToLoadMixin; - } + /** + * @return A modifiable list of dependents. + */ + public Set getDependents() { + return dependents; } } diff --git a/src/main/java/net/minestom/server/extensions/ExtensionDependencyResolver.java b/src/main/java/net/minestom/server/extensions/ExtensionDependencyResolver.java index 71d1ce4bb..300cdc4c1 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionDependencyResolver.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionDependencyResolver.java @@ -37,11 +37,13 @@ public class ExtensionDependencyResolver implements DependencyResolver { // B depends on A (A<-B) // When loading B, with no deep conversion, Ext will not be added to the list of dependencies (because it is not a direct dependency) // But when trying to call/access code from extension A, the parts dependent on Ext won't be inside B's dependencies, triggering a ClassNotFoundException - List deps = new LinkedList<>(); - for (URL u : ext.files) { - deps.add(new ResolvedDependency(u.toExternalForm(), u.toExternalForm(), "", u, new LinkedList<>())); + List dependencies = new LinkedList<>(); + + for (URL url : ext.files) { + dependencies.add(new ResolvedDependency(url.toExternalForm(), url.toExternalForm(), "", url, new LinkedList<>())); } - return new ResolvedDependency(ext.getName(), ext.getName(), ext.getVersion(), ext.files.get(0), deps); + + return new ResolvedDependency(ext.getName(), ext.getName(), ext.getVersion(), ext.files.get(0), dependencies); } throw new UnresolvedDependencyException("No extension named " + extensionName); } diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index b4f672283..260fabaaf 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -24,7 +24,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.net.URL; -import java.net.URLClassLoader; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; @@ -148,12 +147,6 @@ public class ExtensionManager { // Create ExtensionDescription (authors, version etc.) final String extensionName = discoveredExtension.getName(); String mainClass = discoveredExtension.getEntrypoint(); - Extension.ExtensionDescription extensionDescription = new Extension.ExtensionDescription( - extensionName, - discoveredExtension.getVersion(), - Arrays.asList(discoveredExtension.getAuthors()), - discoveredExtension - ); MinestomExtensionClassLoader loader = extensionLoaders.get(extensionName.toLowerCase()); @@ -207,9 +200,9 @@ public class ExtensionManager { // Set extension description try { - Field descriptionField = Extension.class.getDeclaredField("description"); - descriptionField.setAccessible(true); - descriptionField.set(extension, extensionDescription); + Field originField = Extension.class.getDeclaredField("origin"); + originField.setAccessible(true); + originField.set(extension, discoveredExtension); } catch (IllegalAccessException e) { // We made it accessible, should not occur } catch (NoSuchFieldException e) { @@ -236,7 +229,7 @@ public class ExtensionManager { if (dep == null) { LOGGER.warn("Dependency {} of {} is null? This means the extension has been loaded without its dependency, which could cause issues later.", dependency, discoveredExtension.getName()); } else { - dep.getDescription().getDependents().add(discoveredExtension.getName()); + dep.getDependents().add(discoveredExtension.getName()); } } @@ -306,14 +299,16 @@ public class ExtensionManager { } } - @Nullable + @NotNull private List generateLoadOrder(@NotNull List discoveredExtensions) { // Do some mapping so we can map strings to extensions. Map extensionMap = new HashMap<>(); Map> dependencyMap = new HashMap<>(); + for (DiscoveredExtension discoveredExtension : discoveredExtensions) { extensionMap.put(discoveredExtension.getName().toLowerCase(), discoveredExtension); } + for (DiscoveredExtension discoveredExtension : discoveredExtensions) { List dependencies = Arrays.stream(discoveredExtension.getDependencies()) @@ -323,7 +318,7 @@ public class ExtensionManager { if (dependencyExtension == null) { // attempt to see if it is not already loaded (happens with dynamic (re)loading) if (extensions.containsKey(dependencyName.toLowerCase())) { - return extensions.get(dependencyName.toLowerCase()).getDescription().getOrigin(); + return extensions.get(dependencyName.toLowerCase()).getOrigin(); } else { LOGGER.error("Extension {} requires an extension called {}.", discoveredExtension.getName(), dependencyName); LOGGER.error("However the extension {} could not be found.", dependencyName); @@ -356,12 +351,17 @@ public class ExtensionManager { ) { // Get all "loadable" (not actually being loaded!) extensions and put them in the sorted list. for (Map.Entry> entry : loadableExtensions) { + // Add to sorted list. sortedList.add(entry.getKey()); + // Remove to make the next iterations a little bit quicker (hopefully) and to find cyclic dependencies. dependencyMap.remove(entry.getKey()); + // Remove this dependency from all the lists (if they include it) to make way for next level of extensions. - dependencyMap.forEach((key, dependencyList) -> dependencyList.remove(entry.getKey())); + for (var dependencies : dependencyMap.values()) { + dependencies.remove(entry.getKey()); + } } } @@ -387,46 +387,56 @@ public class ExtensionManager { private void loadDependencies(List extensions) { List allLoadedExtensions = new LinkedList<>(extensions); - extensionList.stream().map(ext -> ext.getDescription().getOrigin()).forEach(allLoadedExtensions::add); + + for (Extension extension : extensionList) + allLoadedExtensions.add(extension.getOrigin()); + ExtensionDependencyResolver extensionDependencyResolver = new ExtensionDependencyResolver(allLoadedExtensions); - for (DiscoveredExtension ext : extensions) { + + for (DiscoveredExtension discoveredExtension : extensions) { try { DependencyGetter getter = new DependencyGetter(); - DiscoveredExtension.ExternalDependencies externalDependencies = ext.getExternalDependencies(); + DiscoveredExtension.ExternalDependencies externalDependencies = discoveredExtension.getExternalDependencies(); List repoList = new LinkedList<>(); for (var repository : externalDependencies.repositories) { + if (repository.name == null) { throw new IllegalStateException("Missing 'name' element in repository object."); } + if (repository.name.isEmpty()) { throw new IllegalStateException("Invalid 'name' element in repository object."); } + if (repository.url == null) { throw new IllegalStateException("Missing 'url' element in repository object."); } + if (repository.url.isEmpty()) { throw new IllegalStateException("Invalid 'url' element in repository object."); } + repoList.add(new MavenRepository(repository.name, repository.url)); } + getter.addMavenResolver(repoList); getter.addResolver(extensionDependencyResolver); - for (var artifact : externalDependencies.artifacts) { + for (String artifact : externalDependencies.artifacts) { var resolved = getter.get(artifact, dependenciesFolder); - addDependencyFile(resolved, ext); - LOGGER.trace("Dependency of extension {}: {}", ext.getName(), resolved); + addDependencyFile(resolved, discoveredExtension); + LOGGER.trace("Dependency of extension {}: {}", discoveredExtension.getName(), resolved); } - for (var dependencyName : ext.getDependencies()) { + for (String dependencyName : discoveredExtension.getDependencies()) { var resolved = getter.get(dependencyName, dependenciesFolder); - addDependencyFile(resolved, ext); - LOGGER.trace("Dependency of extension {}: {}", ext.getName(), resolved); + addDependencyFile(resolved, discoveredExtension); + LOGGER.trace("Dependency of extension {}: {}", discoveredExtension.getName(), resolved); } } catch (Exception e) { - ext.loadStatus = DiscoveredExtension.LoadStatus.MISSING_DEPENDENCIES; - LOGGER.error("Failed to load dependencies for extension {}", ext.getName()); - LOGGER.error("Extension '{}' will not be loaded", ext.getName()); + discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.MISSING_DEPENDENCIES; + LOGGER.error("Failed to load dependencies for extension {}", discoveredExtension.getName()); + LOGGER.error("Extension '{}' will not be loaded", discoveredExtension.getName()); LOGGER.error("This is the exception", e); } } @@ -560,7 +570,7 @@ public class ExtensionManager { ext.preTerminate(); ext.terminate(); // remove callbacks for this extension - String extensionName = ext.getDescription().getName(); + String extensionName = ext.getOrigin().getName(); ext.triggerChange(observer -> observer.onExtensionUnload(extensionName)); // TODO: more callback types @@ -570,10 +580,10 @@ public class ExtensionManager { // remove as dependent of other extensions // this avoids issues where a dependent extension fails to reload, and prevents the base extension to reload too for (Extension e : extensionList) { - e.getDescription().getDependents().remove(ext.getDescription().getName()); + e.getDependents().remove(ext.getOrigin().getName()); } - String id = ext.getDescription().getName().toLowerCase(); + String id = ext.getOrigin().getName().toLowerCase(); // remove from loaded extensions extensions.remove(id); extensionList.remove(ext); @@ -595,19 +605,19 @@ public class ExtensionManager { throw new IllegalArgumentException("Extension " + extensionName + " is not currently loaded."); } - File originalJar = ext.getDescription().getOrigin().getOriginalJar(); + File originalJar = ext.getOrigin().getOriginalJar(); if (originalJar == null) { LOGGER.error("Cannot reload extension {} that is not from a .jar file!", extensionName); return; } LOGGER.info("Reload extension {} from jar file {}", extensionName, originalJar.getAbsolutePath()); - List dependents = new LinkedList<>(ext.getDescription().getDependents()); // copy dependents list + List dependents = new LinkedList<>(ext.getDependents()); // copy dependents list List originalJarsOfDependents = new LinkedList<>(); for (String dependentID : dependents) { Extension dependentExt = extensions.get(dependentID.toLowerCase()); - File dependentOriginalJar = dependentExt.getDescription().getOrigin().getOriginalJar(); + File dependentOriginalJar = dependentExt.getOrigin().getOriginalJar(); originalJarsOfDependents.add(dependentOriginalJar); if (dependentOriginalJar == null) { LOGGER.error("Cannot reload extension {} that is not from a .jar file!", dependentID); @@ -696,7 +706,7 @@ public class ExtensionManager { if (ext == null) { throw new IllegalArgumentException("Extension " + extensionName + " is not currently loaded."); } - List dependents = new LinkedList<>(ext.getDescription().getDependents()); // copy dependents list + List dependents = new LinkedList<>(ext.getDependents()); // copy dependents list for (String dependentID : dependents) { Extension dependentExt = extensions.get(dependentID.toLowerCase()); @@ -715,7 +725,9 @@ public class ExtensionManager { * Shutdowns all the extensions by unloading them. */ public void shutdown() { - this.extensionList.forEach(this::unload); + for (Extension extension : getExtensions()) { + extension.unload(); + } } /** @@ -733,7 +745,7 @@ public class ExtensionManager { List discovered = manager.discoverExtensions(); // setup extension class loaders, so that Mixin can load the json configuration file correctly - for(DiscoveredExtension e : discovered) { + for (DiscoveredExtension e : discovered) { manager.setupClassLoader(e); } @@ -741,7 +753,7 @@ public class ExtensionManager { manager.setupCodeModifiers(discovered, MinestomRootClassLoader.getInstance()); // setup is done, remove all extension classloaders - for(MinestomExtensionClassLoader extensionLoader : manager.getExtensionLoaders().values()) { + for (MinestomExtensionClassLoader extensionLoader : manager.getExtensionLoaders().values()) { MinestomRootClassLoader.getInstance().removeChildInHierarchy(extensionLoader); } LOGGER.info("Early load of code modifiers from extensions done!"); diff --git a/src/test/java/demo/commands/ReloadExtensionCommand.java b/src/test/java/demo/commands/ReloadExtensionCommand.java index a62981004..198a2cf55 100644 --- a/src/test/java/demo/commands/ReloadExtensionCommand.java +++ b/src/test/java/demo/commands/ReloadExtensionCommand.java @@ -25,7 +25,7 @@ public class ReloadExtensionCommand extends Command { static { ReloadExtensionCommand.extensionsName = MinecraftServer.getExtensionManager().getExtensions() .stream() - .map(extension -> extension.getDescription().getName()) + .map(extension -> extension.getOrigin().getName()) .toArray(String[]::new); } diff --git a/src/test/java/improveextensions/missingmodifiers/MissingCodeModifiersExtension.java b/src/test/java/improveextensions/missingmodifiers/MissingCodeModifiersExtension.java index 3101ab76a..f8d8679b0 100644 --- a/src/test/java/improveextensions/missingmodifiers/MissingCodeModifiersExtension.java +++ b/src/test/java/improveextensions/missingmodifiers/MissingCodeModifiersExtension.java @@ -12,9 +12,9 @@ public class MissingCodeModifiersExtension extends Extension { // force load of InstanceContainer class try { Assertions.assertFalse(areCodeModifiersAllLoadedCorrectly(), "Mixin configuration could not be loaded and code modifiers are unavailable, the failure should be reported"); - Assertions.assertTrue(getDescription().hasFailedToLoadMixin(), "Mixin configuration does not exist and should not be loaded"); - Assertions.assertEquals(1, getDescription().getMissingCodeModifiers().size(), "Code modifier does not exist, it should be reported as missing"); - Assertions.assertEquals("InvalidCodeModifierClass", getDescription().getMissingCodeModifiers().get(0)); + Assertions.assertTrue(getOrigin().hasFailedToLoadMixin(), "Mixin configuration does not exist and should not be loaded"); + Assertions.assertEquals(1, getOrigin().getMissingCodeModifiers().size(), "Code modifier does not exist, it should be reported as missing"); + Assertions.assertEquals("InvalidCodeModifierClass", getOrigin().getMissingCodeModifiers().get(0)); System.out.println("All tests passed."); } catch (AssertionFailedError e) { e.printStackTrace(); diff --git a/src/test/java/improveextensions/unloadcallbacks/UnloadCallbacksExtension.java b/src/test/java/improveextensions/unloadcallbacks/UnloadCallbacksExtension.java index 5289438a6..a2a74fcb3 100644 --- a/src/test/java/improveextensions/unloadcallbacks/UnloadCallbacksExtension.java +++ b/src/test/java/improveextensions/unloadcallbacks/UnloadCallbacksExtension.java @@ -72,7 +72,7 @@ public class UnloadCallbacksExtension extends Extension { MinecraftServer.getSchedulerManager().buildTask(() -> { // unload self - MinecraftServer.getExtensionManager().unloadExtension(getDescription().getName()); + MinecraftServer.getExtensionManager().unloadExtension(getOrigin().getName()); }).delay(1L, TimeUnit.SECOND).schedule(); } From 5adbbd8c457499ec0d0014a4272530e8210c3dfd Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Tue, 23 Mar 2021 11:42:32 -0400 Subject: [PATCH 025/364] Style cleanup Note: Stream is nice to have in some places, just *please* don't abuse it --- .../net/minestom/server/extensions/ExtensionManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 260fabaaf..5a7eca484 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -127,7 +127,7 @@ public class ExtensionManager { // periodically cleanup observers MinecraftServer.getSchedulerManager().buildTask(() -> { - for(Extension ext : extensionList) { + for (Extension ext : extensionList) { ext.cleanupObservers(); } }).repeat(1L, TimeUnit.MINUTE).schedule(); @@ -448,9 +448,9 @@ public class ExtensionManager { LOGGER.trace("Added dependency {} to extension {} classpath", location.toExternalForm(), extension.getName()); // recurse to add full dependency tree - if(!dependency.getSubdependencies().isEmpty()) { + if (!dependency.getSubdependencies().isEmpty()) { LOGGER.trace("Dependency {} has subdependencies, adding...", location.toExternalForm()); - for(ResolvedDependency sub : dependency.getSubdependencies()) { + for (ResolvedDependency sub : dependency.getSubdependencies()) { addDependencyFile(sub, extension); } LOGGER.trace("Dependency {} has had its subdependencies added.", location.toExternalForm()); From 79a8f4bd933ecfd4fbfef2817aebaa18bd13c78d Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Tue, 23 Mar 2021 11:50:42 -0400 Subject: [PATCH 026/364] General style alignment --- .../server/extensions/DiscoveredExtension.java | 12 ++++++++++-- .../minestom/server/extensions/ExtensionManager.java | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java index 28945fbfd..b77b688ed 100644 --- a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java +++ b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java @@ -95,6 +95,7 @@ public final class DiscoveredExtension { extension.name = extension.loadStatus.name(); return; } + if (!extension.name.matches(NAME_REGEX)) { LOGGER.error("Extension '{}' specified an invalid name.", extension.name); LOGGER.error("Extension '{}' will not be loaded.", extension.name); @@ -104,6 +105,7 @@ public final class DiscoveredExtension { extension.name = extension.loadStatus.name(); return; } + if (extension.entrypoint == null) { LOGGER.error("Extension '{}' did not specify an entry point (via 'entrypoint').", extension.name); LOGGER.error("Extension '{}' will not be loaded.", extension.name); @@ -113,6 +115,7 @@ public final class DiscoveredExtension { extension.entrypoint = extension.loadStatus.name(); return; } + // Handle defaults // If we reach this code, then the extension will most likely be loaded: if (extension.version == null) { @@ -120,19 +123,24 @@ public final class DiscoveredExtension { LOGGER.warn("Extension '{}' will continue to load but should specify a plugin version.", extension.name); extension.version = "Unspecified"; } + if (extension.mixinConfig == null) { extension.mixinConfig = ""; } + if (extension.authors == null) { extension.authors = new String[0]; } + if (extension.codeModifiers == null) { extension.codeModifiers = new String[0]; } + // No dependencies were specified if (extension.dependencies == null) { extension.dependencies = new String[0]; } + // No external dependencies were specified; if (extension.externalDependencies == null) { extension.externalDependencies = new ExternalDependencies(); @@ -177,11 +185,11 @@ public final class DiscoveredExtension { } } - static final class ExternalDependencies { + public static final class ExternalDependencies { Repository[] repositories = new Repository[0]; String[] artifacts = new String[0]; - static class Repository { + public static class Repository { String name = ""; String url = ""; } diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 5a7eca484..ddf36b68a 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -735,7 +735,7 @@ public class ExtensionManager { */ public static void loadCodeModifiersEarly() { // allow users to disable early code modifier load - if("true".equalsIgnoreCase(System.getProperty(DISABLE_EARLY_LOAD_SYSTEM_KEY))) { + if ("true".equalsIgnoreCase(System.getProperty(DISABLE_EARLY_LOAD_SYSTEM_KEY))) { return; } LOGGER.info("Early load of code modifiers from extensions."); @@ -765,8 +765,8 @@ public class ExtensionManager { public void unloadAllExtensions() { // copy names, as the extensions map will be modified via the calls to unload Set extensionNames = new HashSet<>(extensions.keySet()); - for(String ext : extensionNames) { - if(extensions.containsKey(ext)) { // is still loaded? Because extensions can depend on one another, it might have already been unloaded + for (String ext : extensionNames) { + if (extensions.containsKey(ext)) { // is still loaded? Because extensions can depend on one another, it might have already been unloaded unloadExtension(ext); } } From 135ecf7b9c300168d6d356a66fb1befc78af8107 Mon Sep 17 00:00:00 2001 From: themode Date: Tue, 23 Mar 2021 17:01:12 +0100 Subject: [PATCH 027/364] Fix block placement on item entity --- .../net/minestom/server/listener/BlockPlacementListener.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java index b566846b5..bd51a6775 100644 --- a/src/main/java/net/minestom/server/listener/BlockPlacementListener.java +++ b/src/main/java/net/minestom/server/listener/BlockPlacementListener.java @@ -3,6 +3,7 @@ package net.minestom.server.listener; import net.minestom.server.MinecraftServer; import net.minestom.server.data.Data; import net.minestom.server.entity.Entity; +import net.minestom.server.entity.EntityType; import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerBlockInteractEvent; @@ -122,9 +123,11 @@ public class BlockPlacementListener { // Check if the player is trying to place a block in an entity boolean intersect = player.getBoundingBox().intersect(blockPosition); if (!intersect && block.isSolid()) { + // TODO push entities too close to the position for (Entity entity : entities) { // 'player' has already been checked - if (entity == player) + if (entity == player || + entity.getEntityType() == EntityType.ITEM) continue; intersect = entity.getBoundingBox().intersect(blockPosition); From 3b5d066f44d61b3f587b031982ed529731a11d83 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Tue, 23 Mar 2021 19:43:48 -0400 Subject: [PATCH 028/364] Style and stream cleanup --- .../selfmodification/HierarchyClassLoader.java | 15 ++++++++++----- .../MinestomExtensionClassLoader.java | 10 ++++++++-- .../selfmodification/MinestomRootClassLoader.java | 14 ++++---------- .../mixins/MixinServiceMinestom.java | 3 ++- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/minestom/server/extras/selfmodification/HierarchyClassLoader.java b/src/main/java/net/minestom/server/extras/selfmodification/HierarchyClassLoader.java index 1c5ad5c34..be464fb26 100644 --- a/src/main/java/net/minestom/server/extras/selfmodification/HierarchyClassLoader.java +++ b/src/main/java/net/minestom/server/extras/selfmodification/HierarchyClassLoader.java @@ -22,20 +22,25 @@ public abstract class HierarchyClassLoader extends URLClassLoader { children.add(loader); } - public InputStream getResourceAsStreamWithChildren(String name) { + public InputStream getResourceAsStreamWithChildren(@NotNull String name) { InputStream in = getResourceAsStream(name); - if(in != null) return in; + if (in != null) return in; - for(MinestomExtensionClassLoader child : children) { + for (MinestomExtensionClassLoader child : children) { InputStream childInput = child.getResourceAsStreamWithChildren(name); - if(childInput != null) + if (childInput != null) return childInput; } + return null; } public void removeChildInHierarchy(MinestomExtensionClassLoader child) { children.remove(child); - children.forEach(c -> c.removeChildInHierarchy(child)); + + // Also remove all children from these extension's children. + for (MinestomExtensionClassLoader subChild : children) { + subChild.removeChildInHierarchy(child); + } } } diff --git a/src/main/java/net/minestom/server/extras/selfmodification/MinestomExtensionClassLoader.java b/src/main/java/net/minestom/server/extras/selfmodification/MinestomExtensionClassLoader.java index 24557d18b..5dbeb84d7 100644 --- a/src/main/java/net/minestom/server/extras/selfmodification/MinestomExtensionClassLoader.java +++ b/src/main/java/net/minestom/server/extras/selfmodification/MinestomExtensionClassLoader.java @@ -105,8 +105,14 @@ public class MinestomExtensionClassLoader extends HierarchyClassLoader { * @see MinestomRootClassLoader#loadBytes(String, boolean) for more information */ protected boolean isMainExtensionClass(String name) { - if(mainClassName.equals(name)) + + if (mainClassName.equals(name)) return true; - return children.stream().anyMatch(c -> c.isMainExtensionClass(name)); + + for (MinestomExtensionClassLoader child : children) { + if (child.isMainExtensionClass(name)) return true; + } + + return false; } } diff --git a/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java b/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java index b1572aae3..0c0517e07 100644 --- a/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java +++ b/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java @@ -10,7 +10,6 @@ import org.objectweb.asm.tree.ClassNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; @@ -220,9 +219,8 @@ public class MinestomRootClassLoader extends HierarchyClassLoader { return originalBytes; } - public byte[] loadBytesWithChildren(String name, boolean transform) throws IOException, ClassNotFoundException { - if (name == null) - throw new ClassNotFoundException(); + public byte[] loadBytesWithChildren(@NotNull String name, boolean transform) throws IOException, ClassNotFoundException { + String path = name.replace(".", "/") + ".class"; InputStream input = getResourceAsStreamWithChildren(path); if (input == null) { @@ -298,11 +296,7 @@ public class MinestomRootClassLoader extends HierarchyClassLoader { } return true; } catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { - if(MinecraftServer.getExceptionManager() != null) { - MinecraftServer.getExceptionManager().handleException(e); - } else { - e.printStackTrace(); - } + MinecraftServer.getExceptionManager().handleException(e); } return false; } @@ -336,7 +330,7 @@ public class MinestomRootClassLoader extends HierarchyClassLoader { @Nullable public static String findExtensionObjectOwner(@NotNull Object obj) { ClassLoader cl = obj.getClass().getClassLoader(); - if(cl instanceof MinestomExtensionClassLoader) { + if (cl instanceof MinestomExtensionClassLoader) { return ((MinestomExtensionClassLoader) cl).getExtensionName(); } return null; diff --git a/src/main/java/net/minestom/server/extras/selfmodification/mixins/MixinServiceMinestom.java b/src/main/java/net/minestom/server/extras/selfmodification/mixins/MixinServiceMinestom.java index 5af794698..93b6e8ce2 100644 --- a/src/main/java/net/minestom/server/extras/selfmodification/mixins/MixinServiceMinestom.java +++ b/src/main/java/net/minestom/server/extras/selfmodification/mixins/MixinServiceMinestom.java @@ -1,6 +1,7 @@ package net.minestom.server.extras.selfmodification.mixins; import net.minestom.server.extras.selfmodification.MinestomRootClassLoader; +import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.launch.platform.container.ContainerHandleVirtual; import org.spongepowered.asm.launch.platform.container.IContainerHandle; import org.spongepowered.asm.mixin.MixinEnvironment; @@ -64,7 +65,7 @@ public class MixinServiceMinestom extends MixinServiceAbstract { } @Override - public InputStream getResourceAsStream(String name) { + public InputStream getResourceAsStream(@NotNull String name) { return classLoader.getResourceAsStreamWithChildren(name); } From 1de1fb062d6a4f7788c56e981b13d20177af7a22 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Tue, 23 Mar 2021 19:59:40 -0400 Subject: [PATCH 029/364] Better mapping! --- .../server/extensions/ExtensionManager.java | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index ddf36b68a..af2d6fc55 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -80,6 +80,7 @@ public class ExtensionManager { Check.stateCondition(loaded, "Extensions are already loaded!"); this.loaded = true; + // Make extensions folder if necessary if (!extensionFolder.exists()) { if (!extensionFolder.mkdirs()) { LOGGER.error("Could not find or create the extension folder, extensions will not be loaded!"); @@ -87,6 +88,7 @@ public class ExtensionManager { } } + // Make dependencies folder if necessary if (!dependenciesFolder.exists()) { if (!dependenciesFolder.mkdirs()) { LOGGER.error("Could not find nor create the extension dependencies folder, extensions will not be loaded!"); @@ -242,15 +244,23 @@ public class ExtensionManager { @NotNull private List discoverExtensions() { List extensions = new LinkedList<>(); + File[] fileList = extensionFolder.listFiles(); - if(fileList != null) { + + if (fileList != null) { + // Loop through all files in extension folder for (File file : fileList) { + + // Ignore folders if (file.isDirectory()) { continue; } + + // Ignore non .jar files if (!file.getName().endsWith(".jar")) { continue; } + DiscoveredExtension extension = discoverFromJar(file); if (extension != null && extension.loadStatus == DiscoveredExtension.LoadStatus.LOAD_SUCCESS) { extensions.add(extension); @@ -281,7 +291,8 @@ public class ExtensionManager { return extensions; } - private DiscoveredExtension discoverFromJar(File file) { + @Nullable + private DiscoveredExtension discoverFromJar(@NotNull File file) { try (ZipFile f = new ZipFile(file); InputStreamReader reader = new InputStreamReader(f.getInputStream(f.getEntry("extension.json")))) { @@ -309,34 +320,37 @@ public class ExtensionManager { extensionMap.put(discoveredExtension.getName().toLowerCase(), discoveredExtension); } + allExtensions: // label the loop for (DiscoveredExtension discoveredExtension : discoveredExtensions) { - List dependencies = Arrays.stream(discoveredExtension.getDependencies()) - .map(dependencyName -> { - DiscoveredExtension dependencyExtension = extensionMap.get(dependencyName.toLowerCase()); - // Specifies an extension we don't have. - if (dependencyExtension == null) { - // attempt to see if it is not already loaded (happens with dynamic (re)loading) - if (extensions.containsKey(dependencyName.toLowerCase())) { - return extensions.get(dependencyName.toLowerCase()).getOrigin(); - } else { - LOGGER.error("Extension {} requires an extension called {}.", discoveredExtension.getName(), dependencyName); - LOGGER.error("However the extension {} could not be found.", dependencyName); - LOGGER.error("Therefore {} will not be loaded.", discoveredExtension.getName()); - discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.MISSING_DEPENDENCIES; - } - } - // This will return null for an unknown-extension - return extensionMap.get(dependencyName.toLowerCase()); - }).collect(Collectors.toList()); + List dependencies = new ArrayList<>(discoveredExtension.getDependencies().length); - // If the list contains null ignore it. - if (!dependencies.contains(null)) { - dependencyMap.put( - discoveredExtension, - dependencies - ); + // Map the dependencies into DiscoveredExtensions. + for (String dependencyName : discoveredExtension.getDependencies()) { + DiscoveredExtension dependencyExtension = extensionMap.get(dependencyName.toLowerCase()); + // Specifies an extension we don't have. + if (dependencyExtension == null) { + // attempt to see if it is not already loaded (happens with dynamic (re)loading) + if (extensions.containsKey(dependencyName.toLowerCase())) { + dependencies.add(extensions.get(dependencyName.toLowerCase()).getOrigin()); + continue; // Go to the next loop in this dependency loop, this iteration is done. + } else { + LOGGER.error("Extension {} requires an extension called {}.", discoveredExtension.getName(), dependencyName); + LOGGER.error("However the extension {} could not be found.", dependencyName); + LOGGER.error("Therefore {} will not be loaded.", discoveredExtension.getName()); + discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.MISSING_DEPENDENCIES; + continue allExtensions; // the above labeled loop will go to the next extension as this dependency is invalid. + } + } + // This will add null for an unknown-extension + dependencies.add(dependencyExtension); } + + dependencyMap.put( + discoveredExtension, + dependencies + ); + } // List containing the real load order. From b5728e225566fb298e18fe86d991c7bc1b242e15 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Tue, 23 Mar 2021 20:04:25 -0400 Subject: [PATCH 030/364] Scope the dependency map --- .../server/extensions/ExtensionManager.java | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index af2d6fc55..ba83a9405 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -313,44 +313,57 @@ public class ExtensionManager { @NotNull private List generateLoadOrder(@NotNull List discoveredExtensions) { // Do some mapping so we can map strings to extensions. - Map extensionMap = new HashMap<>(); Map> dependencyMap = new HashMap<>(); - for (DiscoveredExtension discoveredExtension : discoveredExtensions) { - extensionMap.put(discoveredExtension.getName().toLowerCase(), discoveredExtension); - } + // Put dependencies in dependency map + { + Map extensionMap = new HashMap<>(); - allExtensions: // label the loop - for (DiscoveredExtension discoveredExtension : discoveredExtensions) { - - List dependencies = new ArrayList<>(discoveredExtension.getDependencies().length); - - // Map the dependencies into DiscoveredExtensions. - for (String dependencyName : discoveredExtension.getDependencies()) { - DiscoveredExtension dependencyExtension = extensionMap.get(dependencyName.toLowerCase()); - // Specifies an extension we don't have. - if (dependencyExtension == null) { - // attempt to see if it is not already loaded (happens with dynamic (re)loading) - if (extensions.containsKey(dependencyName.toLowerCase())) { - dependencies.add(extensions.get(dependencyName.toLowerCase()).getOrigin()); - continue; // Go to the next loop in this dependency loop, this iteration is done. - } else { - LOGGER.error("Extension {} requires an extension called {}.", discoveredExtension.getName(), dependencyName); - LOGGER.error("However the extension {} could not be found.", dependencyName); - LOGGER.error("Therefore {} will not be loaded.", discoveredExtension.getName()); - discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.MISSING_DEPENDENCIES; - continue allExtensions; // the above labeled loop will go to the next extension as this dependency is invalid. - } - } - // This will add null for an unknown-extension - dependencies.add(dependencyExtension); + for (DiscoveredExtension discoveredExtension : discoveredExtensions) { + extensionMap.put(discoveredExtension.getName().toLowerCase(), discoveredExtension); } - dependencyMap.put( - discoveredExtension, - dependencies - ); + allExtensions: + // label the loop + for (DiscoveredExtension discoveredExtension : discoveredExtensions) { + List dependencies = new ArrayList<>(discoveredExtension.getDependencies().length); + + // Map the dependencies into DiscoveredExtensions. + for (String dependencyName : discoveredExtension.getDependencies()) { + + DiscoveredExtension dependencyExtension = extensionMap.get(dependencyName.toLowerCase()); + // Specifies an extension we don't have. + if (dependencyExtension == null) { + + // attempt to see if it is not already loaded (happens with dynamic (re)loading) + if (extensions.containsKey(dependencyName.toLowerCase())) { + + dependencies.add(extensions.get(dependencyName.toLowerCase()).getOrigin()); + continue; // Go to the next loop in this dependency loop, this iteration is done. + + } else { + + // dependency isn't loaded, move on. + LOGGER.error("Extension {} requires an extension called {}.", discoveredExtension.getName(), dependencyName); + LOGGER.error("However the extension {} could not be found.", dependencyName); + LOGGER.error("Therefore {} will not be loaded.", discoveredExtension.getName()); + discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.MISSING_DEPENDENCIES; + continue allExtensions; // the above labeled loop will go to the next extension as this dependency is invalid. + + } + } + // This will add null for an unknown-extension + dependencies.add(dependencyExtension); + + } + + dependencyMap.put( + discoveredExtension, + dependencies + ); + + } } // List containing the real load order. @@ -358,6 +371,7 @@ public class ExtensionManager { // entries with empty lists List>> loadableExtensions; + // While there are entries with no more elements (no more dependencies) while (!( loadableExtensions = dependencyMap.entrySet().stream().filter(entry -> areAllDependenciesLoaded(entry.getValue())).collect(Collectors.toList()) From e9a5389680bdedcd598eca8f0bca412602c9b249 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Tue, 23 Mar 2021 20:13:15 -0400 Subject: [PATCH 031/364] More scoping --- .../server/extensions/ExtensionManager.java | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index ba83a9405..1bac1c390 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -312,19 +312,19 @@ public class ExtensionManager { @NotNull private List generateLoadOrder(@NotNull List discoveredExtensions) { - // Do some mapping so we can map strings to extensions. + // Extension --> Extensions it depends on. Map> dependencyMap = new HashMap<>(); // Put dependencies in dependency map { Map extensionMap = new HashMap<>(); + // go through all the discovered extensions and assign their name in a map. for (DiscoveredExtension discoveredExtension : discoveredExtensions) { extensionMap.put(discoveredExtension.getName().toLowerCase(), discoveredExtension); } - allExtensions: - // label the loop + allExtensions: // go through all the discovered extensions and get their dependencies as extensions for (DiscoveredExtension discoveredExtension : discoveredExtensions) { List dependencies = new ArrayList<>(discoveredExtension.getDependencies().length); @@ -366,29 +366,32 @@ public class ExtensionManager { } } - // List containing the real load order. + // List containing the load order. LinkedList sortedList = new LinkedList<>(); - // entries with empty lists - List>> loadableExtensions; + // TODO actually have to read this + { + // entries with empty lists + List>> loadableExtensions; - // While there are entries with no more elements (no more dependencies) - while (!( - loadableExtensions = dependencyMap.entrySet().stream().filter(entry -> areAllDependenciesLoaded(entry.getValue())).collect(Collectors.toList()) - ).isEmpty() - ) { - // Get all "loadable" (not actually being loaded!) extensions and put them in the sorted list. - for (Map.Entry> entry : loadableExtensions) { + // While there are entries with no more elements (no more dependencies) + while (!( + loadableExtensions = dependencyMap.entrySet().stream().filter(entry -> areAllDependenciesLoaded(entry.getValue())).collect(Collectors.toList()) + ).isEmpty() + ) { + // Get all "loadable" (not actually being loaded!) extensions and put them in the sorted list. + for (Map.Entry> entry : loadableExtensions) { - // Add to sorted list. - sortedList.add(entry.getKey()); + // Add to sorted list. + sortedList.add(entry.getKey()); - // Remove to make the next iterations a little bit quicker (hopefully) and to find cyclic dependencies. - dependencyMap.remove(entry.getKey()); + // Remove to make the next iterations a little bit quicker (hopefully) and to find cyclic dependencies. + dependencyMap.remove(entry.getKey()); - // Remove this dependency from all the lists (if they include it) to make way for next level of extensions. - for (var dependencies : dependencyMap.values()) { - dependencies.remove(entry.getKey()); + // Remove this dependency from all the lists (if they include it) to make way for next level of extensions. + for (var dependencies : dependencyMap.values()) { + dependencies.remove(entry.getKey()); + } } } } From 34a56cf00e8435c5f7efe0b6a18f18be05d33cbf Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 08:39:47 -0400 Subject: [PATCH 032/364] Move to Map of Extension --- .../extensions/DiscoveredExtension.java | 17 ++++++++ .../server/extensions/ExtensionManager.java | 41 ++++++++----------- .../MixinIntoMinestomCore.java | 2 +- ...inestomCoreWithJava9ModuleOnClasspath.java | 2 +- 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java index b77b688ed..c346d19c7 100644 --- a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java +++ b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java @@ -1,5 +1,6 @@ package net.minestom.server.extensions; +import net.minestom.server.extras.selfmodification.MinestomExtensionClassLoader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -28,6 +29,7 @@ public final class DiscoveredExtension { transient List files = new LinkedList<>(); transient LoadStatus loadStatus = LoadStatus.LOAD_SUCCESS; transient private File originalJar; + transient private MinestomExtensionClassLoader minestomExtensionClassLoader; @NotNull public String getName() { @@ -81,6 +83,21 @@ public final class DiscoveredExtension { return originalJar; } + MinestomExtensionClassLoader removeMinestomExtensionClassLoader() { + MinestomExtensionClassLoader oldClassLoader = getMinestomExtensionClassLoader(); + setMinestomExtensionClassLoader(null); + return oldClassLoader; + } + + void setMinestomExtensionClassLoader(@Nullable MinestomExtensionClassLoader minestomExtensionClassLoader) { + this.minestomExtensionClassLoader = minestomExtensionClassLoader; + } + + @Nullable + public MinestomExtensionClassLoader getMinestomExtensionClassLoader() { + return this.minestomExtensionClassLoader; + } + static void verifyIntegrity(@NotNull DiscoveredExtension extension) { if (extension.name == null) { StringBuilder fileList = new StringBuilder(); diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 1bac1c390..3fade047a 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -39,8 +39,10 @@ public class ExtensionManager { public final static String INDEV_RESOURCES_FOLDER = "minestom.extension.indevfolder.resources"; private final static Gson GSON = new Gson(); - private final Map extensionLoaders = new HashMap<>(); - private final Map extensions = new HashMap<>(); + // Not too much concurrency is done through extension managers. + private final Map extensions = new Hashtable<>(); + + private final File extensionFolder = new File("extensions"); private final File dependenciesFolder = new File(extensionFolder, ".libs"); private boolean loaded; @@ -141,7 +143,7 @@ public class ExtensionManager { final URL[] urls = discoveredExtension.files.toArray(new URL[0]); final MinestomExtensionClassLoader loader = newClassLoader(discoveredExtension, urls); - extensionLoaders.put(extensionName.toLowerCase(), loader); + discoveredExtension.setMinestomExtensionClassLoader(loader); } @Nullable @@ -150,7 +152,7 @@ public class ExtensionManager { final String extensionName = discoveredExtension.getName(); String mainClass = discoveredExtension.getEntrypoint(); - MinestomExtensionClassLoader loader = extensionLoaders.get(extensionName.toLowerCase()); + MinestomExtensionClassLoader loader = discoveredExtension.getMinestomExtensionClassLoader(); if (extensions.containsKey(extensionName.toLowerCase())) { LOGGER.error("An extension called '{}' has already been registered.", extensionName); @@ -502,20 +504,16 @@ public class ExtensionManager { // orphaned extension, we can insert it directly root.addChild(loader); } else { - // we need to keep track that it has actually been inserted - // even though it should always be (due to the order in which extensions are loaders), it is an additional layer of """security""" - boolean foundOne = false; + // add children to the dependencies for (String dependency : extension.getDependencies()) { - if (extensionLoaders.containsKey(dependency.toLowerCase())) { - MinestomExtensionClassLoader parentLoader = extensionLoaders.get(dependency.toLowerCase()); - parentLoader.addChild(loader); - foundOne = true; - } - } + if (extensions.containsKey(dependency.toLowerCase())) { + MinestomExtensionClassLoader parentLoader = extensions.get(dependency.toLowerCase()).getOrigin().getMinestomExtensionClassLoader(); - if (!foundOne) { - LOGGER.error("Could not load extension {}, could not find any parent inside classloader hierarchy.", extension.getName()); - throw new RuntimeException("Could not load extension " + extension.getName() + ", could not find any parent inside classloader hierarchy."); + // TODO should never happen but replace with better throws error. + assert parentLoader != null; + + parentLoader.addChild(loader); + } } } return loader; @@ -536,11 +534,6 @@ public class ExtensionManager { return extensions.get(name.toLowerCase()); } - @NotNull - public Map getExtensionLoaders() { - return new HashMap<>(extensionLoaders); - } - /** * Extensions are allowed to apply Mixin transformers, the magic happens here. */ @@ -620,7 +613,7 @@ public class ExtensionManager { extensionList.remove(ext); // remove class loader, required to reload the classes - MinestomExtensionClassLoader classloader = extensionLoaders.remove(id); + MinestomExtensionClassLoader classloader = ext.getOrigin().removeMinestomExtensionClassLoader(); try { // close resources classloader.close(); @@ -784,8 +777,8 @@ public class ExtensionManager { manager.setupCodeModifiers(discovered, MinestomRootClassLoader.getInstance()); // setup is done, remove all extension classloaders - for (MinestomExtensionClassLoader extensionLoader : manager.getExtensionLoaders().values()) { - MinestomRootClassLoader.getInstance().removeChildInHierarchy(extensionLoader); + for (Extension extension : manager.getExtensions()) { + MinestomRootClassLoader.getInstance().removeChildInHierarchy(extension.getOrigin().getMinestomExtensionClassLoader()); } LOGGER.info("Early load of code modifiers from extensions done!"); } diff --git a/src/test/java/improveextensions/MixinIntoMinestomCore.java b/src/test/java/improveextensions/MixinIntoMinestomCore.java index 131734e91..d299f5f43 100644 --- a/src/test/java/improveextensions/MixinIntoMinestomCore.java +++ b/src/test/java/improveextensions/MixinIntoMinestomCore.java @@ -23,7 +23,7 @@ public class MixinIntoMinestomCore extends Extension { System.out.println(c.toString()); try { Assertions.assertTrue(success, "InstanceContainer must have been mixed in with improveextensions.InstanceContainerMixin"); - Assertions.assertEquals(1, MinecraftServer.getExtensionManager().getExtensionLoaders().size(), "Only one extension classloader (this extension's) must be active."); + Assertions.assertEquals(1, MinecraftServer.getExtensionManager().getExtensions().stream().map(extension -> extension.getOrigin().getMinestomExtensionClassLoader()).toArray().length, "Only one extension classloader (this extension's) must be active."); } catch (AssertionFailedError e) { e.printStackTrace(); } diff --git a/src/test/java/improveextensions/MixinIntoMinestomCoreWithJava9ModuleOnClasspath.java b/src/test/java/improveextensions/MixinIntoMinestomCoreWithJava9ModuleOnClasspath.java index 2de1ba459..888fde76c 100644 --- a/src/test/java/improveextensions/MixinIntoMinestomCoreWithJava9ModuleOnClasspath.java +++ b/src/test/java/improveextensions/MixinIntoMinestomCoreWithJava9ModuleOnClasspath.java @@ -28,7 +28,7 @@ public class MixinIntoMinestomCoreWithJava9ModuleOnClasspath extends Extension { System.out.println(c.toString()); try { Assertions.assertTrue(MixinIntoMinestomCore.success, "InstanceContainer must have been mixed in with improveextensions.InstanceContainerMixin"); - Assertions.assertEquals(1, MinecraftServer.getExtensionManager().getExtensionLoaders().size(), "Only one extension classloader (this extension's) must be active."); + Assertions.assertEquals(1, MinecraftServer.getExtensionManager().getExtensions().stream().map(extension -> extension.getOrigin().getMinestomExtensionClassLoader()).toArray().length, "Only one extension classloader (this extension's) must be active."); Assertions.assertEquals("Test", mockedList.get(0)); } catch (AssertionFailedError e) { e.printStackTrace(); From f2924b37a117e437ac70dea00a17fa65e823e45a Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 08:48:37 -0400 Subject: [PATCH 033/364] Make logger public Good for encompassing logs EX a hanging System.out log can be passed back to the extension that called it --- src/main/java/net/minestom/server/extensions/Extension.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/extensions/Extension.java b/src/main/java/net/minestom/server/extensions/Extension.java index 1b6316043..cd9b52028 100644 --- a/src/main/java/net/minestom/server/extensions/Extension.java +++ b/src/main/java/net/minestom/server/extensions/Extension.java @@ -73,7 +73,7 @@ public abstract class Extension { * @return The logger for the extension */ @NotNull - protected Logger getLogger() { + public Logger getLogger() { return logger; } From 5e52121f3b769137f9e3903c87b01c8c9d95c858 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 08:48:55 -0400 Subject: [PATCH 034/364] Use one LinkedHashMap instead of multiple lists --- .../server/extensions/ExtensionManager.java | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 3fade047a..6c248372d 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -25,7 +25,6 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; import java.util.zip.ZipFile; @@ -40,16 +39,13 @@ public class ExtensionManager { private final static Gson GSON = new Gson(); // Not too much concurrency is done through extension managers. - private final Map extensions = new Hashtable<>(); + private final Map extensions = new LinkedHashMap<>(); + private final Map immutableExtensions = Collections.unmodifiableMap(extensions); - private final File extensionFolder = new File("extensions"); private final File dependenciesFolder = new File(extensionFolder, ".libs"); private boolean loaded; - private final List extensionList = new CopyOnWriteArrayList<>(); - private final List immutableExtensionListView = Collections.unmodifiableList(extensionList); - // Option private boolean loadOnStartup = true; @@ -131,15 +127,13 @@ public class ExtensionManager { // periodically cleanup observers MinecraftServer.getSchedulerManager().buildTask(() -> { - for (Extension ext : extensionList) { + for (Extension ext : extensions.values()) { ext.cleanupObservers(); } }).repeat(1L, TimeUnit.MINUTE).schedule(); } private void setupClassLoader(@NotNull DiscoveredExtension discoveredExtension) { - final String extensionName = discoveredExtension.getName(); - final URL[] urls = discoveredExtension.files.toArray(new URL[0]); final MinestomExtensionClassLoader loader = newClassLoader(discoveredExtension, urls); @@ -149,7 +143,7 @@ public class ExtensionManager { @Nullable private Extension attemptSingleLoad(@NotNull DiscoveredExtension discoveredExtension) { // Create ExtensionDescription (authors, version etc.) - final String extensionName = discoveredExtension.getName(); + String extensionName = discoveredExtension.getName(); String mainClass = discoveredExtension.getEntrypoint(); MinestomExtensionClassLoader loader = discoveredExtension.getMinestomExtensionClassLoader(); @@ -237,7 +231,7 @@ public class ExtensionManager { } } - extensionList.add(extension); // add to a list, as lists preserve order + // add to a linked hash map, as lists preserve order extensions.put(extensionName.toLowerCase(), extension); return extension; @@ -421,7 +415,7 @@ public class ExtensionManager { private void loadDependencies(List extensions) { List allLoadedExtensions = new LinkedList<>(extensions); - for (Extension extension : extensionList) + for (Extension extension : immutableExtensions.values()) allLoadedExtensions.add(extension.getOrigin()); ExtensionDependencyResolver extensionDependencyResolver = new ExtensionDependencyResolver(allLoadedExtensions); @@ -525,8 +519,8 @@ public class ExtensionManager { } @NotNull - public List getExtensions() { - return immutableExtensionListView; + public Collection getExtensions() { + return immutableExtensions.values(); } @Nullable @@ -603,14 +597,13 @@ public class ExtensionManager { // remove as dependent of other extensions // this avoids issues where a dependent extension fails to reload, and prevents the base extension to reload too - for (Extension e : extensionList) { + for (Extension e : extensions.values()) { e.getDependents().remove(ext.getOrigin().getName()); } String id = ext.getOrigin().getName().toLowerCase(); // remove from loaded extensions extensions.remove(id); - extensionList.remove(ext); // remove class loader, required to reload the classes MinestomExtensionClassLoader classloader = ext.getOrigin().removeMinestomExtensionClassLoader(); From fe7b48df4890b32ffe2b7c7e13244bab4600e8cc Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 24 Mar 2021 13:58:44 +0100 Subject: [PATCH 035/364] Added ArgumentResourceLocation --- .../builder/arguments/ArgumentType.java | 7 ++++ .../minecraft/ArgumentResourceLocation.java | 34 +++++++++++++++++++ src/test/java/demo/commands/TestCommand.java | 15 ++------ 3 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentResourceLocation.java diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java index 9d3942014..e5eff2866 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java @@ -134,6 +134,13 @@ public class ArgumentType { return new ArgumentParticle(id); } + /** + * @see ArgumentResourceLocation + */ + public static ArgumentResourceLocation ResourceLocation(@NotNull String id) { + return new ArgumentResourceLocation(id); + } + /** * @see ArgumentPotionEffect */ diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentResourceLocation.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentResourceLocation.java new file mode 100644 index 000000000..7bf8b410a --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentResourceLocation.java @@ -0,0 +1,34 @@ +package net.minestom.server.command.builder.arguments.minecraft; + +import net.minestom.server.command.builder.NodeMaker; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +public class ArgumentResourceLocation extends Argument { + + public static final int SPACE_ERROR = 1; + + public ArgumentResourceLocation(@NotNull String id) { + super(id); + } + + @NotNull + @Override + public String parse(@NotNull String input) throws ArgumentSyntaxException { + if (input.contains(StringUtils.SPACE)) + throw new ArgumentSyntaxException("Resource location cannot contain space character", input, SPACE_ERROR); + + return input; + } + + @Override + public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); + argumentNode.parser = "minecraft:resource_location"; + + nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode}); + } +} diff --git a/src/test/java/demo/commands/TestCommand.java b/src/test/java/demo/commands/TestCommand.java index de7c3b936..88332aee3 100644 --- a/src/test/java/demo/commands/TestCommand.java +++ b/src/test/java/demo/commands/TestCommand.java @@ -14,22 +14,11 @@ public class TestCommand extends Command { super("testcmd"); setDefaultExecutor(this::usage); - var test1 = Word("msg").setSuggestionCallback((sender, context, suggestion) -> { - suggestion.addEntry(new SuggestionEntry("test")); - }); - - var test2 = String("msg2").setSuggestionCallback((sender, context, suggestion) -> { - suggestion.addEntry(new SuggestionEntry("greer")); - }); + var test = ResourceLocation("msg"); addSyntax((sender, context) -> { System.out.println("executed"); - }, Literal("test"), test1, test2); - - addSyntax((sender, context) -> { - System.out.println("cmd syntax"); - }, Literal("debug"), Command("cmd").setShortcut("testcmd test")); - + },test); } private void usage(CommandSender sender, CommandContext context) { From 5320beddb86d5e8ef361f2e17a77062ae72b29ba Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 24 Mar 2021 15:13:25 +0100 Subject: [PATCH 036/364] Added ArgumentUUID --- .../builder/arguments/ArgumentType.java | 7 ++++ .../arguments/minecraft/ArgumentUUID.java | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentUUID.java diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java index e5eff2866..bf244ba8d 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java @@ -197,6 +197,13 @@ public class ArgumentType { return new ArgumentComponent(id); } + /** + * @see ArgumentUUID + */ + public static ArgumentUUID UUID(@NotNull String id) { + return new ArgumentUUID(id); + } + /** * @see ArgumentNbtTag */ diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentUUID.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentUUID.java new file mode 100644 index 000000000..9521f9245 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentUUID.java @@ -0,0 +1,36 @@ +package net.minestom.server.command.builder.arguments.minecraft; + +import net.minestom.server.command.builder.NodeMaker; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class ArgumentUUID extends Argument { + + public static final int INVALID_UUID = -1; + + public ArgumentUUID(@NotNull String id) { + super(id); + } + + @NotNull + @Override + public UUID parse(@NotNull String input) throws ArgumentSyntaxException { + try { + return UUID.fromString(input); + } catch (IllegalArgumentException exception) { + throw new ArgumentSyntaxException("Invalid UUID", input, INVALID_UUID); + } + } + + @Override + public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); + argumentNode.parser = "minecraft:uuid"; + + nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode}); + } +} From 5e92e04c29825c0d6c33b24e13f2aacb4e87df38 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 11:23:56 -0400 Subject: [PATCH 037/364] JavaDocs --- .../extensions/DiscoveredExtension.java | 57 +++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java index c346d19c7..2739dac9a 100644 --- a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java +++ b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java @@ -11,24 +11,59 @@ import java.net.URL; import java.util.LinkedList; import java.util.List; +/** + * Represents an extension from an `extension.json` that is capable of powering an Extension object. + * + * This has no constructor as its properties are set via GSON. + */ public final class DiscoveredExtension { - public final static Logger LOGGER = LoggerFactory.getLogger(DiscoveredExtension.class); + /** Static logger for this class. */ + public static final Logger LOGGER = LoggerFactory.getLogger(DiscoveredExtension.class); + /** The regex that this name must pass. If it doesn't, it will not be accepted. */ public static final String NAME_REGEX = "[A-Za-z][_A-Za-z0-9]+"; + + /** Name of the DiscoveredExtension. Unique for all extensions. */ private String name; + + /** Main class of this DiscoveredExtension, must extend Extension. */ private String entrypoint; + + /** Version of this extension, highly reccomended to set it. */ private String version; + + /** Points to sponge mixin config in resources folder. */ private String mixinConfig; + + /** People who have made this extension. */ private String[] authors; + + /** All code modifiers (the classes they point to) */ private String[] codeModifiers; + + /** List of extension names that this depends on. */ private String[] dependencies; + + /** List of Repositories and URLs that this depends on. */ private ExternalDependencies externalDependencies; + + /** A list of any missing code modifiers to be used for logging. */ private final List missingCodeModifiers = new LinkedList<>(); + + /** If this extension couldn't load its mixin configuration. */ private boolean failedToLoadMixin = false; + + /** All files of this extension */ transient List files = new LinkedList<>(); + + /** The load status of this extension -- LOAD_SUCCESS is the only good one. */ transient LoadStatus loadStatus = LoadStatus.LOAD_SUCCESS; + + /** The original jar this is from. */ transient private File originalJar; + + /** The class loader that powers it. */ transient private MinestomExtensionClassLoader minestomExtensionClassLoader; @NotNull @@ -74,7 +109,7 @@ public final class DiscoveredExtension { return externalDependencies; } - void setOriginalJar(@Nullable File file) { + public void setOriginalJar(@Nullable File file) { originalJar = file; } @@ -83,13 +118,13 @@ public final class DiscoveredExtension { return originalJar; } - MinestomExtensionClassLoader removeMinestomExtensionClassLoader() { + public MinestomExtensionClassLoader removeMinestomExtensionClassLoader() { MinestomExtensionClassLoader oldClassLoader = getMinestomExtensionClassLoader(); setMinestomExtensionClassLoader(null); return oldClassLoader; } - void setMinestomExtensionClassLoader(@Nullable MinestomExtensionClassLoader minestomExtensionClassLoader) { + public void setMinestomExtensionClassLoader(@Nullable MinestomExtensionClassLoader minestomExtensionClassLoader) { this.minestomExtensionClassLoader = minestomExtensionClassLoader; } @@ -98,7 +133,14 @@ public final class DiscoveredExtension { return this.minestomExtensionClassLoader; } - static void verifyIntegrity(@NotNull DiscoveredExtension extension) { + /** + * Ensures that all properties of this extension are properly set if they aren't + * + * TODO this is an impure function. + * + * @param extension The extension to verify + */ + public static void verifyIntegrity(@NotNull DiscoveredExtension extension) { if (extension.name == null) { StringBuilder fileList = new StringBuilder(); for (URL f : extension.files) { @@ -181,6 +223,11 @@ public final class DiscoveredExtension { return failedToLoadMixin; } + /** + * The status this extension has, all are breakpoints. + * + * LOAD_SUCCESS is the only valid one. + */ enum LoadStatus { LOAD_SUCCESS("Actually, it did not fail. This message should not have been printed."), MISSING_DEPENDENCIES("Missing dependencies, check your logs."), From faa53b3d48668a5c30ab5f496b2d7ac8fad172ec Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 11:55:46 -0400 Subject: [PATCH 038/364] Cleanup, renaming, and docs --- .../server/extensions/ExtensionManager.java | 162 +++++++++++++----- 1 file changed, 119 insertions(+), 43 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 6c248372d..6d7178339 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -74,54 +74,93 @@ public class ExtensionManager { this.loadOnStartup = loadOnStartup; } + /** + * Loads all extensions in the extension folder into this server. + * + * Pipeline: + * + * Finds all .jar files in the extensions folder. + * + * Per each jar: + * Turns its extension.json into a DiscoveredExtension object. + * Verifies that all properties of extension.json are correctly set. + * + * It then sorts all those jars depending on its load order (extensions can have dependencies) + * + * Afterwards, it loads all external dependencies (sort of an outsourced dynamic shadowJar) + * + * Then removes any invalid extensions (Invalid being its Load Status isn't SUCCESS) + * + * After that, it set its classloaders so each extension is self-contained, + * + * Removes invalid extensions again, + * + * and loads all of those extensions into Minestom + * (Extension fields are set via reflection after each extension is verified, then loaded.) + * + * If the extension successfully loads, add it to the global extension Map (Name to Extension) + * + * Then makes a scheduler to clean observers per extension. + * + */ public void loadExtensions() { Check.stateCondition(loaded, "Extensions are already loaded!"); this.loaded = true; - // Make extensions folder if necessary - if (!extensionFolder.exists()) { - if (!extensionFolder.mkdirs()) { - LOGGER.error("Could not find or create the extension folder, extensions will not be loaded!"); - return; + // Initialize folders + { + // Make extensions folder if necessary + if (!extensionFolder.exists()) { + if (!extensionFolder.mkdirs()) { + LOGGER.error("Could not find or create the extension folder, extensions will not be loaded!"); + return; + } + } + + // Make dependencies folder if necessary + if (!dependenciesFolder.exists()) { + if (!dependenciesFolder.mkdirs()) { + LOGGER.error("Could not find nor create the extension dependencies folder, extensions will not be loaded!"); + return; + } } } - // Make dependencies folder if necessary - if (!dependenciesFolder.exists()) { - if (!dependenciesFolder.mkdirs()) { - LOGGER.error("Could not find nor create the extension dependencies folder, extensions will not be loaded!"); - return; + // Load extensions + { + // Get all extensions and order them accordingly. + List discoveredExtensions = discoverExtensions(); + discoveredExtensions = generateLoadOrder(discoveredExtensions); + loadDependencies(discoveredExtensions); + + // remove invalid extensions + discoveredExtensions.removeIf(ext -> ext.loadStatus != DiscoveredExtension.LoadStatus.LOAD_SUCCESS); + + // set class loaders for all extensions. + for (DiscoveredExtension discoveredExtension : discoveredExtensions) { + try { + setupClassLoader(discoveredExtension); + } catch (Exception e) { + discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.FAILED_TO_SETUP_CLASSLOADER; + MinecraftServer.getExceptionManager().handleException(e); + LOGGER.error("Failed to load extension {}", discoveredExtension.getName()); + LOGGER.error("Failed to load extension", e); + } } - } - List discoveredExtensions = discoverExtensions(); - discoveredExtensions = generateLoadOrder(discoveredExtensions); - loadDependencies(discoveredExtensions); - // remove invalid extensions - discoveredExtensions.removeIf(ext -> ext.loadStatus != DiscoveredExtension.LoadStatus.LOAD_SUCCESS); + // remove invalid extensions + discoveredExtensions.removeIf(ext -> ext.loadStatus != DiscoveredExtension.LoadStatus.LOAD_SUCCESS); + setupCodeModifiers(discoveredExtensions); - for (DiscoveredExtension discoveredExtension : discoveredExtensions) { - try { - setupClassLoader(discoveredExtension); - } catch (Exception e) { - discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.FAILED_TO_SETUP_CLASSLOADER; - MinecraftServer.getExceptionManager().handleException(e); - LOGGER.error("Failed to load extension {}", discoveredExtension.getName()); - LOGGER.error("Failed to load extension", e); - } - } - - // remove invalid extensions - discoveredExtensions.removeIf(ext -> ext.loadStatus != DiscoveredExtension.LoadStatus.LOAD_SUCCESS); - setupCodeModifiers(discoveredExtensions); - - for (DiscoveredExtension discoveredExtension : discoveredExtensions) { - try { - attemptSingleLoad(discoveredExtension); - } catch (Exception e) { - discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.LOAD_FAILED; - LOGGER.error("Failed to load extension {}", discoveredExtension.getName()); - MinecraftServer.getExceptionManager().handleException(e); + // Load the extensions + for (DiscoveredExtension discoveredExtension : discoveredExtensions) { + try { + loadExtension(discoveredExtension); + } catch (Exception e) { + discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.LOAD_FAILED; + LOGGER.error("Failed to load extension {}", discoveredExtension.getName()); + MinecraftServer.getExceptionManager().handleException(e); + } } } @@ -133,6 +172,11 @@ public class ExtensionManager { }).repeat(1L, TimeUnit.MINUTE).schedule(); } + /** + * Initializes the class loader for this extension + * + * @param discoveredExtension The extension to initialize said extension's class loader. + */ private void setupClassLoader(@NotNull DiscoveredExtension discoveredExtension) { final URL[] urls = discoveredExtension.files.toArray(new URL[0]); final MinestomExtensionClassLoader loader = newClassLoader(discoveredExtension, urls); @@ -140,8 +184,15 @@ public class ExtensionManager { discoveredExtension.setMinestomExtensionClassLoader(loader); } + /** + * Loads an extension into Minestom. + * + * @param discoveredExtension The extension. Make sure to verify its integrity, set its class loader, and its files. + * + * @return An extension object made from this DiscoveredExtension + */ @Nullable - private Extension attemptSingleLoad(@NotNull DiscoveredExtension discoveredExtension) { + private Extension loadExtension(@NotNull DiscoveredExtension discoveredExtension) { // Create ExtensionDescription (authors, version etc.) String extensionName = discoveredExtension.getName(); String mainClass = discoveredExtension.getEntrypoint(); @@ -237,6 +288,13 @@ public class ExtensionManager { return extension; } + /** + * Get all extensions from the extensions folder and make them discovered. + * + * It skims the extension folder, discovers and verifies each extension, and returns those created DiscoveredExtensions. + * + * @return A list of discovered extensions from this folder. + */ @NotNull private List discoverExtensions() { List extensions = new LinkedList<>(); @@ -287,11 +345,19 @@ public class ExtensionManager { return extensions; } + /** + * Grabs a discovered extension from a jar. + * + * @param file The jar to grab it from (a .jar is a formatted .zip file) + * + * @return The created DiscoveredExtension. + */ @Nullable private DiscoveredExtension discoverFromJar(@NotNull File file) { try (ZipFile f = new ZipFile(file); InputStreamReader reader = new InputStreamReader(f.getInputStream(f.getEntry("extension.json")))) { + // Initialize DiscoveredExtension from GSON. DiscoveredExtension extension = GSON.fromJson(reader, DiscoveredExtension.class); extension.setOriginalJar(file); extension.files.add(file.toURI().toURL()); @@ -372,7 +438,7 @@ public class ExtensionManager { // While there are entries with no more elements (no more dependencies) while (!( - loadableExtensions = dependencyMap.entrySet().stream().filter(entry -> areAllDependenciesLoaded(entry.getValue())).collect(Collectors.toList()) + loadableExtensions = dependencyMap.entrySet().stream().filter(entry -> isLoaded(entry.getValue())).collect(Collectors.toList()) ).isEmpty() ) { // Get all "loadable" (not actually being loaded!) extensions and put them in the sorted list. @@ -408,8 +474,16 @@ public class ExtensionManager { return sortedList; } - private boolean areAllDependenciesLoaded(@NotNull List dependencies) { - return dependencies.isEmpty() || dependencies.stream().allMatch(ext -> extensions.containsKey(ext.getName().toLowerCase())); + /** + * Checks if this list of extensions are loaded + * @param extensions The list of extensions to check against. + * @return If all of these extensions are loaded. + */ + private boolean isLoaded(@NotNull List extensions) { + return + extensions.isEmpty() // Don't waste CPU on checking an empty array + // Make sure the internal extensions list contains all of these. + || extensions.stream().allMatch(ext -> this.extensions.containsKey(ext.getName().toLowerCase())); } private void loadDependencies(List extensions) { @@ -699,7 +773,7 @@ public class ExtensionManager { for (DiscoveredExtension toReload : extensionsToLoad) { // reload extensions LOGGER.info("Actually load extension {}", toReload.getName()); - Extension loadedExtension = attemptSingleLoad(toReload); + Extension loadedExtension = loadExtension(toReload); if (loadedExtension != null) { newExtensions.add(loadedExtension); } @@ -720,9 +794,11 @@ public class ExtensionManager { public void unloadExtension(String extensionName) { Extension ext = extensions.get(extensionName.toLowerCase()); + if (ext == null) { throw new IllegalArgumentException("Extension " + extensionName + " is not currently loaded."); } + List dependents = new LinkedList<>(ext.getDependents()); // copy dependents list for (String dependentID : dependents) { From 0fe222ea859517a774335aa40cac9b83d22238c3 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 11:58:51 -0400 Subject: [PATCH 039/364] Small docs cleanup --- .../net/minestom/server/extensions/ExtensionManager.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 6d7178339..1d9be8217 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -85,9 +85,10 @@ public class ExtensionManager { * Turns its extension.json into a DiscoveredExtension object. * Verifies that all properties of extension.json are correctly set. * - * It then sorts all those jars depending on its load order (extensions can have dependencies) + * It then sorts all those jars by their load order (making sure that an extension's dependencies load before it) + * Note: Cyclic dependencies will stop both extensions from being loaded. * - * Afterwards, it loads all external dependencies (sort of an outsourced dynamic shadowJar) + * Afterwards, it loads all external dependencies and adds them to the extension's files * * Then removes any invalid extensions (Invalid being its Load Status isn't SUCCESS) * @@ -100,7 +101,7 @@ public class ExtensionManager { * * If the extension successfully loads, add it to the global extension Map (Name to Extension) * - * Then makes a scheduler to clean observers per extension. + * And finally make a scheduler to clean observers per extension. * */ public void loadExtensions() { From 2de657bca6d93819a6aaed92ae25cb5928405c82 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 12:16:29 -0400 Subject: [PATCH 040/364] Outsource classloader to DiscoveredExtension --- .../extensions/DiscoveredExtension.java | 31 ++++++++- .../server/extensions/ExtensionManager.java | 63 ++++--------------- 2 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java index 2739dac9a..63dcaa9b6 100644 --- a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java +++ b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java @@ -1,6 +1,8 @@ package net.minestom.server.extensions; +import net.minestom.server.MinecraftServer; import net.minestom.server.extras.selfmodification.MinestomExtensionClassLoader; +import net.minestom.server.extras.selfmodification.MinestomRootClassLoader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -136,8 +138,6 @@ public final class DiscoveredExtension { /** * Ensures that all properties of this extension are properly set if they aren't * - * TODO this is an impure function. - * * @param extension The extension to verify */ public static void verifyIntegrity(@NotNull DiscoveredExtension extension) { @@ -223,6 +223,33 @@ public final class DiscoveredExtension { return failedToLoadMixin; } + public MinestomExtensionClassLoader makeClassLoader() { + final URL[] urls = this.files.toArray(new URL[0]); + + MinestomRootClassLoader root = MinestomRootClassLoader.getInstance(); + + MinestomExtensionClassLoader loader = new MinestomExtensionClassLoader(this.getName(), this.getEntrypoint(), urls, root); + + if (this.getDependencies().length == 0) { + // orphaned extension, we can insert it directly + root.addChild(loader); + } else { + // add children to the dependencies + for (String dependency : this.getDependencies()) { + if (MinecraftServer.getExtensionManager().hasExtension(dependency.toLowerCase())) { + MinestomExtensionClassLoader parentLoader = MinecraftServer.getExtensionManager().getExtension(dependency.toLowerCase()).getOrigin().getMinestomExtensionClassLoader(); + + // TODO should never happen but replace with better throws error. + assert parentLoader != null; + + parentLoader.addChild(loader); + } + } + } + + return loader; + } + /** * The status this extension has, all are breakpoints. * diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 1d9be8217..36cf6d1fe 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -140,7 +140,7 @@ public class ExtensionManager { // set class loaders for all extensions. for (DiscoveredExtension discoveredExtension : discoveredExtensions) { try { - setupClassLoader(discoveredExtension); + discoveredExtension.setMinestomExtensionClassLoader(discoveredExtension.makeClassLoader()); } catch (Exception e) { discoveredExtension.loadStatus = DiscoveredExtension.LoadStatus.FAILED_TO_SETUP_CLASSLOADER; MinecraftServer.getExceptionManager().handleException(e); @@ -173,18 +173,6 @@ public class ExtensionManager { }).repeat(1L, TimeUnit.MINUTE).schedule(); } - /** - * Initializes the class loader for this extension - * - * @param discoveredExtension The extension to initialize said extension's class loader. - */ - private void setupClassLoader(@NotNull DiscoveredExtension discoveredExtension) { - final URL[] urls = discoveredExtension.files.toArray(new URL[0]); - final MinestomExtensionClassLoader loader = newClassLoader(discoveredExtension, urls); - - discoveredExtension.setMinestomExtensionClassLoader(loader); - } - /** * Loads an extension into Minestom. * @@ -248,7 +236,7 @@ public class ExtensionManager { return null; } - // Set extension description + // Set extension origin to its DiscoveredExtension try { Field originField = Extension.class.getDeclaredField("origin"); originField.setAccessible(true); @@ -274,12 +262,12 @@ public class ExtensionManager { } // add dependents to pre-existing extensions, so that they can easily be found during reloading - for (String dependency : discoveredExtension.getDependencies()) { - Extension dep = extensions.get(dependency.toLowerCase()); - if (dep == null) { - LOGGER.warn("Dependency {} of {} is null? This means the extension has been loaded without its dependency, which could cause issues later.", dependency, discoveredExtension.getName()); + for (String dependencyName : discoveredExtension.getDependencies()) { + Extension dependency = extensions.get(dependencyName.toLowerCase()); + if (dependency == null) { + LOGGER.warn("Dependency {} of {} is null? This means the extension has been loaded without its dependency, which could cause issues later.", dependencyName, discoveredExtension.getName()); } else { - dep.getDependents().add(discoveredExtension.getName()); + dependency.getDependents().add(discoveredExtension.getName()); } } @@ -559,35 +547,6 @@ public class ExtensionManager { } } - /** - * Creates a new class loader for the given extension. - * Will add the new loader as a child of all its dependencies' loaders. - * - * @param urls {@link URL} (usually a JAR) that should be loaded. - */ - @NotNull - public MinestomExtensionClassLoader newClassLoader(@NotNull DiscoveredExtension extension, @NotNull URL[] urls) { - MinestomRootClassLoader root = MinestomRootClassLoader.getInstance(); - MinestomExtensionClassLoader loader = new MinestomExtensionClassLoader(extension.getName(), extension.getEntrypoint(), urls, root); - if (extension.getDependencies().length == 0) { - // orphaned extension, we can insert it directly - root.addChild(loader); - } else { - // add children to the dependencies - for (String dependency : extension.getDependencies()) { - if (extensions.containsKey(dependency.toLowerCase())) { - MinestomExtensionClassLoader parentLoader = extensions.get(dependency.toLowerCase()).getOrigin().getMinestomExtensionClassLoader(); - - // TODO should never happen but replace with better throws error. - assert parentLoader != null; - - parentLoader.addChild(loader); - } - } - } - return loader; - } - @NotNull public File getExtensionFolder() { return extensionFolder; @@ -603,6 +562,10 @@ public class ExtensionManager { return extensions.get(name.toLowerCase()); } + public boolean hasExtension(@NotNull String name) { + return extensions.containsKey(name); + } + /** * Extensions are allowed to apply Mixin transformers, the magic happens here. */ @@ -763,7 +726,7 @@ public class ExtensionManager { // setup new classloaders for the extensions to reload for (DiscoveredExtension toReload : extensionsToLoad) { LOGGER.debug("Setting up classloader for extension {}", toReload.getName()); - setupClassLoader(toReload); + toReload.setMinestomExtensionClassLoader(toReload.makeClassLoader()); } // setup code modifiers for these extensions @@ -840,7 +803,7 @@ public class ExtensionManager { // setup extension class loaders, so that Mixin can load the json configuration file correctly for (DiscoveredExtension e : discovered) { - manager.setupClassLoader(e); + e.setMinestomExtensionClassLoader(e.makeClassLoader()); } // setup code modifiers and mixins From 606079ea980a20bfeacca335a976c4d05ac702ec Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 12:23:44 -0400 Subject: [PATCH 041/364] Move cleanup to front, don't discover if not necessary --- .../server/extensions/ExtensionManager.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 36cf6d1fe..0bd5bafc7 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -127,10 +127,21 @@ public class ExtensionManager { } } + // Periodically cleanup observers + MinecraftServer.getSchedulerManager().buildTask(() -> { + for (Extension ext : extensions.values()) { + ext.cleanupObservers(); + } + }).repeat(1L, TimeUnit.MINUTE).schedule(); + // Load extensions { // Get all extensions and order them accordingly. List discoveredExtensions = discoverExtensions(); + + // Don't waste resources on doing extra actions if there is nothing to do. + if (discoveredExtensions.isEmpty()) return; + discoveredExtensions = generateLoadOrder(discoveredExtensions); loadDependencies(discoveredExtensions); @@ -164,13 +175,6 @@ public class ExtensionManager { } } } - - // periodically cleanup observers - MinecraftServer.getSchedulerManager().buildTask(() -> { - for (Extension ext : extensions.values()) { - ext.cleanupObservers(); - } - }).repeat(1L, TimeUnit.MINUTE).schedule(); } /** From 082330dcd45926438f0f779049e402a04a3cca77 Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 12:25:33 -0400 Subject: [PATCH 042/364] Remove invalid TODO --- src/main/java/net/minestom/server/MinecraftServer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index eae0aaab1..7c2c2cff1 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -750,7 +750,6 @@ public final class MinecraftServer { // Load extensions extensionManager.loadExtensions(); // Init extensions - // TODO: Extensions should handle depending on each other and have a load-order. extensionManager.getExtensions().forEach(Extension::preInitialize); extensionManager.getExtensions().forEach(Extension::initialize); extensionManager.getExtensions().forEach(Extension::postInitialize); From 69efdfd63f122802779aaad3951f3ce2b3a830ab Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 13:25:36 -0400 Subject: [PATCH 043/364] Spacing, more annotations --- .../server/extensions/ExtensionManager.java | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 0bd5bafc7..133dae805 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -76,33 +76,45 @@ public class ExtensionManager { /** * Loads all extensions in the extension folder into this server. + *

* * Pipeline: - * + *
* Finds all .jar files in the extensions folder. - * + *
* Per each jar: + *
* Turns its extension.json into a DiscoveredExtension object. + *
* Verifies that all properties of extension.json are correctly set. + *

* * It then sorts all those jars by their load order (making sure that an extension's dependencies load before it) + *
* Note: Cyclic dependencies will stop both extensions from being loaded. + *

* * Afterwards, it loads all external dependencies and adds them to the extension's files + *

* * Then removes any invalid extensions (Invalid being its Load Status isn't SUCCESS) + *

* * After that, it set its classloaders so each extension is self-contained, + *

* * Removes invalid extensions again, + *

* * and loads all of those extensions into Minestom + *
* (Extension fields are set via reflection after each extension is verified, then loaded.) + *

* * If the extension successfully loads, add it to the global extension Map (Name to Extension) + *

* * And finally make a scheduler to clean observers per extension. - * */ public void loadExtensions() { Check.stateCondition(loaded, "Extensions are already loaded!"); @@ -479,7 +491,7 @@ public class ExtensionManager { || extensions.stream().allMatch(ext -> this.extensions.containsKey(ext.getName().toLowerCase())); } - private void loadDependencies(List extensions) { + private void loadDependencies(@NotNull List extensions) { List allLoadedExtensions = new LinkedList<>(extensions); for (Extension extension : immutableExtensions.values()) @@ -536,7 +548,7 @@ public class ExtensionManager { } } - private void addDependencyFile(ResolvedDependency dependency, DiscoveredExtension extension) { + private void addDependencyFile(@NotNull ResolvedDependency dependency, @NotNull DiscoveredExtension extension) { URL location = dependency.getContentsLocation(); extension.files.add(location); LOGGER.trace("Added dependency {} to extension {} classpath", location.toExternalForm(), extension.getName()); @@ -626,7 +638,7 @@ public class ExtensionManager { LOGGER.info("Done loading code modifiers."); } - private void unload(Extension ext) { + private void unload(@NotNull Extension ext) { ext.preTerminate(); ext.terminate(); // remove callbacks for this extension @@ -658,7 +670,7 @@ public class ExtensionManager { MinestomRootClassLoader.getInstance().removeChildInHierarchy(classloader); } - public void reload(String extensionName) { + public boolean reload(@NotNull String extensionName) { Extension ext = extensions.get(extensionName.toLowerCase()); if (ext == null) { throw new IllegalArgumentException("Extension " + extensionName + " is not currently loaded."); @@ -667,7 +679,7 @@ public class ExtensionManager { File originalJar = ext.getOrigin().getOriginalJar(); if (originalJar == null) { LOGGER.error("Cannot reload extension {} that is not from a .jar file!", extensionName); - return; + return false; } LOGGER.info("Reload extension {} from jar file {}", extensionName, originalJar.getAbsolutePath()); @@ -680,7 +692,7 @@ public class ExtensionManager { originalJarsOfDependents.add(dependentOriginalJar); if (dependentOriginalJar == null) { LOGGER.error("Cannot reload extension {} that is not from a .jar file!", dependentID); - return; + return false; } LOGGER.info("Unloading dependent extension {} (because it depends on {})", dependentID, extensionName); @@ -708,9 +720,11 @@ public class ExtensionManager { // ensure correct order of dependencies loadExtensionList(extensionsToReload); + + return true; } - public boolean loadDynamicExtension(File jarFile) throws FileNotFoundException { + public boolean loadDynamicExtension(@NotNull File jarFile) throws FileNotFoundException { if (!jarFile.exists()) { throw new FileNotFoundException("File '" + jarFile.getAbsolutePath() + "' does not exists. Cannot load extension."); } @@ -721,7 +735,7 @@ public class ExtensionManager { return loadExtensionList(extensionsToLoad); } - private boolean loadExtensionList(List extensionsToLoad) { + private boolean loadExtensionList(@NotNull List extensionsToLoad) { // ensure correct order of dependencies LOGGER.debug("Reorder extensions to ensure proper load order"); extensionsToLoad = generateLoadOrder(extensionsToLoad); @@ -760,7 +774,7 @@ public class ExtensionManager { return true; } - public void unloadExtension(String extensionName) { + public void unloadExtension(@NotNull String extensionName) { Extension ext = extensions.get(extensionName.toLowerCase()); if (ext == null) { From 1b6bf4f1733feff33f4c25bd227b0d2ca86068ec Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 15:53:02 -0400 Subject: [PATCH 044/364] Comment fixes --- .../net/minestom/server/extensions/ExtensionManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/ExtensionManager.java b/src/main/java/net/minestom/server/extensions/ExtensionManager.java index 133dae805..74295d536 100644 --- a/src/main/java/net/minestom/server/extensions/ExtensionManager.java +++ b/src/main/java/net/minestom/server/extensions/ExtensionManager.java @@ -38,7 +38,7 @@ public class ExtensionManager { public final static String INDEV_RESOURCES_FOLDER = "minestom.extension.indevfolder.resources"; private final static Gson GSON = new Gson(); - // Not too much concurrency is done through extension managers. + // LinkedHashMaps are HashMaps that preserve order private final Map extensions = new LinkedHashMap<>(); private final Map immutableExtensions = Collections.unmodifiableMap(extensions); @@ -198,7 +198,7 @@ public class ExtensionManager { */ @Nullable private Extension loadExtension(@NotNull DiscoveredExtension discoveredExtension) { - // Create ExtensionDescription (authors, version etc.) + // Create Extension (authors, version etc.) String extensionName = discoveredExtension.getName(); String mainClass = discoveredExtension.getEntrypoint(); @@ -287,7 +287,7 @@ public class ExtensionManager { } } - // add to a linked hash map, as lists preserve order + // add to a linked hash map, as they preserve order extensions.put(extensionName.toLowerCase(), extension); return extension; From c60b92394cbc8d84ec8189063618ba31743fcb1d Mon Sep 17 00:00:00 2001 From: LeoDog896 Date: Wed, 24 Mar 2021 15:55:48 -0400 Subject: [PATCH 045/364] jglr's notes --- .../net/minestom/server/extensions/DiscoveredExtension.java | 4 ++-- .../extras/selfmodification/MinestomRootClassLoader.java | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java index 63dcaa9b6..c4da772d4 100644 --- a/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java +++ b/src/main/java/net/minestom/server/extensions/DiscoveredExtension.java @@ -120,13 +120,13 @@ public final class DiscoveredExtension { return originalJar; } - public MinestomExtensionClassLoader removeMinestomExtensionClassLoader() { + MinestomExtensionClassLoader removeMinestomExtensionClassLoader() { MinestomExtensionClassLoader oldClassLoader = getMinestomExtensionClassLoader(); setMinestomExtensionClassLoader(null); return oldClassLoader; } - public void setMinestomExtensionClassLoader(@Nullable MinestomExtensionClassLoader minestomExtensionClassLoader) { + void setMinestomExtensionClassLoader(@Nullable MinestomExtensionClassLoader minestomExtensionClassLoader) { this.minestomExtensionClassLoader = minestomExtensionClassLoader; } diff --git a/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java b/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java index 0c0517e07..98c1d56e8 100644 --- a/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java +++ b/src/main/java/net/minestom/server/extras/selfmodification/MinestomRootClassLoader.java @@ -296,7 +296,11 @@ public class MinestomRootClassLoader extends HierarchyClassLoader { } return true; } catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { - MinecraftServer.getExceptionManager().handleException(e); + if (MinecraftServer.getExceptionManager() != null) { + MinecraftServer.getExceptionManager().handleException(e); + } else { + e.printStackTrace(); + } } return false; } From 0f5bb0e4f2858fa8fbcd3745376367bb11b4899f Mon Sep 17 00:00:00 2001 From: Eoghanmc22 Date: Thu, 25 Mar 2021 10:44:02 -0400 Subject: [PATCH 046/364] Changes to networking --- prismarine-minecraft-data | 2 +- .../network/netty/codec/PacketCompressor.java | 4 +-- .../network/player/NettyPlayerConnection.java | 9 ++++-- .../minestom/server/utils/PacketUtils.java | 29 ++++++++++--------- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/prismarine-minecraft-data b/prismarine-minecraft-data index 424c41d75..2b25cb297 160000 --- a/prismarine-minecraft-data +++ b/prismarine-minecraft-data @@ -1 +1 @@ -Subproject commit 424c41d75e375c749640d608b9a3ae3c9592a25e +Subproject commit 2b25cb297bdad56a0673ab54f2a631a9808ea3b6 diff --git a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java b/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java index ebee11ebc..9dcc77125 100644 --- a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java +++ b/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java @@ -34,8 +34,6 @@ public class PacketCompressor extends ByteToMessageCodec { private final int threshold; - private final byte[] buffer = new byte[8192]; - private final Deflater deflater = new Deflater(3); private final Inflater inflater = new Inflater(); @@ -45,7 +43,7 @@ public class PacketCompressor extends ByteToMessageCodec { @Override protected void encode(ChannelHandlerContext ctx, ByteBuf from, ByteBuf to) { - PacketUtils.compressBuffer(deflater, buffer, from, to); + PacketUtils.compressBuffer(deflater, from, to); } @Override diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 5347fe5e5..053fe23a0 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -143,9 +143,13 @@ public class NettyPlayerConnection extends PlayerConnection { timestamp > timedBuffer.getTimestamp(); if (shouldUpdate) { - final ByteBuf buffer = PacketUtils.createFramedPacket(serverPacket, false); + final ByteBuf buffer = PacketUtils.createFramedPacket(serverPacket, true); + TimedBuffer oldBuffer = timedBuffer; timedBuffer = new TimedBuffer(buffer, timestamp); temporaryCache.cache(identifier, timedBuffer); + if (oldBuffer != null) { + oldBuffer.getBuffer().release(); + } } write(new FramedPacket(timedBuffer.getBuffer())); @@ -172,8 +176,9 @@ public class NettyPlayerConnection extends PlayerConnection { } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; synchronized (tickBuffer) { - final ByteBuf framedPacket = PacketUtils.createFramedPacket(serverPacket, false); + final ByteBuf framedPacket = PacketUtils.createFramedPacket(serverPacket, true); tickBuffer.writeBytes(framedPacket); + framedPacket.release(); } return; } else if (message instanceof ByteBuf) { diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 3fa5eeb07..97ef80ac3 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -158,29 +158,31 @@ public final class PacketUtils { * @param packetBuffer the buffer containing all the packet fields * @param compressionTarget the buffer which will receive the compressed version of {@code packetBuffer} */ - public static void compressBuffer(@NotNull Deflater deflater, @Nullable byte[] buffer, - @NotNull ByteBuf packetBuffer, @NotNull ByteBuf compressionTarget) { + public static void compressBuffer(@NotNull Deflater deflater, @NotNull ByteBuf packetBuffer, @NotNull ByteBuf compressionTarget) { final int packetLength = packetBuffer.readableBytes(); final boolean compression = packetLength > MinecraftServer.getCompressionThreshold(); Utils.writeVarIntBuf(compressionTarget, compression ? packetLength : 0); if (compression) { - compress(deflater, buffer, packetBuffer, compressionTarget); + compress(deflater, packetBuffer, compressionTarget); } else { compressionTarget.writeBytes(packetBuffer); } } - private static void compress(@NotNull Deflater deflater, @Nullable byte[] buffer, - @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) { - // Allocate buffer if not already - byte[] output = buffer != null ? buffer : new byte[8192]; + private static void compress(@NotNull Deflater deflater, @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) { deflater.setInput(uncompressed.nioBuffer()); deflater.finish(); while (!deflater.finished()) { - final int length = deflater.deflate(output); - compressed.writeBytes(output, 0, length); + compressed.writerIndex( + deflater.deflate(compressed.nioBuffer(compressed.writerIndex(), compressed.writableBytes())) + + compressed.writerIndex() + ); + + if (compressed.writableBytes() == 0) { + compressed.ensureWritable(8192); + } } deflater.reset(); @@ -197,7 +199,7 @@ public final class PacketUtils { */ @NotNull public static ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer) { - ByteBuf packetBuf = Unpooled.buffer(); + ByteBuf packetBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); writePacket(packetBuf, serverPacket); final int dataLength = packetBuf.readableBytes(); @@ -211,18 +213,19 @@ public final class PacketUtils { ByteBuf compressedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); Utils.writeVarIntBuf(compressedBuf, dataLength); final Deflater deflater = DEFLATER.get(); - compress(deflater, null, packetBuf, compressedBuf); + compress(deflater, packetBuf, compressedBuf); + packetBuf.release(); packetBuf = compressedBuf; } else { // Packet too small - ByteBuf uncompressedLengthBuffer = Unpooled.buffer(); + ByteBuf uncompressedLengthBuffer = directBuffer ? BufUtils.getBuffer(true, 1) : Unpooled.buffer(); Utils.writeVarIntBuf(uncompressedLengthBuffer, 0); packetBuf = Unpooled.wrappedBuffer(uncompressedLengthBuffer, packetBuf); } } // Write the final length of the packet - ByteBuf packetLengthBuffer = Unpooled.buffer(); + ByteBuf packetLengthBuffer = directBuffer ? BufUtils.getBuffer(true, 5) : Unpooled.buffer(); Utils.writeVarIntBuf(packetLengthBuffer, packetBuf.readableBytes()); return Unpooled.wrappedBuffer(packetLengthBuffer, packetBuf); From b3655ad82d7411fbfa659947e3896fd02c3609e2 Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 25 Mar 2021 16:05:10 +0100 Subject: [PATCH 047/364] Small style cleanup --- .../java/net/minestom/server/utils/PacketUtils.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 97ef80ac3..d8af05b94 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -14,6 +14,7 @@ import net.minestom.server.utils.callback.validator.PlayerValidator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.nio.ByteBuffer; import java.util.Collection; import java.util.zip.Deflater; @@ -153,8 +154,6 @@ public final class PacketUtils { * {@code packetBuffer} needs to be the packet content without any header (if you want to use it to write a Minecraft packet). * * @param deflater the deflater for zlib compression - * @param buffer a cached buffer which will be used to store temporary the deflater output, - * null if you prefer the buffer to be allocated dynamically when required * @param packetBuffer the buffer containing all the packet fields * @param compressionTarget the buffer which will receive the compressed version of {@code packetBuffer} */ @@ -170,15 +169,12 @@ public final class PacketUtils { } private static void compress(@NotNull Deflater deflater, @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) { - deflater.setInput(uncompressed.nioBuffer()); deflater.finish(); while (!deflater.finished()) { - compressed.writerIndex( - deflater.deflate(compressed.nioBuffer(compressed.writerIndex(), compressed.writableBytes())) - + compressed.writerIndex() - ); + ByteBuffer nioBuffer = compressed.nioBuffer(compressed.writerIndex(), compressed.writableBytes()); + compressed.writerIndex(deflater.deflate(nioBuffer) + compressed.writerIndex()); if (compressed.writableBytes() == 0) { compressed.ensureWritable(8192); From 622f632989fbe52cb8c5cd20b9990bdf92a8cdaf Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 25 Mar 2021 16:51:36 +0100 Subject: [PATCH 048/364] Use heap buffer for short lived packets --- .../minestom/server/network/player/NettyPlayerConnection.java | 2 +- src/main/java/net/minestom/server/utils/PacketUtils.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 053fe23a0..7581d3160 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -176,7 +176,7 @@ public class NettyPlayerConnection extends PlayerConnection { } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; synchronized (tickBuffer) { - final ByteBuf framedPacket = PacketUtils.createFramedPacket(serverPacket, true); + final ByteBuf framedPacket = PacketUtils.createFramedPacket(serverPacket, false); tickBuffer.writeBytes(framedPacket); framedPacket.release(); } diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index d8af05b94..f9c839bed 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -214,14 +214,14 @@ public final class PacketUtils { packetBuf = compressedBuf; } else { // Packet too small - ByteBuf uncompressedLengthBuffer = directBuffer ? BufUtils.getBuffer(true, 1) : Unpooled.buffer(); + ByteBuf uncompressedLengthBuffer = Unpooled.buffer(); Utils.writeVarIntBuf(uncompressedLengthBuffer, 0); packetBuf = Unpooled.wrappedBuffer(uncompressedLengthBuffer, packetBuf); } } // Write the final length of the packet - ByteBuf packetLengthBuffer = directBuffer ? BufUtils.getBuffer(true, 5) : Unpooled.buffer(); + ByteBuf packetLengthBuffer = Unpooled.buffer(); Utils.writeVarIntBuf(packetLengthBuffer, packetBuf.readableBytes()); return Unpooled.wrappedBuffer(packetLengthBuffer, packetBuf); From 845f41b88acaf06e02ffa830ed050cbfbd4c513e Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 25 Mar 2021 17:07:05 +0000 Subject: [PATCH 049/364] Remove cast and instanceof check from Viewable#sendPacketToViewersAndSelf --- src/main/java/net/minestom/server/Viewable.java | 3 --- src/main/java/net/minestom/server/entity/Player.java | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/Viewable.java b/src/main/java/net/minestom/server/Viewable.java index 1a3febf3d..8dd6f1f79 100644 --- a/src/main/java/net/minestom/server/Viewable.java +++ b/src/main/java/net/minestom/server/Viewable.java @@ -80,9 +80,6 @@ public interface Viewable { * @param packet the packet to send */ default void sendPacketToViewersAndSelf(@NotNull ServerPacket packet) { - if (this instanceof Player) { - ((Player) this).getPlayerConnection().sendPacket(packet); - } sendPacketToViewers(packet); } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 7d833c86a..861f1f2f4 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -654,6 +654,12 @@ public class Player extends LivingEntity implements CommandSender { return result; } + @Override + public void sendPacketToViewersAndSelf(@NotNull ServerPacket packet) { + this.playerConnection.sendPacket(packet); + super.sendPacketToViewersAndSelf(packet); + } + /** * Changes the player instance and load surrounding chunks if needed. *

From eb4a6baf8e72bc2820f215a10afffacd7ff1001a Mon Sep 17 00:00:00 2001 From: themode Date: Thu, 25 Mar 2021 21:36:18 +0100 Subject: [PATCH 050/364] Use a thread safe Set for EntityCreature ai groups --- .../java/net/minestom/server/entity/EntityCreature.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/EntityCreature.java b/src/main/java/net/minestom/server/entity/EntityCreature.java index df8b3bb33..77de37bdf 100644 --- a/src/main/java/net/minestom/server/entity/EntityCreature.java +++ b/src/main/java/net/minestom/server/entity/EntityCreature.java @@ -14,15 +14,15 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.CopyOnWriteArraySet; public class EntityCreature extends LivingEntity implements NavigableEntity, EntityAI { private int removalAnimationDelay = 1000; - private final Set aiGroups = new HashSet<>(); + private final Set aiGroups = new CopyOnWriteArraySet<>(); private final Navigator navigator = new Navigator(this); @@ -108,7 +108,7 @@ public class EntityCreature extends LivingEntity implements NavigableEntity, Ent @Override public Collection getAIGroups() { - return this.aiGroups; + return aiGroups; } /** From e9d76cc742333a62d9c0360086691d847ea2b648 Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 08:37:47 +0100 Subject: [PATCH 051/364] Flush in netty event loop --- .../server/network/player/NettyPlayerConnection.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 7581d3160..e3c0d28d2 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -71,10 +71,12 @@ public class NettyPlayerConnection extends PlayerConnection { @Override public void update() { // Flush - if (channel.isActive()) { - writeWaitingPackets(); - this.channel.flush(); - } + this.channel.eventLoop().submit(() -> { + if (channel.isActive()) { + writeWaitingPackets(); + this.channel.flush(); + } + }); // Network stats super.update(); } From c6c720e0c714d57b935b19ea83194f7bacecfc95 Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 08:49:27 +0100 Subject: [PATCH 052/364] Use concurrent map instead of copyonwrite set for player's viewable entities --- src/main/java/net/minestom/server/entity/Player.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 050d5eeb7..eb045d395 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -64,6 +64,7 @@ import org.jetbrains.annotations.Nullable; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; @@ -81,7 +82,7 @@ public class Player extends LivingEntity implements CommandSender { private String username; protected final PlayerConnection playerConnection; // All the entities that this player can see - protected final Set viewableEntities = new CopyOnWriteArraySet<>(); + protected final Set viewableEntities = ConcurrentHashMap.newKeySet(); private int latency; private JsonMessage displayName; From 0eea491f9d1d16be62b194b67a94ad9759992494 Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 08:52:16 +0100 Subject: [PATCH 053/364] Fix intellij warning --- src/main/java/net/minestom/server/utils/entity/EntityUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/utils/entity/EntityUtils.java b/src/main/java/net/minestom/server/utils/entity/EntityUtils.java index c8fd52ad4..879164a3b 100644 --- a/src/main/java/net/minestom/server/utils/entity/EntityUtils.java +++ b/src/main/java/net/minestom/server/utils/entity/EntityUtils.java @@ -29,7 +29,7 @@ public final class EntityUtils { final Chunk chunk = instance.getChunk(chunkX, chunkZ); if (chunk == null) continue; - instance.getChunkEntities(chunk).forEach(consumer::accept); + instance.getChunkEntities(chunk).forEach(consumer); } } From c92a7873dbfdf2f3be82056cc5022e3354393468 Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 09:04:28 +0100 Subject: [PATCH 054/364] Only send velocity packet in #addViewer if required --- src/main/java/net/minestom/server/entity/Entity.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 80f6de3ce..a5d823678 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -345,7 +345,9 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission PlayerConnection playerConnection = player.getPlayerConnection(); playerConnection.sendPacket(getEntityType().getSpawnType().getSpawnPacket(this)); - playerConnection.sendPacket(getVelocityPacket()); + if (hasVelocity()) { + playerConnection.sendPacket(getVelocityPacket()); + } playerConnection.sendPacket(getMetadataPacket()); // Passenger From 1ceffd854026dd3d0323891524855f0870cad77d Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 11:09:30 +0100 Subject: [PATCH 055/364] Do not write empty buffer --- .../network/player/NettyPlayerConnection.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index e3c0d28d2..ebe1d9981 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -71,12 +71,15 @@ public class NettyPlayerConnection extends PlayerConnection { @Override public void update() { // Flush - this.channel.eventLoop().submit(() -> { - if (channel.isActive()) { - writeWaitingPackets(); - this.channel.flush(); - } - }); + final int bufferSize = tickBuffer.writerIndex(); + if (bufferSize > 0) { + this.channel.eventLoop().submit(() -> { + if (channel.isActive()) { + writeWaitingPackets(); + channel.flush(); + } + }); + } // Network stats super.update(); } @@ -208,6 +211,11 @@ public class NettyPlayerConnection extends PlayerConnection { private void writeWaitingPackets() { synchronized (tickBuffer) { final ByteBuf copy = tickBuffer.copy(); + if (copy.writerIndex() == 0) { + // Nothing to write + copy.release(); + return; + } ChannelFuture channelFuture = channel.write(new FramedPacket(copy)); channelFuture.addListener(future -> copy.release()); From 71f2e885a43ca6d64c6c70f400312d8e0dabff34 Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 11:24:09 +0100 Subject: [PATCH 056/364] Remove unused field --- src/main/java/net/minestom/server/entity/Player.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index c2dbcbd63..006be9c9a 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -134,7 +134,6 @@ public class Player extends LivingEntity implements CommandSender { private final Set targetBreakers = Collections.singleton(this); // Position synchronization with viewers - private long lastPlayerSynchronizationTime; private double lastPlayerSyncX, lastPlayerSyncY, lastPlayerSyncZ; private float lastPlayerSyncYaw, lastPlayerSyncPitch; @@ -407,8 +406,6 @@ public class Player extends LivingEntity implements CommandSender { // Multiplayer sync if (!viewers.isEmpty()) { - this.lastPlayerSynchronizationTime = time; - final boolean positionChanged = position.getX() != lastPlayerSyncX || position.getY() != lastPlayerSyncY || position.getZ() != lastPlayerSyncZ; From 6db1c34c50da3d729cd839bcfbcbc00212cea26c Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 11:25:03 +0100 Subject: [PATCH 057/364] Remove mention of deprecated class --- src/main/java/net/minestom/server/entity/Entity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index a5d823678..a80c8d448 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -50,7 +50,7 @@ import java.util.function.Consumer; /** * Could be a player, a monster, or an object. *

- * To create your own entity you probably want to extends {@link ObjectEntity} or {@link EntityCreature} instead. + * To create your own entity you probably want to extends {@link LivingEntity} or {@link EntityCreature} instead. */ public class Entity implements Viewable, EventHandler, DataContainer, PermissionHandler { From 9c8c8c8bf8403b22cc96af735cf295697eb60b2c Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 13:08:05 +0100 Subject: [PATCH 058/364] WIP packet allocation improvement --- .../network/player/NettyPlayerConnection.java | 3 +- .../minestom/server/utils/PacketUtils.java | 79 +++++++++++++------ src/test/java/demo/Main.java | 2 + 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index ebe1d9981..9a6e92af6 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -181,6 +181,7 @@ public class NettyPlayerConnection extends PlayerConnection { } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; synchronized (tickBuffer) { + //PacketUtils.writeFramedPacket(tickBuffer, serverPacket, false); final ByteBuf framedPacket = PacketUtils.createFramedPacket(serverPacket, false); tickBuffer.writeBytes(framedPacket); framedPacket.release(); @@ -188,7 +189,7 @@ public class NettyPlayerConnection extends PlayerConnection { return; } else if (message instanceof ByteBuf) { synchronized (tickBuffer) { - tickBuffer.writeBytes((ByteBuf) message); + //tickBuffer.writeBytes((ByteBuf) message); } return; } diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index f9c839bed..f9a282132 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -7,14 +7,17 @@ import net.minestom.server.entity.Player; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.network.netty.packet.FramedPacket; import net.minestom.server.network.packet.server.ServerPacket; +import net.minestom.server.network.packet.server.play.ChunkDataPacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.callback.validator.PlayerValidator; +import net.minestom.server.utils.debug.DebugUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Collection; import java.util.zip.Deflater; @@ -184,21 +187,12 @@ public final class PacketUtils { deflater.reset(); } - /** - * Creates a "framed packet" (packet which can be send and understood by a Minecraft client) - * from a server packet, directly into an output buffer. - *

- * Can be used if you want to store a raw buffer and send it later without the additional writing cost. - * Compression is applied if {@link MinecraftServer#getCompressionThreshold()} is greater than 0. - * - * @param serverPacket the server packet to write - */ - @NotNull - public static ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer) { - ByteBuf packetBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); - writePacket(packetBuf, serverPacket); + public static void writeFramedPacket(@NotNull ByteBuf buffer, + @NotNull ServerPacket serverPacket, boolean directBuffer) { + ByteBuf packetBuf = null;//directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); + //writePacket(packetBuf, serverPacket); - final int dataLength = packetBuf.readableBytes(); + final int dataLength = 0;//packetBuf.readableBytes(); final int compressionThreshold = MinecraftServer.getCompressionThreshold(); final boolean compression = compressionThreshold > 0; @@ -212,19 +206,60 @@ public final class PacketUtils { compress(deflater, packetBuf, compressedBuf); packetBuf.release(); packetBuf = compressedBuf; + + // Write the final length of the packet + Utils.writeVarIntBuf(buffer, packetBuf.readableBytes()); + buffer.writeBytes(packetBuf); } else { // Packet too small - ByteBuf uncompressedLengthBuffer = Unpooled.buffer(); - Utils.writeVarIntBuf(uncompressedLengthBuffer, 0); - packetBuf = Unpooled.wrappedBuffer(uncompressedLengthBuffer, packetBuf); + Utils.writeVarIntBuf(buffer, packetBuf.readableBytes()+1); // +1 for the 0 varint + Utils.writeVarIntBuf(buffer, 0); + buffer.writeBytes(packetBuf); } + }else{ + // No compression + final int hardcodedSize = 3; + final int index = buffer.writerIndex(); + for(int i=0;i + * Can be used if you want to store a raw buffer and send it later without the additional writing cost. + * Compression is applied if {@link MinecraftServer#getCompressionThreshold()} is greater than 0. + * + * @param serverPacket the server packet to write + */ + @NotNull + public static ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer) { + ByteBuf packetBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); + writeFramedPacket(packetBuf, serverPacket, directBuffer); + return packetBuf; } } diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java index 149bf959a..eaa882cac 100644 --- a/src/test/java/demo/Main.java +++ b/src/test/java/demo/Main.java @@ -11,6 +11,7 @@ import net.minestom.server.instance.block.BlockManager; import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule; import net.minestom.server.storage.StorageManager; import net.minestom.server.storage.systems.FileStorageSystem; +import net.minestom.server.utils.Utils; import net.minestom.server.utils.time.TimeUnit; import net.minestom.server.utils.time.UpdateOption; @@ -21,6 +22,7 @@ public class Main { MinecraftServer minecraftServer = MinecraftServer.init(); // MinecraftServer.setShouldProcessNettyErrors(true); + MinecraftServer.setCompressionThreshold(0); BlockManager blockManager = MinecraftServer.getBlockManager(); blockManager.registerCustomBlock(new CustomBlockSample()); From cef7e3d5914dc17ea99492e579e4b77f6078e15b Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 15:58:46 +0100 Subject: [PATCH 059/364] Optimize packet writing & reduce allocation --- .../network/player/NettyPlayerConnection.java | 7 +- .../minestom/server/utils/PacketUtils.java | 89 +++++++++---------- .../java/net/minestom/server/utils/Utils.java | 29 ++++++ 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 9a6e92af6..2e62cc817 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -181,15 +181,12 @@ public class NettyPlayerConnection extends PlayerConnection { } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; synchronized (tickBuffer) { - //PacketUtils.writeFramedPacket(tickBuffer, serverPacket, false); - final ByteBuf framedPacket = PacketUtils.createFramedPacket(serverPacket, false); - tickBuffer.writeBytes(framedPacket); - framedPacket.release(); + PacketUtils.writeFramedPacket(tickBuffer, serverPacket, false); } return; } else if (message instanceof ByteBuf) { synchronized (tickBuffer) { - //tickBuffer.writeBytes((ByteBuf) message); + tickBuffer.writeBytes((ByteBuf) message); } return; } diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index f9a282132..ffcbffddf 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -7,17 +7,14 @@ import net.minestom.server.entity.Player; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.network.netty.packet.FramedPacket; import net.minestom.server.network.packet.server.ServerPacket; -import net.minestom.server.network.packet.server.play.ChunkDataPacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.callback.validator.PlayerValidator; -import net.minestom.server.utils.debug.DebugUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.ByteBuffer; -import java.util.Arrays; import java.util.Collection; import java.util.zip.Deflater; @@ -188,61 +185,59 @@ public final class PacketUtils { } public static void writeFramedPacket(@NotNull ByteBuf buffer, - @NotNull ServerPacket serverPacket, boolean directBuffer) { - ByteBuf packetBuf = null;//directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); - //writePacket(packetBuf, serverPacket); - - final int dataLength = 0;//packetBuf.readableBytes(); - + @NotNull ServerPacket serverPacket, boolean directBuffer) { final int compressionThreshold = MinecraftServer.getCompressionThreshold(); final boolean compression = compressionThreshold > 0; + final int hardcodedVarIntSize = 3; + if (compression) { - if (dataLength >= compressionThreshold) { - // Packet large enough - ByteBuf compressedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); - Utils.writeVarIntBuf(compressedBuf, dataLength); - final Deflater deflater = DEFLATER.get(); - compress(deflater, packetBuf, compressedBuf); - packetBuf.release(); - packetBuf = compressedBuf; - - // Write the final length of the packet - Utils.writeVarIntBuf(buffer, packetBuf.readableBytes()); - buffer.writeBytes(packetBuf); - } else { - // Packet too small - Utils.writeVarIntBuf(buffer, packetBuf.readableBytes()+1); // +1 for the 0 varint - Utils.writeVarIntBuf(buffer, 0); - buffer.writeBytes(packetBuf); - } - }else{ - // No compression - final int hardcodedSize = 3; - final int index = buffer.writerIndex(); - for(int i=0;i= compressionThreshold) { + // Packet large enough + + // Compress id + payload + ByteBuf compressedBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer(); + final Deflater deflater = DEFLATER.get(); + compress(deflater, buffer.slice(contentIndex, packetSize), compressedBuf); + + final int totalPacketLength = compressedBuf.readableBytes() + hardcodedVarIntSize; + + // Replace uncompressed by compressed data + buffer.setBytes(contentIndex, compressedBuf); + buffer.writerIndex(contentIndex + compressedBuf.writerIndex()); + compressedBuf.release(); + + Utils.overrideVarInt(buffer, packetLengthIndex, hardcodedVarIntSize, totalPacketLength); + Utils.overrideVarInt(buffer, dataLengthIndex, hardcodedVarIntSize, packetSize); + } else { + // Packet too small, just override header values + final int totalPacketLength = packetSize + hardcodedVarIntSize; + Utils.overrideVarInt(buffer, packetLengthIndex, hardcodedVarIntSize, totalPacketLength); + Utils.overrideVarInt(buffer, dataLengthIndex, hardcodedVarIntSize, 0); // -> Uncompressed } + } else { + // No compression - if(index == 0){ - //System.out.println("test "+ Utils.readVarInt(buffer)+" "+packetSize); - } + // Write dummy varint + final int index = Utils.writeEmptyVarInt(buffer, hardcodedVarIntSize); - //System.out.println("test "+packetSize+" "+serverPacket.getClass().getSimpleName()+" "+varintSize); - //Utils.writeVarIntBuf(buffer, packetBuf.readableBytes()); - //buffer.writeBytes(packetBuf); + // Write packet id + payload + writePacket(buffer, serverPacket); + + // Rewrite dummy varint to packet length + final int afterIndex = buffer.writerIndex(); + final int packetSize = (afterIndex - index) - hardcodedVarIntSize; + Utils.overrideVarInt(buffer, index, hardcodedVarIntSize, packetSize); } } diff --git a/src/main/java/net/minestom/server/utils/Utils.java b/src/main/java/net/minestom/server/utils/Utils.java index ca249e8fc..aef87a28b 100644 --- a/src/main/java/net/minestom/server/utils/Utils.java +++ b/src/main/java/net/minestom/server/utils/Utils.java @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.shorts.Short2ShortLinkedOpenHashMap; import net.minestom.server.instance.palette.Section; import net.minestom.server.utils.binary.BinaryWriter; +import org.jetbrains.annotations.NotNull; import java.util.UUID; @@ -43,6 +44,34 @@ public final class Utils { } while (value != 0); } + public static void overrideVarInt(@NotNull ByteBuf buffer, int startIndex, int length, int value) { + final int indexCache = buffer.writerIndex(); + buffer.writerIndex(startIndex); + writeVarIntBuf(buffer, value); + + final int loopEnd = startIndex + length; + for (int i = startIndex; i < loopEnd; i++) { + byte b = buffer.getByte(i); + boolean last = i == loopEnd - 1; + if (last) { + b &= ~(1 << 7); + } else { + b |= 1 << 7; + } + buffer.setByte(i, b); + } + + buffer.writerIndex(indexCache); + } + + public static int writeEmptyVarInt(@NotNull ByteBuf buffer, int length) { + final int index = buffer.writerIndex(); + for (int i = 0; i < length; i++) { + buffer.writeByte(0); + } + return index; + } + public static int readVarInt(ByteBuf buffer) { int numRead = 0; int result = 0; From 0d7d7ef4532edbe120553311377b545fd9d7ad57 Mon Sep 17 00:00:00 2001 From: themode Date: Fri, 26 Mar 2021 16:43:25 +0100 Subject: [PATCH 060/364] Cleanup comment --- src/main/java/net/minestom/server/utils/PacketUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index ffcbffddf..bdf4a9612 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -192,6 +192,7 @@ public final class PacketUtils { final int hardcodedVarIntSize = 3; if (compression) { + // Dummy varint final int packetLengthIndex = Utils.writeEmptyVarInt(buffer, hardcodedVarIntSize); final int dataLengthIndex = Utils.writeEmptyVarInt(buffer, hardcodedVarIntSize); @@ -199,7 +200,6 @@ public final class PacketUtils { final int contentIndex = buffer.writerIndex(); writePacket(buffer, serverPacket); final int afterIndex = buffer.writerIndex(); - //System.out.println("test "+contentIndex+" "+afterIndex); final int packetSize = (afterIndex - dataLengthIndex) - hardcodedVarIntSize; if (packetSize >= compressionThreshold) { @@ -217,6 +217,7 @@ public final class PacketUtils { buffer.writerIndex(contentIndex + compressedBuf.writerIndex()); compressedBuf.release(); + // Update header values Utils.overrideVarInt(buffer, packetLengthIndex, hardcodedVarIntSize, totalPacketLength); Utils.overrideVarInt(buffer, dataLengthIndex, hardcodedVarIntSize, packetSize); } else { From 4f0944ba9fd1d2513fda93fd6f10d7b5d5e1d33a Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 1 Mar 2021 15:25:23 +0000 Subject: [PATCH 061/364] Initial adventure implementation - deprecate old text classes - make CommandSender and Audience - implement in ConsoleSender - partially implement in Player --- build.gradle | 7 + .../minestom/server/chat/ChatClickEvent.java | 1 + .../net/minestom/server/chat/ChatColor.java | 1 + .../minestom/server/chat/ChatHoverEvent.java | 1 + .../net/minestom/server/chat/ChatParser.java | 1 + .../net/minestom/server/chat/ColoredText.java | 1 + .../net/minestom/server/chat/JsonMessage.java | 12 ++ .../net/minestom/server/chat/RichMessage.java | 1 + .../server/chat/TranslatableText.java | 1 + .../server/command/CommandSender.java | 19 +- .../server/command/ConsoleSender.java | 10 + .../net/minestom/server/entity/Player.java | 171 +++++++++++++----- .../packet/server/play/ChatMessagePacket.java | 74 +++++++- .../packet/server/play/DisconnectPacket.java | 18 +- .../play/PlayerListHeaderAndFooterPacket.java | 27 ++- .../packet/server/play/TitlePacket.java | 81 +++++++-- .../net/minestom/server/utils/TickUtils.java | 25 +++ 17 files changed, 357 insertions(+), 94 deletions(-) create mode 100644 src/main/java/net/minestom/server/utils/TickUtils.java 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); + } +} From cfa9cffe8728c4252fd8e4f1112f0e004131d592 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 1 Mar 2021 15:58:00 +0000 Subject: [PATCH 062/364] Clean up non-Player CommandSender implementations --- .../net/minestom/server/command/CommandSender.java | 2 +- .../net/minestom/server/command/ConsoleSender.java | 5 ----- .../net/minestom/server/command/ServerSender.java | 12 +++++------- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/minestom/server/command/CommandSender.java b/src/main/java/net/minestom/server/command/CommandSender.java index a32561fde..bd0e44727 100644 --- a/src/main/java/net/minestom/server/command/CommandSender.java +++ b/src/main/java/net/minestom/server/command/CommandSender.java @@ -22,7 +22,7 @@ public interface CommandSender extends PermissionHandler, Audience { * @deprecated Use {@link #sendMessage(Component)} */ @Deprecated - void sendMessage(@NotNull String message); + default void sendMessage(@NotNull String message) { this.sendMessage(Component.text(message)); } /** * Sends multiple raw string messages. diff --git a/src/main/java/net/minestom/server/command/ConsoleSender.java b/src/main/java/net/minestom/server/command/ConsoleSender.java index da3480225..36b3ad6cc 100644 --- a/src/main/java/net/minestom/server/command/ConsoleSender.java +++ b/src/main/java/net/minestom/server/command/ConsoleSender.java @@ -27,11 +27,6 @@ public class ConsoleSender implements CommandSender { LOGGER.info(PlainComponentSerializer.plain().serialize(message)); } - @Override - public void sendMessage(@NotNull String message) { - LOGGER.info(message); - } - @NotNull @Override public Set getAllPermissions() { diff --git a/src/main/java/net/minestom/server/command/ServerSender.java b/src/main/java/net/minestom/server/command/ServerSender.java index 1f415e867..235630771 100644 --- a/src/main/java/net/minestom/server/command/ServerSender.java +++ b/src/main/java/net/minestom/server/command/ServerSender.java @@ -1,6 +1,8 @@ package net.minestom.server.command; import net.minestom.server.command.builder.CommandContext; +import net.kyori.adventure.audience.Audience; +import net.minestom.server.command.builder.Arguments; import net.minestom.server.permission.Permission; import org.jetbrains.annotations.NotNull; @@ -11,18 +13,14 @@ import java.util.Set; /** * Sender used in {@link CommandManager#executeServerCommand(String)}. *

- * Be aware that {@link #sendMessage(String)} is empty on purpose because the purpose - * of this sender is to process the data of {@link CommandContext#getReturnData()}. + * Although this class implemented {@link CommandSender} and thus {@link Audience}, no + * data can be sent to this sender because it's purpose is to process the data of + * {@link CommandContext#getReturnData()}. */ public class ServerSender implements CommandSender { private final Set permissions = Collections.unmodifiableSet(new HashSet<>()); - @Override - public void sendMessage(@NotNull String message) { - // Empty on purpose - } - @NotNull @Override public Set getAllPermissions() { From aff04c0e0dc78391ab77df408f6c6e36a92e804c Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 1 Mar 2021 17:25:55 +0000 Subject: [PATCH 063/364] Add boss bar implementations --- .../net/minestom/server/MinecraftServer.java | 13 + .../server/adventure/BossBarManager.java | 223 ++++++++++++++++++ .../net/minestom/server/bossbar/BarColor.java | 2 + .../minestom/server/bossbar/BarDivision.java | 3 + .../net/minestom/server/bossbar/BossBar.java | 13 +- .../net/minestom/server/entity/Player.java | 17 +- .../packet/server/play/BossBarPacket.java | 18 +- 7 files changed, 273 insertions(+), 16 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/BossBarManager.java diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 7c2c2cff1..24feecfd6 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -1,6 +1,7 @@ package net.minestom.server; import net.minestom.server.advancements.AdvancementManager; +import net.minestom.server.adventure.BossBarManager; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.command.CommandManager; import net.minestom.server.data.DataManager; @@ -115,6 +116,7 @@ public final class MinecraftServer { private static DimensionTypeManager dimensionTypeManager; private static BiomeManager biomeManager; private static AdvancementManager advancementManager; + private static BossBarManager bossBarManager; private static ExtensionManager extensionManager; @@ -180,6 +182,7 @@ public final class MinecraftServer { dimensionTypeManager = new DimensionTypeManager(); biomeManager = new BiomeManager(); advancementManager = new AdvancementManager(); + bossBarManager = new BossBarManager(); updateManager = new UpdateManager(); @@ -427,6 +430,16 @@ public final class MinecraftServer { return connectionManager; } + /** + * Gets the boss bar manager. + * + * @return the boss bar manager + */ + public static BossBarManager getBossBarManager() { + checkInitStatus(bossBarManager); + return bossBarManager; + } + /** * Gets the object handling the client packets processing. *

diff --git a/src/main/java/net/minestom/server/adventure/BossBarManager.java b/src/main/java/net/minestom/server/adventure/BossBarManager.java new file mode 100644 index 000000000..71c178d62 --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/BossBarManager.java @@ -0,0 +1,223 @@ +package net.minestom.server.adventure; + +import com.google.common.collect.MapMaker; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.bossbar.BossBar.*; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.minestom.server.MinecraftServer; +import net.minestom.server.entity.Player; +import net.minestom.server.event.GlobalEventHandler; +import net.minestom.server.event.player.PlayerDisconnectEvent; +import net.minestom.server.network.packet.server.play.BossBarPacket; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.*; +import java.util.function.Consumer; +import java.util.logging.Handler; + +import static net.minestom.server.network.packet.server.play.BossBarPacket.Action.*; + +/** + * Manages all boss bars known to this Minestom instance. This implementation is heavily + * based on Velocity's + * boss bar management system. + */ +public class BossBarManager implements BossBar.Listener { + private final Map bars; + + /** + * Creates a new boss bar manager. + */ + public BossBarManager() { + this.bars = new MapMaker().weakKeys().makeMap(); + + MinecraftServer.getGlobalEventHandler().addEventCallback(PlayerDisconnectEvent.class, this::onDisconnect); + } + + /** + * Adds the specified player to the boss bar's viewers and spawns the boss bar, registering the + * boss bar if needed. + * + * @param player the intended viewer + * @param bar the boss bar to show + */ + public void addBossBar(@NotNull Player player, @NotNull BossBar bar) { + Holder holder = this.getOrCreateHandler(bar); + + if (holder.players.add(player.getUuid())) { + player.getPlayerConnection().sendPacket(holder.createAddPacket()); + } + } + /** + * Removes the specified player from the boss bar's viewers and despawns the boss bar. + * + * @param player the intended viewer + * @param bar the boss bar to hide + */ + + public void removeBossBar(@NotNull Player player, @NotNull BossBar bar) { + Holder holder = this.getOrCreateHandler(bar); + + if (holder.players.remove(player.getUuid())) { + player.getPlayerConnection().sendPacket(holder.createRemovePacket()); + } + } + + @Override + public void bossBarNameChanged(@NonNull BossBar bar, @NonNull Component oldName, @NonNull Component newName) { + Holder holder = this.bars.get(bar); + this.updatePlayers(holder.createTitleUpdate(newName), holder.players); + } + + @Override + public void bossBarProgressChanged(@NonNull BossBar bar, float oldProgress, float newProgress) { + Holder holder = this.bars.get(bar); + this.updatePlayers(holder.createPercentUpdate(newProgress), holder.players); + } + + @Override + public void bossBarColorChanged(@NonNull BossBar bar, @NonNull Color oldColor, @NonNull Color newColor) { + Holder holder = this.bars.get(bar); + this.updatePlayers(holder.createColorUpdate(newColor), holder.players); + } + + @Override + public void bossBarOverlayChanged(@NonNull BossBar bar, BossBar.@NonNull Overlay oldOverlay, BossBar.@NonNull Overlay newOverlay) { + Holder holder = this.bars.get(bar); + this.updatePlayers(holder.createOverlayUpdate(newOverlay), holder.players); + } + + @Override + public void bossBarFlagsChanged(@NonNull BossBar bar, @NonNull Set flagsAdded, @NonNull Set flagsRemoved) { + Holder holder = this.bars.get(bar); + this.updatePlayers(holder.createFlagsUpdate(), holder.players); + } + + /** + * Sends the packet to all players in the set, removing them if they no longer exist + * in the connection manager. + * + * @param packet the packet + * @param players the players + */ + private void updatePlayers(BossBarPacket packet, Set players) { + Iterator iterator = players.iterator(); + + while (iterator.hasNext()) { + Player player = MinecraftServer.getConnectionManager().getPlayer(iterator.next()); + + if (player == null) { + iterator.remove(); + } else { + player.getPlayerConnection().sendPacket(packet); + } + } + } + + /** + * Gets or creates a handler for this bar. + * + * @param bar the bar + * + * @return the handler + */ + private @NotNull Holder getOrCreateHandler(@NotNull BossBar bar) { + Holder holder = this.bars.computeIfAbsent(bar, Holder::new); + + if (!holder.registered) { + bar.addListener(this); + holder.registered = true; + } + + return holder; + } + + /** + * Called when a player disconnects. This removes the player from any boss bars they + * may be subscribed to. + * + * @param event the event + */ + private void onDisconnect(@NotNull PlayerDisconnectEvent event) { + for (Holder holder : this.bars.values()) { + holder.players.remove(event.getPlayer().getUuid()); + } + } + + private static class Holder { + protected final UUID uuid; + protected final BossBar bar; + protected final Set players; + protected boolean registered; + + Holder(@NotNull BossBar bar) { + this.uuid = UUID.randomUUID(); + this.bar = bar; + this.players = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); + this.registered = false; + } + + BossBarPacket createRemovePacket() { + return this.createGenericPacket(REMOVE, packet -> {}); + } + + BossBarPacket createAddPacket() { + return this.createGenericPacket(ADD, packet -> { + packet.title = GsonComponentSerializer.gson().serialize(bar.name()); + packet.color = bar.color().ordinal(); + packet.division = bar.overlay().ordinal(); + packet.health = bar.progress(); + packet.flags = serializeFlags(bar.flags()); + }); + } + + BossBarPacket createPercentUpdate(float newPercent) { + return this.createGenericPacket(UPDATE_HEALTH, packet -> packet.health = newPercent); + } + + BossBarPacket createColorUpdate(@NotNull Color color) { + return this.createGenericPacket(UPDATE_STYLE, packet -> { + packet.color = color.ordinal(); + packet.division = bar.overlay().ordinal(); + }); + } + + BossBarPacket createTitleUpdate(@NotNull Component title) { + return this.createGenericPacket(UPDATE_TITLE, packet -> packet.title = GsonComponentSerializer.gson().serialize(title)); + } + + BossBarPacket createFlagsUpdate() { + return createFlagsUpdate(bar.flags()); + } + + BossBarPacket createFlagsUpdate(@NotNull Set newFlags) { + return this.createGenericPacket(UPDATE_FLAGS, packet -> packet.flags = serializeFlags(bar.flags())); + } + + BossBarPacket createOverlayUpdate(@NotNull Overlay overlay) { + return this.createGenericPacket(UPDATE_STYLE, packet -> { + packet.division = overlay.ordinal(); + packet.color = bar.color().ordinal(); + }); + } + + BossBarPacket createGenericPacket(@NotNull BossBarPacket.Action action, @NotNull Consumer consumer) { + BossBarPacket packet = new BossBarPacket(); + packet.uuid = this.uuid; + packet.action = action; + consumer.accept(packet); + return packet; + } + + private static byte serializeFlags(@NotNull Set flags) { + byte val = 0x0; + for (Flag flag : flags) { + val |= flag.ordinal(); + } + return val; + } + } +} diff --git a/src/main/java/net/minestom/server/bossbar/BarColor.java b/src/main/java/net/minestom/server/bossbar/BarColor.java index 79c997ae5..c9f5a7e08 100644 --- a/src/main/java/net/minestom/server/bossbar/BarColor.java +++ b/src/main/java/net/minestom/server/bossbar/BarColor.java @@ -2,7 +2,9 @@ package net.minestom.server.bossbar; /** * Represents the displayed color of a {@link BossBar}. + * @deprecated Use {@link net.kyori.adventure.bossbar.BossBar} */ +@Deprecated public enum BarColor { PINK, BLUE, diff --git a/src/main/java/net/minestom/server/bossbar/BarDivision.java b/src/main/java/net/minestom/server/bossbar/BarDivision.java index 317aed640..9b79bbe51 100644 --- a/src/main/java/net/minestom/server/bossbar/BarDivision.java +++ b/src/main/java/net/minestom/server/bossbar/BarDivision.java @@ -2,7 +2,10 @@ package net.minestom.server.bossbar; /** * Used to define the number of segments on a {@link BossBar}. + * + * @deprecated Use {@link net.kyori.adventure.bossbar.BossBar} */ +@Deprecated public enum BarDivision { SOLID, SEGMENT_6, diff --git a/src/main/java/net/minestom/server/bossbar/BossBar.java b/src/main/java/net/minestom/server/bossbar/BossBar.java index ebcd11f45..cd129d227 100644 --- a/src/main/java/net/minestom/server/bossbar/BossBar.java +++ b/src/main/java/net/minestom/server/bossbar/BossBar.java @@ -19,7 +19,10 @@ import java.util.concurrent.CopyOnWriteArraySet; * and add the {@link Player} you want using {@link #addViewer(Player)} and remove them using {@link #removeViewer(Player)}. *

* You can retrieve all the boss bars of a {@link Player} with {@link #getBossBars(Player)}. + * + * @deprecated Use {@link net.kyori.adventure.audience.Audience#showBossBar(net.kyori.adventure.bossbar.BossBar)} */ +@Deprecated public class BossBar implements Viewable { private static final int MAX_BOSSBAR = 7; @@ -249,10 +252,10 @@ public class BossBar implements Viewable { BossBarPacket bossBarPacket = new BossBarPacket(); bossBarPacket.uuid = uuid; bossBarPacket.action = BossBarPacket.Action.ADD; - bossBarPacket.title = title; + bossBarPacket.title = title.toString(); bossBarPacket.health = progress; - bossBarPacket.color = color; - bossBarPacket.division = division; + bossBarPacket.color = color.ordinal(); + bossBarPacket.division = division.ordinal(); bossBarPacket.flags = flags; player.getPlayerConnection().sendPacket(bossBarPacket); } @@ -275,7 +278,7 @@ public class BossBar implements Viewable { BossBarPacket bossBarPacket = new BossBarPacket(); bossBarPacket.uuid = uuid; bossBarPacket.action = BossBarPacket.Action.UPDATE_TITLE; - bossBarPacket.title = title; + bossBarPacket.title = title.toString(); sendPacketToViewers(bossBarPacket); } @@ -291,7 +294,7 @@ public class BossBar implements Viewable { BossBarPacket bossBarPacket = new BossBarPacket(); bossBarPacket.uuid = uuid; bossBarPacket.action = BossBarPacket.Action.UPDATE_STYLE; - bossBarPacket.color = color; + bossBarPacket.color = color.ordinal(); sendPacketToViewers(bossBarPacket); } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 4ffd7e79a..f0e0cf39d 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -2,6 +2,7 @@ package net.minestom.server.entity; import com.google.common.collect.Queues; import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.inventory.Book; import net.kyori.adventure.text.Component; @@ -12,8 +13,10 @@ import net.kyori.adventure.title.Title; import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.AdvancementTab; import net.minestom.server.attribute.Attribute; +import net.minestom.server.attribute.AttributeInstance; import net.minestom.server.bossbar.BossBar; import net.minestom.server.chat.ChatParser; +import net.minestom.server.attribute.Attributes; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; import net.minestom.server.chat.RichMessage; @@ -561,9 +564,9 @@ public class Player extends LivingEntity implements CommandSender { // Boss bars cache { - Set bossBars = BossBar.getBossBars(this); + Set bossBars = net.minestom.server.bossbar.BossBar.getBossBars(this); if (bossBars != null) { - for (BossBar bossBar : bossBars) { + for (net.minestom.server.bossbar.BossBar bossBar : bossBars) { bossBar.removeViewer(this); } } @@ -1091,6 +1094,16 @@ public class Player extends LivingEntity implements CommandSender { playerConnection.sendPacket(titlePacket); } + @Override + public void showBossBar(@NonNull BossBar bar) { + MinecraftServer.getBossBarManager().addBossBar(this, bar); + } + + @Override + public void hideBossBar(@NonNull BossBar bar) { + MinecraftServer.getBossBarManager().removeBossBar(this, bar); + } + /** * Opens a book ui for the player with the given book metadata. * diff --git a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java index cf14956a8..e733ebca3 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java @@ -15,10 +15,10 @@ public class BossBarPacket implements ServerPacket { public UUID uuid; public Action action; - public JsonMessage title; // Only text + public String title; public float health; - public BarColor color; - public BarDivision division; + public int color; + public int division; public byte flags; @@ -29,10 +29,10 @@ public class BossBarPacket implements ServerPacket { switch (action) { case ADD: - writer.writeSizedString(title.toString()); + writer.writeSizedString(title); writer.writeFloat(health); - writer.writeVarInt(color.ordinal()); - writer.writeVarInt(division.ordinal()); + writer.writeVarInt(color); + writer.writeVarInt(division); writer.writeByte(flags); break; case REMOVE: @@ -42,11 +42,11 @@ public class BossBarPacket implements ServerPacket { writer.writeFloat(health); break; case UPDATE_TITLE: - writer.writeSizedString(title.toString()); + writer.writeSizedString(title); break; case UPDATE_STYLE: - writer.writeVarInt(color.ordinal()); - writer.writeVarInt(division.ordinal()); + writer.writeVarInt(color); + writer.writeVarInt(division); break; case UPDATE_FLAGS: writer.writeByte(flags); From f9ab109eb06d0bd9b35e19d3dc6449ac69d59d6c Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 1 Mar 2021 17:27:10 +0000 Subject: [PATCH 064/364] Fix player title overrides --- src/main/java/net/minestom/server/entity/Player.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index f0e0cf39d..5e9ecd4d6 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1078,11 +1078,7 @@ public class Player extends LivingEntity implements CommandSender { 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. - */ + @Override public void resetTitle() { TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.RESET); playerConnection.sendPacket(titlePacket); From d4873e5e024be175234063491545f91f3452609b Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 1 Mar 2021 17:37:51 +0000 Subject: [PATCH 065/364] Make Team implement ForwardingAudience --- .../net/minestom/server/scoreboard/Team.java | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/scoreboard/Team.java b/src/main/java/net/minestom/server/scoreboard/Team.java index e7bea2a55..a2fbc7195 100644 --- a/src/main/java/net/minestom/server/scoreboard/Team.java +++ b/src/main/java/net/minestom/server/scoreboard/Team.java @@ -1,5 +1,8 @@ package net.minestom.server.scoreboard; +import com.google.common.collect.MapMaker; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.ForwardingAudience; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; @@ -11,16 +14,19 @@ import net.minestom.server.network.packet.server.play.TeamsPacket; import net.minestom.server.network.packet.server.play.TeamsPacket.CollisionRule; import net.minestom.server.network.packet.server.play.TeamsPacket.NameTagVisibility; import net.minestom.server.utils.PacketUtils; +import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; import java.util.Collections; +import java.util.HashSet; import java.util.Set; +import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; /** * This object represents a team on a scoreboard that has a common display theme and other properties. */ -public class Team { +public class Team implements ForwardingAudience { private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager(); @@ -65,6 +71,9 @@ public class Team { */ private JsonMessage suffix; + private final Set playerMembers = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); + private boolean isPlayerMembersUpToDate; + /** * Default constructor to creates a team. * @@ -104,6 +113,9 @@ public class Team { // Sends to all online players the add player packet PacketUtils.sendGroupedPacket(CONNECTION_MANAGER.getOnlinePlayers(), addPlayerPacket); + + // invalidate player members + this.isPlayerMembersUpToDate = false; } /** @@ -123,6 +135,9 @@ public class Team { // Removes the member from the team this.members.remove(member); + + // invalidate player members + this.isPlayerMembersUpToDate = false; } /** @@ -412,4 +427,23 @@ public class Team { PacketUtils.sendGroupedPacket(MinecraftServer.getConnectionManager().getOnlinePlayers(), updatePacket); } + + @Override + public @NonNull Iterable audiences() { + if (!this.isPlayerMembersUpToDate) { + this.playerMembers.clear(); + + for (String member : this.members) { + Player player = MinecraftServer.getConnectionManager().getPlayer(member); + + if (player != null) { + this.playerMembers.add(player); + } + } + + this.isPlayerMembersUpToDate = true; + } + + return this.playerMembers; + } } From 838c95828d3c8e0caf434596ce5b63639ed4c743 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 1 Mar 2021 17:38:19 +0000 Subject: [PATCH 066/364] Expose Adventure APIs --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index af869d0d8..6cce96516 100644 --- a/build.gradle +++ b/build.gradle @@ -186,9 +186,9 @@ dependencies { // 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" + api "net.kyori:adventure-text-serializer-gson" + api "net.kyori:adventure-text-serializer-plain" + api "net.kyori:adventure-text-serializer-legacy" } publishing { From 9fca53a8d36eba79364c61f42b0e6390958f6ea9 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 1 Mar 2021 17:39:10 +0000 Subject: [PATCH 067/364] Make JsonMessage a ComponentLike --- .../java/net/minestom/server/chat/JsonMessage.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/minestom/server/chat/JsonMessage.java b/src/main/java/net/minestom/server/chat/JsonMessage.java index 1cfa81a7a..61c855b52 100644 --- a/src/main/java/net/minestom/server/chat/JsonMessage.java +++ b/src/main/java/net/minestom/server/chat/JsonMessage.java @@ -4,6 +4,7 @@ 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.ComponentLike; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.jetbrains.annotations.NotNull; @@ -17,7 +18,7 @@ import java.util.Objects; * @see Chat Format */ @Deprecated -public abstract class JsonMessage { +public abstract class JsonMessage implements ComponentLike { // true if the compiled string is up-to-date, false otherwise private boolean updated; @@ -52,11 +53,8 @@ public abstract class JsonMessage { return getTextMessage(getJsonObject()).toString(); } - /** - * Gets this JSON message as an Adventure Component. - * @return the component - */ - public Component asComponent() { + @Override + public @NotNull Component asComponent() { return GsonComponentSerializer.gson().deserialize(this.toString()); } From ac57726a983ce873b98b596bf31f2755f112964f Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Tue, 2 Mar 2021 13:42:34 +0000 Subject: [PATCH 068/364] Make more classes implement Audience --- .../net/minestom/server/MinecraftServer.java | 9 +++++- .../java/net/minestom/server/Viewable.java | 12 ++++++++ .../server/adventure/WrapperAudience.java | 28 +++++++++++++++++++ .../minestom/server/instance/Instance.java | 13 ++++++++- .../server/network/ConnectionManager.java | 10 ++++++- 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/WrapperAudience.java diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 24feecfd6..b3545e4ca 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -1,5 +1,7 @@ package net.minestom.server; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.ForwardingAudience; import net.minestom.server.advancements.AdvancementManager; import net.minestom.server.adventure.BossBarManager; import net.minestom.server.benchmark.BenchmarkManager; @@ -50,6 +52,7 @@ import net.minestom.server.utils.validate.Check; import net.minestom.server.world.Difficulty; import net.minestom.server.world.DimensionTypeManager; import net.minestom.server.world.biomes.BiomeManager; +import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -63,7 +66,7 @@ import java.io.IOException; * The server needs to be initialized with {@link #init()} and started with {@link #start(String, int)}. * You should register all of your dimensions, biomes, commands, events, etc... in-between. */ -public final class MinecraftServer { +public final class MinecraftServer implements ForwardingAudience.Single { public final static Logger LOGGER = LoggerFactory.getLogger(MinecraftServer.class); @@ -816,4 +819,8 @@ public final class MinecraftServer { "if you are developing an extension be sure to retrieve them at least after Extension#preInitialize");*/ } + @Override + public @NonNull Audience audience() { + return getConnectionManager(); + } } diff --git a/src/main/java/net/minestom/server/Viewable.java b/src/main/java/net/minestom/server/Viewable.java index 8dd6f1f79..861930804 100644 --- a/src/main/java/net/minestom/server/Viewable.java +++ b/src/main/java/net/minestom/server/Viewable.java @@ -1,5 +1,8 @@ package net.minestom.server; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.ForwardingAudience; +import net.minestom.server.adventure.WrapperAudience; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.utils.PacketUtils; @@ -82,4 +85,13 @@ public interface Viewable { default void sendPacketToViewersAndSelf(@NotNull ServerPacket packet) { sendPacketToViewers(packet); } + + /** + * Gets the result of {@link #getViewers()} as an Adventure Audience. + * + * @return the audience + */ + default @NotNull Audience asAudience() { + return new WrapperAudience(this.getViewers()); + } } diff --git a/src/main/java/net/minestom/server/adventure/WrapperAudience.java b/src/main/java/net/minestom/server/adventure/WrapperAudience.java new file mode 100644 index 000000000..52b2b3db0 --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/WrapperAudience.java @@ -0,0 +1,28 @@ +package net.minestom.server.adventure; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.ForwardingAudience; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.NotNull; + +/** + * Implementation of {@link ForwardingAudience} that acts as a wrapper around another + * iterable collection of audiences. + */ +public class WrapperAudience implements ForwardingAudience { + private final Iterable audiences; + + /** + * Creates a new wrapper audience. + * + * @param audiences the audiences to wrap + */ + public WrapperAudience(@NotNull Iterable audiences) { + this.audiences = audiences; + } + + @Override + public @NonNull Iterable audiences() { + return this.audiences; + } +} diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index 0e5982217..378fc2cf6 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -1,6 +1,11 @@ package net.minestom.server.instance; import com.google.common.collect.Queues; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.ForwardingAudience; import net.minestom.server.MinecraftServer; import net.minestom.server.UpdateManager; import net.minestom.server.data.Data; @@ -34,6 +39,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; @@ -53,7 +59,7 @@ import java.util.function.Consumer; * you need to be sure to signal the {@link UpdateManager} of the changes using * {@link UpdateManager#signalChunkLoad(Instance, int, int)} and {@link UpdateManager#signalChunkUnload(Instance, int, int)}. */ -public abstract class Instance implements BlockModifier, EventHandler, DataContainer { +public abstract class Instance implements BlockModifier, EventHandler, DataContainer, ForwardingAudience { protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager(); protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager(); @@ -1100,4 +1106,9 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta public PFInstanceSpace getInstanceSpace() { return instanceSpace; } + + @Override + public @NonNull Iterable audiences() { + return this.getPlayers(); + } } \ No newline at end of file diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index f4272b4e2..2abc58d28 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -1,6 +1,8 @@ package net.minestom.server.network; import io.netty.channel.Channel; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.ForwardingAudience; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; @@ -24,6 +26,7 @@ import net.minestom.server.utils.async.AsyncUtils; import net.minestom.server.utils.callback.validator.PlayerValidator; import net.minestom.server.utils.validate.Check; import org.apache.commons.text.similarity.JaroWinklerDistance; +import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -37,7 +40,7 @@ import java.util.function.Consumer; /** * Manages the connected clients. */ -public final class ConnectionManager { +public final class ConnectionManager implements ForwardingAudience { private static final long KEEP_ALIVE_DELAY = 10_000; private static final long KEEP_ALIVE_KICK = 30_000; @@ -528,4 +531,9 @@ public final class ConnectionManager { public void addWaitingPlayer(@NotNull Player player) { this.waitingPlayers.add(player); } + + @Override + public @NonNull Iterable audiences() { + return this.getOnlinePlayers(); + } } From be5b31e207cafdf0be155951923652b58a2fdf8e Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Tue, 2 Mar 2021 13:47:54 +0000 Subject: [PATCH 069/364] Add concurrencyLevel to the MapMaker --- .../java/net/minestom/server/adventure/BossBarManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/adventure/BossBarManager.java b/src/main/java/net/minestom/server/adventure/BossBarManager.java index 71c178d62..b50e32139 100644 --- a/src/main/java/net/minestom/server/adventure/BossBarManager.java +++ b/src/main/java/net/minestom/server/adventure/BossBarManager.java @@ -26,13 +26,15 @@ import static net.minestom.server.network.packet.server.play.BossBarPacket.Actio * boss bar management system. */ public class BossBarManager implements BossBar.Listener { + private static final int CONCURRENCY_LEVEL = 4; + private final Map bars; /** * Creates a new boss bar manager. */ public BossBarManager() { - this.bars = new MapMaker().weakKeys().makeMap(); + this.bars = new MapMaker().concurrencyLevel(CONCURRENCY_LEVEL).weakKeys().makeMap(); MinecraftServer.getGlobalEventHandler().addEventCallback(PlayerDisconnectEvent.class, this::onDisconnect); } From d9c7f2cd6132b7af272e865b52992c2d630c531f Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Tue, 2 Mar 2021 14:04:48 +0000 Subject: [PATCH 070/364] Formatting changes --- .../packet/server/play/DisconnectPacket.java | 11 +++++------ .../play/PlayerListHeaderAndFooterPacket.java | 18 ++++++++---------- .../packet/server/play/TitlePacket.java | 13 +++++++++---- 3 files changed, 22 insertions(+), 20 deletions(-) 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 c3ef7bd56..48a3e3e7b 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,6 +1,5 @@ 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; @@ -8,14 +7,14 @@ import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; public class DisconnectPacket implements ServerPacket { - private String payload; + public String message; /** * Creates a new disconnect packet with a given string. - * @param payload the message + * @param message the message */ - public DisconnectPacket(@NotNull String payload) { - this.payload = payload; + public DisconnectPacket(@NotNull String message) { + this.message = message; } /** @@ -28,7 +27,7 @@ public class DisconnectPacket implements ServerPacket { @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(payload); + writer.writeSizedString(message); } @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 021f98727..003540418 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,26 +1,24 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; 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; +import java.util.Objects; + public class PlayerListHeaderAndFooterPacket implements ServerPacket { + private static final String EMPTY_COMPONENT = PlainComponentSerializer.plain().serialize(Component.empty()); + public String header; public String footer; - 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) { - writer.writeSizedString(header); - writer.writeSizedString(footer); + writer.writeSizedString(Objects.requireNonNullElse(header, EMPTY_COMPONENT)); + writer.writeSizedString(Objects.requireNonNullElse(footer, EMPTY_COMPONENT)); } @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 25f8717bc..f99e0e53b 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 @@ -2,7 +2,6 @@ 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; @@ -18,14 +17,17 @@ import static net.minestom.server.network.packet.server.play.TitlePacket.Action. public class TitlePacket implements ServerPacket { - private Action action; + public Action action; - private String payload; + public String payload; - private int fadeIn, stay, fadeOut; + public int fadeIn; + public int stay; + public int fadeOut; /** * 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}, @@ -39,6 +41,7 @@ public class TitlePacket implements ServerPacket { /** * 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} @@ -49,6 +52,7 @@ public class TitlePacket implements ServerPacket { /** * 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 @@ -97,6 +101,7 @@ public class TitlePacket implements ServerPacket { /** * Creates a collection of title packets from an Adventure title. + * * @param title the title * @return the packets */ From 653859e7842f5bf8d2397f2f911d6a96e88f069d Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Tue, 2 Mar 2021 14:41:45 +0000 Subject: [PATCH 071/364] Add SerializationManager --- .../net/minestom/server/MinecraftServer.java | 13 +++ .../server/adventure/BossBarManager.java | 4 +- .../adventure/SerializationManager.java | 87 +++++++++++++++++++ .../server/command/ConsoleSender.java | 1 + .../net/minestom/server/entity/Player.java | 16 ++-- .../play/PlayerListHeaderAndFooterPacket.java | 3 +- .../packet/server/play/TitlePacket.java | 5 +- 7 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/SerializationManager.java diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index b3545e4ca..502987259 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -4,6 +4,7 @@ import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.minestom.server.advancements.AdvancementManager; import net.minestom.server.adventure.BossBarManager; +import net.minestom.server.adventure.SerializationManager; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.command.CommandManager; import net.minestom.server.data.DataManager; @@ -119,6 +120,7 @@ public final class MinecraftServer implements ForwardingAudience.Single { private static DimensionTypeManager dimensionTypeManager; private static BiomeManager biomeManager; private static AdvancementManager advancementManager; + private static SerializationManager serializationManager; private static BossBarManager bossBarManager; private static ExtensionManager extensionManager; @@ -185,6 +187,7 @@ public final class MinecraftServer implements ForwardingAudience.Single { dimensionTypeManager = new DimensionTypeManager(); biomeManager = new BiomeManager(); advancementManager = new AdvancementManager(); + serializationManager = new SerializationManager(); bossBarManager = new BossBarManager(); updateManager = new UpdateManager(); @@ -433,6 +436,16 @@ public final class MinecraftServer implements ForwardingAudience.Single { return connectionManager; } + /** + * Gets the manager handing component serialization. + * + * @return the manager + */ + public static SerializationManager getSerializationManager() { + checkInitStatus(serializationManager); + return serializationManager; + } + /** * Gets the boss bar manager. * diff --git a/src/main/java/net/minestom/server/adventure/BossBarManager.java b/src/main/java/net/minestom/server/adventure/BossBarManager.java index b50e32139..0e7c0d3a3 100644 --- a/src/main/java/net/minestom/server/adventure/BossBarManager.java +++ b/src/main/java/net/minestom/server/adventure/BossBarManager.java @@ -168,7 +168,7 @@ public class BossBarManager implements BossBar.Listener { BossBarPacket createAddPacket() { return this.createGenericPacket(ADD, packet -> { - packet.title = GsonComponentSerializer.gson().serialize(bar.name()); + packet.title = MinecraftServer.getSerializationManager().serialize(bar.name()); packet.color = bar.color().ordinal(); packet.division = bar.overlay().ordinal(); packet.health = bar.progress(); @@ -188,7 +188,7 @@ public class BossBarManager implements BossBar.Listener { } BossBarPacket createTitleUpdate(@NotNull Component title) { - return this.createGenericPacket(UPDATE_TITLE, packet -> packet.title = GsonComponentSerializer.gson().serialize(title)); + return this.createGenericPacket(UPDATE_TITLE, packet -> packet.title = MinecraftServer.getSerializationManager().serialize(title)); } BossBarPacket createFlagsUpdate() { diff --git a/src/main/java/net/minestom/server/adventure/SerializationManager.java b/src/main/java/net/minestom/server/adventure/SerializationManager.java new file mode 100644 index 000000000..fb8051990 --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/SerializationManager.java @@ -0,0 +1,87 @@ +package net.minestom.server.adventure; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.Function; + +/** + * Manager class for handling Adventure serialization. By default this will simply + * serialize components to Strings using {@link GsonComponentSerializer}. However, this + * class can be used to change the way text is serialized. For example, a pre-JSON + * implementation of Minestom could change this to the plain component serializer. + *

+ * This manager also provides the ability to wrap the serializer in a renderer that + * performs operations on each component before the final serialization. + * + * @see #setSerializer(Function) (Function) + * @see #addRenderer(Function) + */ +public class SerializationManager { + private final Set> renderers = new CopyOnWriteArraySet<>(); + private Function serializer = component -> GsonComponentSerializer.gson().serialize(component); + + /** + * Gets the root serializer that is used to convert Components into Strings. + * + * @return the serializer + */ + public @NotNull Function getSerializer() { + return this.serializer; + } + + /** + * Sets the root serializer that is used to convert Components into Strings. This + * method does not replace any existing renderers set with {@link #addRenderer(Function)}. + * + * @param serializer the serializer + */ + public void setSerializer(@NotNull Function serializer) { + this.serializer = serializer; + } + + /** + * Adds a renderer that will be applied to each serializer. The order in which + * each renderer will be applied is arbitrary. If you want control over the order + * of renderers, create a multi-function using {@link Function#andThen(Function)}. + * + * @param renderer the renderer + */ + public void addRenderer(@NotNull Function renderer) { + this.renderers.add(renderer); + } + + /** + * Removes a renderer. + * + * @param renderer the renderer + */ + public void removeRenderer(@NotNull Function renderer) { + this.renderers.remove(renderer); + } + + /** + * Removes all current renderers. + */ + public void clearRenderers() { + this.renderers.clear(); + } + + /** + * Serializes a component into a String using the current serializer. + * + * @param component the component + * + * @return the serialized string + */ + public String serialize(Component component) { + for (Function renderer : this.renderers) { + component = renderer.apply(component); + } + + return this.serializer.apply(component); + } +} diff --git a/src/main/java/net/minestom/server/command/ConsoleSender.java b/src/main/java/net/minestom/server/command/ConsoleSender.java index 36b3ad6cc..782f2a699 100644 --- a/src/main/java/net/minestom/server/command/ConsoleSender.java +++ b/src/main/java/net/minestom/server/command/ConsoleSender.java @@ -24,6 +24,7 @@ public class ConsoleSender implements CommandSender { @Override public void sendMessage(@NonNull Identity source, @NonNull Component message, @NonNull MessageType type) { + // we don't use the serializer here as we just need the plain text of the message LOGGER.info(PlainComponentSerializer.plain().serialize(message)); } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 5e9ecd4d6..2b33dd512 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -820,7 +820,7 @@ public class Player extends LivingEntity implements CommandSender { @Override public void sendMessage(@NonNull Identity source, @NonNull Component message, @NonNull MessageType type) { - ChatMessagePacket chatMessagePacket = new ChatMessagePacket(GsonComponentSerializer.gson().serialize(message), type, source.uuid()); + ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().serialize(message), type, source.uuid()); playerConnection.sendPacket(chatMessagePacket); } @@ -964,10 +964,10 @@ public class Player extends LivingEntity implements CommandSender { @Override public void sendPlayerListHeaderAndFooter(@NonNull Component header, @NonNull Component footer) { - playerConnection.sendPacket(new PlayerListHeaderAndFooterPacket( - GsonComponentSerializer.gson().serialize(header), - GsonComponentSerializer.gson().serialize(footer) - )); + PlayerListHeaderAndFooterPacket packet = new PlayerListHeaderAndFooterPacket(); + packet.header = MinecraftServer.getSerializationManager().serialize(header); + packet.footer = MinecraftServer.getSerializationManager().serialize(footer); + playerConnection.sendPacket(packet); } /** @@ -1047,7 +1047,7 @@ public class Player extends LivingEntity implements CommandSender { @Override public void sendActionBar(@NonNull Component message) { - TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, GsonComponentSerializer.gson().serialize(message)); + TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().serialize(message)); playerConnection.sendPacket(titlePacket); } @@ -1830,9 +1830,9 @@ public class Player extends LivingEntity implements CommandSender { // Packet type depends on the current player connection state final ServerPacket disconnectPacket; if (connectionState == ConnectionState.LOGIN) { - disconnectPacket = new LoginDisconnectPacket(GsonComponentSerializer.gson().serialize(component)); + disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(component)); } else { - disconnectPacket = new DisconnectPacket(GsonComponentSerializer.gson().serialize(component)); + disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().serialize(component)); } if (playerConnection instanceof NettyPlayerConnection) { 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 003540418..667491d06 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,6 +1,7 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; @@ -10,7 +11,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Objects; public class PlayerListHeaderAndFooterPacket implements ServerPacket { - private static final String EMPTY_COMPONENT = PlainComponentSerializer.plain().serialize(Component.empty()); + private static final String EMPTY_COMPONENT = GsonComponentSerializer.gson().serialize(Component.empty()); public String header; public String footer; 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 f99e0e53b..51b8e7047 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 @@ -2,6 +2,7 @@ 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.MinecraftServer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.TickUtils; @@ -109,8 +110,8 @@ public class TitlePacket implements ServerPacket { 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()))); + packets.add(new TitlePacket(SET_TITLE, MinecraftServer.getSerializationManager().serialize(title.title()))); + packets.add(new TitlePacket(SET_SUBTITLE, MinecraftServer.getSerializationManager().serialize(title.subtitle()))); // times packet Title.Times times = title.times(); From 52831e70911ef4f4373d8575a4a09565a3e38046 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Tue, 2 Mar 2021 14:45:54 +0000 Subject: [PATCH 072/364] Add converter from Component to JsonMessage --- src/main/java/net/minestom/server/chat/JsonMessage.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/minestom/server/chat/JsonMessage.java b/src/main/java/net/minestom/server/chat/JsonMessage.java index 61c855b52..44e7257fc 100644 --- a/src/main/java/net/minestom/server/chat/JsonMessage.java +++ b/src/main/java/net/minestom/server/chat/JsonMessage.java @@ -58,6 +58,10 @@ public abstract class JsonMessage implements ComponentLike { return GsonComponentSerializer.gson().deserialize(this.toString()); } + public static @NotNull JsonMessage fromComponent(@NotNull Component component) { + return new RawJsonMessage(GsonComponentSerializer.gson().serialize(component)); + } + /** * Gets the Json representation. *

From 2c2f1b6ceeb10949af5685253070cc593e73f13c Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Tue, 2 Mar 2021 15:56:20 +0000 Subject: [PATCH 073/364] Implement playSound and openBook methods --- .../server/adventure/AdventureUtils.java | 29 +++++ .../net/minestom/server/entity/Player.java | 103 ++++++++++++++++-- .../server/item/metadata/WrittenBookMeta.java | 39 +++++-- .../server/play/NamedSoundEffectPacket.java | 4 +- .../packet/server/play/SoundEffectPacket.java | 11 +- .../minestom/server/sound/SoundCategory.java | 4 + 6 files changed, 165 insertions(+), 25 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/AdventureUtils.java diff --git a/src/main/java/net/minestom/server/adventure/AdventureUtils.java b/src/main/java/net/minestom/server/adventure/AdventureUtils.java new file mode 100644 index 000000000..e39ed046b --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/AdventureUtils.java @@ -0,0 +1,29 @@ +package net.minestom.server.adventure; + +import net.kyori.adventure.key.Key; +import net.minestom.server.sound.Sound; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Adventure related utilities. + */ +public class AdventureUtils { + private static final Map SOUND_MAP = + Arrays.stream(Sound.values()).collect(Collectors.toMap(Sound::getNamespaceID, sound -> sound)); + + /** + * Attempts to get an NMS sound from an Adventure key. + * + * @param name the key + * + * @return the sound, if found + */ + public static @Nullable Sound asSound(@NotNull Key name) { + return SOUND_MAP.get(name.asString()); + } +} diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 2b33dd512..0bbe81b33 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -5,14 +5,13 @@ import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.sound.SoundStop; 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; +import net.minestom.server.adventure.AdventureUtils; import net.minestom.server.attribute.AttributeInstance; import net.minestom.server.bossbar.BossBar; import net.minestom.server.chat.ChatParser; @@ -845,12 +844,13 @@ public class Player extends LivingEntity implements CommandSender { * @param z the effect Z * @param volume the volume of the sound (1 is 100%) * @param pitch the pitch of the sound, between 0.5 and 2.0 + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)} */ - public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, - int x, int y, int z, float volume, float pitch) { + @Deprecated + public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) { SoundEffectPacket soundEffectPacket = new SoundEffectPacket(); soundEffectPacket.soundId = sound.getId(); - soundEffectPacket.soundCategory = soundCategory; + soundEffectPacket.soundCategory = soundCategory.ordinal(); soundEffectPacket.x = x; soundEffectPacket.y = y; soundEffectPacket.z = z; @@ -863,9 +863,10 @@ public class Player extends LivingEntity implements CommandSender { * Plays a sound from the {@link Sound} enum. * * @see #playSound(Sound, SoundCategory, int, int, int, float, float) + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)} */ - public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, - BlockPosition position, float volume, float pitch) { + @Deprecated + public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, BlockPosition position, float volume, float pitch) { playSound(sound, soundCategory, position.getX(), position.getY(), position.getZ(), volume, pitch); } @@ -879,12 +880,13 @@ public class Player extends LivingEntity implements CommandSender { * @param z the effect Z * @param volume the volume of the sound (1 is 100%) * @param pitch the pitch of the sound, between 0.5 and 2.0 + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)} */ - public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, - int x, int y, int z, float volume, float pitch) { + @Deprecated + public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) { NamedSoundEffectPacket namedSoundEffectPacket = new NamedSoundEffectPacket(); namedSoundEffectPacket.soundName = identifier; - namedSoundEffectPacket.soundCategory = soundCategory; + namedSoundEffectPacket.soundCategory = soundCategory.ordinal(); namedSoundEffectPacket.x = x; namedSoundEffectPacket.y = y; namedSoundEffectPacket.z = z; @@ -897,7 +899,9 @@ public class Player extends LivingEntity implements CommandSender { * Plays a sound from an identifier (represents a custom sound in a resource pack). * * @see #playSound(String, SoundCategory, int, int, int, float, float) + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound, double, double, double)} */ + @Deprecated public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, BlockPosition position, float volume, float pitch) { playSound(identifier, soundCategory, position.getX(), position.getY(), position.getZ(), volume, pitch); } @@ -909,7 +913,9 @@ public class Player extends LivingEntity implements CommandSender { * @param soundCategory the sound category * @param volume the volume of the sound (1 is 100%) * @param pitch the pitch of the sound, between 0.5 and 2.0 + * @deprecated Use {@link #playSound(net.kyori.adventure.sound.Sound)} */ + @Deprecated public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, float volume, float pitch) { EntitySoundEffectPacket entitySoundEffectPacket = new EntitySoundEffectPacket(); entitySoundEffectPacket.entityId = getEntityId(); @@ -920,6 +926,56 @@ public class Player extends LivingEntity implements CommandSender { playerConnection.sendPacket(entitySoundEffectPacket); } + @Override + public void playSound(net.kyori.adventure.sound.@NonNull Sound sound) { + this.playSound(sound, this.position.getX(), this.position.getY(), this.position.getZ()); + } + + @Override + public void playSound(net.kyori.adventure.sound.@NonNull Sound sound, double x, double y, double z) { + Sound minestomSound = AdventureUtils.asSound(sound.name()); + + if (minestomSound == null) { + NamedSoundEffectPacket packet = new NamedSoundEffectPacket(); + packet.soundName = sound.name().asString(); + packet.soundCategory = sound.source().ordinal(); + packet.x = (int) x; + packet.y = (int) y; + packet.z = (int) z; + packet.volume = sound.volume(); + packet.pitch = sound.pitch(); + playerConnection.sendPacket(packet); + } else { + SoundEffectPacket packet = new SoundEffectPacket(); + packet.soundId = minestomSound.getId(); + packet.soundCategory = sound.source().ordinal(); + packet.x = (int) x; + packet.y = (int) y; + packet.z = (int) z; + packet.volume = sound.volume(); + packet.pitch = sound.pitch(); + playerConnection.sendPacket(packet); + } + } + + @Override + public void stopSound(@NonNull SoundStop stop) { + StopSoundPacket packet = new StopSoundPacket(); + packet.flags = 0x0; + + if (stop.source() != null) { + packet.flags |= 0x1; + packet.source = stop.source().ordinal(); + } + + if (stop.sound() != null) { + packet.flags |= 0x2; + packet.sound = stop.sound().asString(); + } + + this.playerConnection.sendPacket(packet); + } + /** * Plays a given effect at the given position for this player. * @@ -941,7 +997,9 @@ public class Player extends LivingEntity implements CommandSender { /** * Sends a {@link StopSoundPacket} packet. + * @deprecated Use {@link #stopSound(SoundStop)} with {@link SoundStop#all()} */ + @Deprecated public void stopSound() { StopSoundPacket stopSoundPacket = new StopSoundPacket(); stopSoundPacket.flags = 0x00; @@ -1129,7 +1187,28 @@ public class Player extends LivingEntity implements CommandSender { @Override public void openBook(@NonNull Book book) { - // TODO write the book + // make the book + ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1); + writtenBook.setItemMeta(WrittenBookMeta.fromAdventure(book)); + + // Set book in offhand + SetSlotPacket setBookPacket = new SetSlotPacket(); + setBookPacket.windowId = 0; + setBookPacket.slot = 45; + setBookPacket.itemStack = writtenBook; + playerConnection.sendPacket(setBookPacket); + + // Open the book + OpenBookPacket openBookPacket = new OpenBookPacket(); + openBookPacket.hand = Hand.OFF; + playerConnection.sendPacket(openBookPacket); + + // Restore the item in offhand + SetSlotPacket restoreItemPacket = new SetSlotPacket(); + restoreItemPacket.windowId = 0; + restoreItemPacket.slot = 45; + restoreItemPacket.itemStack = getItemInOffHand(); + playerConnection.sendPacket(restoreItemPacket); } @Override diff --git a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java index 575fa102a..2fc0b4201 100644 --- a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java @@ -1,5 +1,8 @@ package net.minestom.server.item.metadata; +import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatParser; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; @@ -18,7 +21,7 @@ public class WrittenBookMeta extends ItemMeta { private WrittenBookGeneration generation; private String author; private String title; - private List pages = new ArrayList<>(); + private List pages = new ArrayList<>(); /** * Gets if the book is resolved. @@ -100,7 +103,7 @@ public class WrittenBookMeta extends ItemMeta { * * @return a modifiable {@link ArrayList} with the pages of the book */ - public List getPages() { + public List getPages() { return pages; } @@ -109,7 +112,7 @@ public class WrittenBookMeta extends ItemMeta { * * @param pages the array list containing the book pages */ - public void setPages(List pages) { + public void setPages(List pages) { this.pages = pages; } @@ -149,9 +152,7 @@ public class WrittenBookMeta extends ItemMeta { if (compound.containsKey("pages")) { final NBTList list = compound.getList("pages"); for (NBTString page : list) { - final String jsonPage = page.getValue(); - final ColoredText coloredText = ChatParser.toColoredText(jsonPage); - this.pages.add(coloredText); + this.pages.add(page.getValue()); } } } @@ -172,8 +173,8 @@ public class WrittenBookMeta extends ItemMeta { } if (!pages.isEmpty()) { NBTList list = new NBTList<>(NBTTypes.TAG_String); - for (JsonMessage page : pages) { - list.add(new NBTString(page.toString())); + for (String page : pages) { + list.add(new NBTString(page)); } compound.set("pages", list); } @@ -196,4 +197,26 @@ public class WrittenBookMeta extends ItemMeta { ORIGINAL, COPY_OF_ORIGINAL, COPY_OF_COPY, TATTERED } + /** + * Creates a written book meta from an Adventure book. This meta will not be + * resolved and the generation will default to {@link WrittenBookGeneration#ORIGINAL}. + * + * @param book the book + * + * @return the meta + */ + public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book) { + WrittenBookMeta meta = new WrittenBookMeta(); + meta.resolved = false; + meta.generation = WrittenBookGeneration.ORIGINAL; + meta.author = MinecraftServer.getSerializationManager().serialize(book.author()); + meta.title = MinecraftServer.getSerializationManager().serialize(book.title()); + meta.pages = new ArrayList<>(); + + for (Component page : book.pages()) { + meta.pages.add(MinecraftServer.getSerializationManager().serialize(page)); + } + + return meta; + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java index 0647fd297..5173054d7 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull; public class NamedSoundEffectPacket implements ServerPacket { public String soundName; - public SoundCategory soundCategory; + public int soundCategory; public int x, y, z; public float volume; public float pitch; @@ -17,7 +17,7 @@ public class NamedSoundEffectPacket implements ServerPacket { @Override public void write(@NotNull BinaryWriter writer) { writer.writeSizedString(soundName); - writer.writeVarInt(soundCategory.ordinal()); + writer.writeVarInt(soundCategory); writer.writeInt(x * 8); writer.writeInt(y * 8); writer.writeInt(z * 8); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java index 4909a3106..4db791a6d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java @@ -1,5 +1,6 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.sound.Sound.Source; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.sound.Sound; @@ -11,15 +12,19 @@ import org.jetbrains.annotations.NotNull; public class SoundEffectPacket implements ServerPacket { public int soundId; - public SoundCategory soundCategory; + public int soundCategory; public int x, y, z; public float volume; public float pitch; + /** + * @deprecated Use variables + */ + @Deprecated public static SoundEffectPacket create(SoundCategory category, Sound sound, Position position, float volume, float pitch) { SoundEffectPacket packet = new SoundEffectPacket(); packet.soundId = sound.getId(); - packet.soundCategory = category; + packet.soundCategory = category.ordinal(); // *8 converts to fixed-point representation with 3 bits for fractional part packet.x = (int) position.getX(); packet.y = (int) position.getY(); @@ -32,7 +37,7 @@ public class SoundEffectPacket implements ServerPacket { @Override public void write(@NotNull BinaryWriter writer) { writer.writeVarInt(soundId); - writer.writeVarInt(soundCategory.ordinal()); + writer.writeVarInt(soundCategory); writer.writeInt(x * 8); writer.writeInt(y * 8); writer.writeInt(z * 8); diff --git a/src/main/java/net/minestom/server/sound/SoundCategory.java b/src/main/java/net/minestom/server/sound/SoundCategory.java index cc92bb629..c80f46131 100644 --- a/src/main/java/net/minestom/server/sound/SoundCategory.java +++ b/src/main/java/net/minestom/server/sound/SoundCategory.java @@ -1,5 +1,9 @@ package net.minestom.server.sound; +/** + * @deprecated Use {@link net.kyori.adventure.sound.Sound.Source} + */ +@Deprecated public enum SoundCategory { MASTER, MUSIC, From 316ecbbf5a066813f3726dd6919c89b21862642a Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Tue, 2 Mar 2021 17:47:56 +0000 Subject: [PATCH 074/364] Cleanup annotations and imports --- .../net/minestom/server/MinecraftServer.java | 3 +- .../server/adventure/AdventureUtils.java | 2 +- .../server/adventure/BossBarManager.java | 37 +++++++++---------- .../server/adventure/WrapperAudience.java | 3 +- .../server/command/ConsoleSender.java | 3 +- .../net/minestom/server/entity/Player.java | 21 +++++------ .../minestom/server/instance/Instance.java | 3 +- .../server/network/ConnectionManager.java | 3 +- .../net/minestom/server/scoreboard/Team.java | 5 +-- 9 files changed, 34 insertions(+), 46 deletions(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 502987259..0518f5f89 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -53,7 +53,6 @@ import net.minestom.server.utils.validate.Check; import net.minestom.server.world.Difficulty; import net.minestom.server.world.DimensionTypeManager; import net.minestom.server.world.biomes.BiomeManager; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -833,7 +832,7 @@ public final class MinecraftServer implements ForwardingAudience.Single { } @Override - public @NonNull Audience audience() { + public @NotNull Audience audience() { return getConnectionManager(); } } diff --git a/src/main/java/net/minestom/server/adventure/AdventureUtils.java b/src/main/java/net/minestom/server/adventure/AdventureUtils.java index e39ed046b..cd23b178c 100644 --- a/src/main/java/net/minestom/server/adventure/AdventureUtils.java +++ b/src/main/java/net/minestom/server/adventure/AdventureUtils.java @@ -2,8 +2,8 @@ package net.minestom.server.adventure; import net.kyori.adventure.key.Key; import net.minestom.server.sound.Sound; -import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Map; diff --git a/src/main/java/net/minestom/server/adventure/BossBarManager.java b/src/main/java/net/minestom/server/adventure/BossBarManager.java index 0e7c0d3a3..a86123fc3 100644 --- a/src/main/java/net/minestom/server/adventure/BossBarManager.java +++ b/src/main/java/net/minestom/server/adventure/BossBarManager.java @@ -2,21 +2,18 @@ package net.minestom.server.adventure; import com.google.common.collect.MapMaker; import net.kyori.adventure.bossbar.BossBar; -import net.kyori.adventure.bossbar.BossBar.*; +import net.kyori.adventure.bossbar.BossBar.Color; +import net.kyori.adventure.bossbar.BossBar.Flag; +import net.kyori.adventure.bossbar.BossBar.Overlay; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; -import net.minestom.server.event.GlobalEventHandler; import net.minestom.server.event.player.PlayerDisconnectEvent; import net.minestom.server.network.packet.server.play.BossBarPacket; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; -import javax.swing.*; import java.util.*; import java.util.function.Consumer; -import java.util.logging.Handler; import static net.minestom.server.network.packet.server.play.BossBarPacket.Action.*; @@ -69,31 +66,31 @@ public class BossBarManager implements BossBar.Listener { } @Override - public void bossBarNameChanged(@NonNull BossBar bar, @NonNull Component oldName, @NonNull Component newName) { + public void bossBarNameChanged(@NotNull BossBar bar, @NotNull Component oldName, @NotNull Component newName) { Holder holder = this.bars.get(bar); this.updatePlayers(holder.createTitleUpdate(newName), holder.players); } @Override - public void bossBarProgressChanged(@NonNull BossBar bar, float oldProgress, float newProgress) { + public void bossBarProgressChanged(@NotNull BossBar bar, float oldProgress, float newProgress) { Holder holder = this.bars.get(bar); this.updatePlayers(holder.createPercentUpdate(newProgress), holder.players); } @Override - public void bossBarColorChanged(@NonNull BossBar bar, @NonNull Color oldColor, @NonNull Color newColor) { + public void bossBarColorChanged(@NotNull BossBar bar, @NotNull Color oldColor, @NotNull Color newColor) { Holder holder = this.bars.get(bar); this.updatePlayers(holder.createColorUpdate(newColor), holder.players); } @Override - public void bossBarOverlayChanged(@NonNull BossBar bar, BossBar.@NonNull Overlay oldOverlay, BossBar.@NonNull Overlay newOverlay) { + public void bossBarOverlayChanged(@NotNull BossBar bar, BossBar.@NotNull Overlay oldOverlay, BossBar.@NotNull Overlay newOverlay) { Holder holder = this.bars.get(bar); this.updatePlayers(holder.createOverlayUpdate(newOverlay), holder.players); } @Override - public void bossBarFlagsChanged(@NonNull BossBar bar, @NonNull Set flagsAdded, @NonNull Set flagsRemoved) { + public void bossBarFlagsChanged(@NotNull BossBar bar, @NotNull Set flagsAdded, @NotNull Set flagsRemoved) { Holder holder = this.bars.get(bar); this.updatePlayers(holder.createFlagsUpdate(), holder.players); } @@ -162,11 +159,11 @@ public class BossBarManager implements BossBar.Listener { this.registered = false; } - BossBarPacket createRemovePacket() { + @NotNull BossBarPacket createRemovePacket() { return this.createGenericPacket(REMOVE, packet -> {}); } - BossBarPacket createAddPacket() { + @NotNull BossBarPacket createAddPacket() { return this.createGenericPacket(ADD, packet -> { packet.title = MinecraftServer.getSerializationManager().serialize(bar.name()); packet.color = bar.color().ordinal(); @@ -176,37 +173,37 @@ public class BossBarManager implements BossBar.Listener { }); } - BossBarPacket createPercentUpdate(float newPercent) { + @NotNull BossBarPacket createPercentUpdate(float newPercent) { return this.createGenericPacket(UPDATE_HEALTH, packet -> packet.health = newPercent); } - BossBarPacket createColorUpdate(@NotNull Color color) { + @NotNull BossBarPacket createColorUpdate(@NotNull Color color) { return this.createGenericPacket(UPDATE_STYLE, packet -> { packet.color = color.ordinal(); packet.division = bar.overlay().ordinal(); }); } - BossBarPacket createTitleUpdate(@NotNull Component title) { + @NotNull BossBarPacket createTitleUpdate(@NotNull Component title) { return this.createGenericPacket(UPDATE_TITLE, packet -> packet.title = MinecraftServer.getSerializationManager().serialize(title)); } - BossBarPacket createFlagsUpdate() { + @NotNull BossBarPacket createFlagsUpdate() { return createFlagsUpdate(bar.flags()); } - BossBarPacket createFlagsUpdate(@NotNull Set newFlags) { + @NotNull BossBarPacket createFlagsUpdate(@NotNull Set newFlags) { return this.createGenericPacket(UPDATE_FLAGS, packet -> packet.flags = serializeFlags(bar.flags())); } - BossBarPacket createOverlayUpdate(@NotNull Overlay overlay) { + @NotNull BossBarPacket createOverlayUpdate(@NotNull Overlay overlay) { return this.createGenericPacket(UPDATE_STYLE, packet -> { packet.division = overlay.ordinal(); packet.color = bar.color().ordinal(); }); } - BossBarPacket createGenericPacket(@NotNull BossBarPacket.Action action, @NotNull Consumer consumer) { + @NotNull BossBarPacket createGenericPacket(@NotNull BossBarPacket.Action action, @NotNull Consumer consumer) { BossBarPacket packet = new BossBarPacket(); packet.uuid = this.uuid; packet.action = action; diff --git a/src/main/java/net/minestom/server/adventure/WrapperAudience.java b/src/main/java/net/minestom/server/adventure/WrapperAudience.java index 52b2b3db0..c0ca3c400 100644 --- a/src/main/java/net/minestom/server/adventure/WrapperAudience.java +++ b/src/main/java/net/minestom/server/adventure/WrapperAudience.java @@ -2,7 +2,6 @@ package net.minestom.server.adventure; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; /** @@ -22,7 +21,7 @@ public class WrapperAudience implements ForwardingAudience { } @Override - public @NonNull Iterable audiences() { + public @NotNull Iterable audiences() { return this.audiences; } } diff --git a/src/main/java/net/minestom/server/command/ConsoleSender.java b/src/main/java/net/minestom/server/command/ConsoleSender.java index 782f2a699..62c6c9994 100644 --- a/src/main/java/net/minestom/server/command/ConsoleSender.java +++ b/src/main/java/net/minestom/server/command/ConsoleSender.java @@ -5,7 +5,6 @@ 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; @@ -23,7 +22,7 @@ public class ConsoleSender implements CommandSender { private final Set permissions = new CopyOnWriteArraySet<>(); @Override - public void sendMessage(@NonNull Identity source, @NonNull Component message, @NonNull MessageType type) { + public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { // we don't use the serializer here as we just need the plain text of the message LOGGER.info(PlainComponentSerializer.plain().serialize(message)); } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 0bbe81b33..6e879a84b 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -69,7 +69,6 @@ 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; @@ -818,7 +817,7 @@ public class Player extends LivingEntity implements CommandSender { } @Override - public void sendMessage(@NonNull Identity source, @NonNull Component message, @NonNull MessageType type) { + public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().serialize(message), type, source.uuid()); playerConnection.sendPacket(chatMessagePacket); } @@ -927,12 +926,12 @@ public class Player extends LivingEntity implements CommandSender { } @Override - public void playSound(net.kyori.adventure.sound.@NonNull Sound sound) { + public void playSound(net.kyori.adventure.sound.@NotNull Sound sound) { this.playSound(sound, this.position.getX(), this.position.getY(), this.position.getZ()); } @Override - public void playSound(net.kyori.adventure.sound.@NonNull Sound sound, double x, double y, double z) { + public void playSound(net.kyori.adventure.sound.@NotNull Sound sound, double x, double y, double z) { Sound minestomSound = AdventureUtils.asSound(sound.name()); if (minestomSound == null) { @@ -959,7 +958,7 @@ public class Player extends LivingEntity implements CommandSender { } @Override - public void stopSound(@NonNull SoundStop stop) { + public void stopSound(@NotNull SoundStop stop) { StopSoundPacket packet = new StopSoundPacket(); packet.flags = 0x0; @@ -1021,7 +1020,7 @@ public class Player extends LivingEntity implements CommandSender { } @Override - public void sendPlayerListHeaderAndFooter(@NonNull Component header, @NonNull Component footer) { + public void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) { PlayerListHeaderAndFooterPacket packet = new PlayerListHeaderAndFooterPacket(); packet.header = MinecraftServer.getSerializationManager().serialize(header); packet.footer = MinecraftServer.getSerializationManager().serialize(footer); @@ -1097,14 +1096,14 @@ public class Player extends LivingEntity implements CommandSender { } @Override - public void showTitle(@NonNull Title title) { + public void showTitle(@NotNull Title title) { for (TitlePacket titlePacket : TitlePacket.of(title)) { playerConnection.sendPacket(titlePacket); } } @Override - public void sendActionBar(@NonNull Component message) { + public void sendActionBar(@NotNull Component message) { TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().serialize(message)); playerConnection.sendPacket(titlePacket); } @@ -1149,12 +1148,12 @@ public class Player extends LivingEntity implements CommandSender { } @Override - public void showBossBar(@NonNull BossBar bar) { + public void showBossBar(@NotNull BossBar bar) { MinecraftServer.getBossBarManager().addBossBar(this, bar); } @Override - public void hideBossBar(@NonNull BossBar bar) { + public void hideBossBar(@NotNull BossBar bar) { MinecraftServer.getBossBarManager().removeBossBar(this, bar); } @@ -1186,7 +1185,7 @@ public class Player extends LivingEntity implements CommandSender { } @Override - public void openBook(@NonNull Book book) { + public void openBook(@NotNull Book book) { // make the book ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1); writtenBook.setItemMeta(WrittenBookMeta.fromAdventure(book)); diff --git a/src/main/java/net/minestom/server/instance/Instance.java b/src/main/java/net/minestom/server/instance/Instance.java index 378fc2cf6..16b3acd61 100644 --- a/src/main/java/net/minestom/server/instance/Instance.java +++ b/src/main/java/net/minestom/server/instance/Instance.java @@ -39,7 +39,6 @@ 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; @@ -1108,7 +1107,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta } @Override - public @NonNull Iterable audiences() { + public @NotNull Iterable audiences() { return this.getPlayers(); } } \ No newline at end of file diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 2abc58d28..b07b98fca 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -26,7 +26,6 @@ import net.minestom.server.utils.async.AsyncUtils; import net.minestom.server.utils.callback.validator.PlayerValidator; import net.minestom.server.utils.validate.Check; import org.apache.commons.text.similarity.JaroWinklerDistance; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -533,7 +532,7 @@ public final class ConnectionManager implements ForwardingAudience { } @Override - public @NonNull Iterable audiences() { + public @NotNull Iterable audiences() { return this.getOnlinePlayers(); } } diff --git a/src/main/java/net/minestom/server/scoreboard/Team.java b/src/main/java/net/minestom/server/scoreboard/Team.java index a2fbc7195..a342e836d 100644 --- a/src/main/java/net/minestom/server/scoreboard/Team.java +++ b/src/main/java/net/minestom/server/scoreboard/Team.java @@ -14,13 +14,10 @@ import net.minestom.server.network.packet.server.play.TeamsPacket; import net.minestom.server.network.packet.server.play.TeamsPacket.CollisionRule; import net.minestom.server.network.packet.server.play.TeamsPacket.NameTagVisibility; import net.minestom.server.utils.PacketUtils; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; import java.util.Collections; -import java.util.HashSet; import java.util.Set; -import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; /** @@ -429,7 +426,7 @@ public class Team implements ForwardingAudience { } @Override - public @NonNull Iterable audiences() { + public @NotNull Iterable audiences() { if (!this.isPlayerMembersUpToDate) { this.playerMembers.clear(); From 6095523d8a8a951dce6786ca715371199a9a998c Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Wed, 3 Mar 2021 14:46:17 +0000 Subject: [PATCH 075/364] Deprecate old chat in packets --- .../server/network/ConnectionManager.java | 67 +++++++++++++++++-- .../client/handshake/HandshakePacket.java | 13 ++-- .../login/LoginPluginResponsePacket.java | 8 +-- .../packet/client/login/LoginStartPacket.java | 6 +- .../server/login/LoginDisconnectPacket.java | 4 ++ .../server/play/AdvancementsPacket.java | 17 +++-- .../packet/server/play/BossBarPacket.java | 26 +++++-- .../packet/server/play/ChatMessagePacket.java | 22 ++++-- .../packet/server/play/CombatEventPacket.java | 12 +++- .../packet/server/play/DisconnectPacket.java | 7 +- .../server/play/EntitySoundEffectPacket.java | 10 ++- .../packet/server/play/MapDataPacket.java | 12 +++- .../server/play/NamedSoundEffectPacket.java | 7 +- .../packet/server/play/PlayerInfoPacket.java | 28 ++++++-- .../play/PlayerListHeaderAndFooterPacket.java | 15 ++++- .../play/ScoreboardObjectivePacket.java | 10 ++- .../packet/server/play/SoundEffectPacket.java | 7 +- .../packet/server/play/TabCompletePacket.java | 9 ++- .../packet/server/play/TeamsPacket.java | 25 +++++-- .../packet/server/play/TitlePacket.java | 1 - .../network/player/PlayerConnection.java | 6 +- 21 files changed, 246 insertions(+), 66 deletions(-) diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index b07b98fca..5bef23706 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -3,6 +3,10 @@ package net.minestom.server.network; import io.netty.channel.Channel; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; @@ -43,7 +47,7 @@ public final class ConnectionManager implements ForwardingAudience { private static final long KEEP_ALIVE_DELAY = 10_000; private static final long KEEP_ALIVE_KICK = 30_000; - private static final ColoredText TIMEOUT_TEXT = ColoredText.of(ChatColor.RED + "Timeout"); + private static final Component TIMEOUT_TEXT = Component.text("Timeout", NamedTextColor.RED); private final Queue waitingPlayers = new ConcurrentLinkedQueue<>(); private final Set players = new CopyOnWriteArraySet<>(); @@ -60,7 +64,7 @@ public final class ConnectionManager implements ForwardingAudience { // The consumers to call once a player connect, mostly used to init events private final List> playerInitializations = new CopyOnWriteArrayList<>(); - private JsonMessage shutdownText = ColoredText.of(ChatColor.RED, "The server is shutting down."); + private Component shutdownText = Component.text("The server is shutting down.", NamedTextColor.RED); /** * Gets the {@link Player} linked to a {@link PlayerConnection}. @@ -146,7 +150,10 @@ public final class ConnectionManager implements ForwardingAudience { * * @param jsonMessage the message to send, probably a {@link net.minestom.server.chat.ColoredText} or {@link net.minestom.server.chat.RichMessage} * @param condition the condition to receive the message + * + * @deprecated Use {@link Audience#sendMessage(Component)} on {@link #audiences(PlayerValidator)} */ + @Deprecated public void broadcastMessage(@NotNull JsonMessage jsonMessage, @Nullable PlayerValidator condition) { final Collection recipients = getRecipients(condition); @@ -160,7 +167,9 @@ public final class ConnectionManager implements ForwardingAudience { * Sends a {@link JsonMessage} to all online players. * * @param jsonMessage the message to send, probably a {@link net.minestom.server.chat.ColoredText} or {@link net.minestom.server.chat.RichMessage} + * @deprecated Use {@link #sendMessage(Component)} */ + @Deprecated public void broadcastMessage(@NotNull JsonMessage jsonMessage) { broadcastMessage(jsonMessage, null); } @@ -315,23 +324,48 @@ public final class ConnectionManager implements ForwardingAudience { this.playerInitializations.remove(playerInitialization); } + /** + * Gets the kick reason when the server is shutdown using {@link MinecraftServer#stopCleanly()}. + * + * @return the kick reason in case on a shutdown + * + * @deprecated Use {@link #getShutdownText()} + */ + @Deprecated + @NotNull + public JsonMessage getShutdownTextJson() { + return JsonMessage.fromComponent(shutdownText); + } + /** * Gets the kick reason when the server is shutdown using {@link MinecraftServer#stopCleanly()}. * * @return the kick reason in case on a shutdown */ @NotNull - public JsonMessage getShutdownText() { + public Component getShutdownText() { return shutdownText; } + /** + * Changes the kick reason in case of a shutdown. + * + * @param shutdownText the new shutdown kick reason + * @see #getShutdownTextJson() + * @deprecated Use {@link #setShutdownText(Component)} + */ + @Deprecated + public void setShutdownText(@NotNull JsonMessage shutdownText) { + this.shutdownText = shutdownText.asComponent(); + } + /** * Changes the kick reason in case of a shutdown. * * @param shutdownText the new shutdown kick reason * @see #getShutdownText() */ - public void setShutdownText(@NotNull JsonMessage shutdownText) { + public void setShutdownText(@NotNull Component shutdownText) { this.shutdownText = shutdownText; } @@ -461,7 +495,7 @@ public final class ConnectionManager implements ForwardingAudience { * Shutdowns the connection manager by kicking all the currently connected players. */ public void shutdown() { - DisconnectPacket disconnectPacket = new DisconnectPacket(getShutdownText()); + DisconnectPacket disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().serialize(shutdownText)); for (Player player : getOnlinePlayers()) { final PlayerConnection playerConnection = player.getPlayerConnection(); if (playerConnection instanceof NettyPlayerConnection) { @@ -535,4 +569,27 @@ public final class ConnectionManager implements ForwardingAudience { public @NotNull Iterable audiences() { return this.getOnlinePlayers(); } + + /** + * Gets the audiences of players who match a given validator. + * + * @param validator the validator + * + * @return the audience + */ + public @NotNull Iterable audiences(@Nullable PlayerValidator validator) { + if (validator == null) { + return this.audiences(); + } + + List validatedPlayers = new ArrayList<>(); + + for (Player onlinePlayer : this.getOnlinePlayers()) { + if (validator.isValid(onlinePlayer)) { + validatedPlayers.add(onlinePlayer); + } + } + + return validatedPlayers; + } } diff --git a/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java b/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java index b64f08061..a0441b745 100644 --- a/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java @@ -1,8 +1,8 @@ package net.minestom.server.network.packet.client.handshake; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; -import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; import net.minestom.server.entity.PlayerSkin; import net.minestom.server.extras.bungee.BungeeCordProxy; import net.minestom.server.network.ConnectionState; @@ -21,9 +21,8 @@ public class HandshakePacket implements ClientPreplayPacket { /** * Text sent if a player tries to connect with an invalid version of the client */ - private static final ColoredText INVALID_VERSION_TEXT = ColoredText.of(ChatColor.RED, "Invalid Version, please use " + MinecraftServer.VERSION_NAME); - - private static final ColoredText INVALID_BUNGEE_FORWARDING = ColoredText.of(ChatColor.RED, "If you wish to use IP forwarding, please enable it in your BungeeCord config as well!"); + private static final Component INVALID_VERSION_TEXT = Component.text("Invalid Version, please use " + MinecraftServer.VERSION_NAME, NamedTextColor.RED); + private static final Component INVALID_BUNGEE_FORWARDING = Component.text("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!", NamedTextColor.RED); private int protocolVersion; private String serverAddress; @@ -71,7 +70,7 @@ public class HandshakePacket implements ClientPreplayPacket { nettyPlayerConnection.UNSAFE_setBungeeSkin(playerSkin); } else { - nettyPlayerConnection.sendPacket(new LoginDisconnectPacket(INVALID_BUNGEE_FORWARDING)); + nettyPlayerConnection.sendPacket(new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(INVALID_BUNGEE_FORWARDING))); nettyPlayerConnection.disconnect(); return; } @@ -95,7 +94,7 @@ public class HandshakePacket implements ClientPreplayPacket { } } else { // Incorrect client version - connection.sendPacket(new LoginDisconnectPacket(INVALID_VERSION_TEXT.toString())); + connection.sendPacket(new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(INVALID_VERSION_TEXT))); connection.disconnect(); } break; diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java index 3a65e0d15..abe4a74e5 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java @@ -1,8 +1,8 @@ package net.minestom.server.network.packet.client.login; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; -import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; import net.minestom.server.entity.Player; import net.minestom.server.entity.PlayerSkin; import net.minestom.server.extras.velocity.VelocityProxy; @@ -23,7 +23,7 @@ public class LoginPluginResponsePacket implements ClientPreplayPacket { private final static ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager(); - public static final ColoredText INVALID_PROXY_RESPONSE = ColoredText.of(ChatColor.RED, "Invalid proxy response!"); + public static final Component INVALID_PROXY_RESPONSE = Component.text("Invalid proxy response!", NamedTextColor.RED); public int messageId; public boolean successful; @@ -80,7 +80,7 @@ public class LoginPluginResponsePacket implements ClientPreplayPacket { Player player = CONNECTION_MANAGER.startPlayState(connection, uuid, username, true); player.setSkin(playerSkin); } else { - LoginDisconnectPacket disconnectPacket = new LoginDisconnectPacket(INVALID_PROXY_RESPONSE); + LoginDisconnectPacket disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(INVALID_PROXY_RESPONSE)); nettyPlayerConnection.sendPacket(disconnectPacket); } diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java index 6e6f2881e..01fc88b5f 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java @@ -1,5 +1,7 @@ package net.minestom.server.network.packet.client.login; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; @@ -22,7 +24,7 @@ import java.util.concurrent.ThreadLocalRandom; public class LoginStartPacket implements ClientPreplayPacket { - private static final ColoredText ALREADY_CONNECTED_JSON = ColoredText.of(ChatColor.RED, "You are already on this server"); + private static final Component ALREADY_CONNECTED = Component.text("You are already on this server", NamedTextColor.RED); public String username; @@ -72,7 +74,7 @@ public class LoginStartPacket implements ClientPreplayPacket { if (MojangAuth.isEnabled() && isNettyClient) { // Mojang auth if (CONNECTION_MANAGER.getPlayer(username) != null) { - connection.sendPacket(new LoginDisconnectPacket(ALREADY_CONNECTED_JSON)); + connection.sendPacket(new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(ALREADY_CONNECTED))); connection.disconnect(); return; } diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java index 764d1a5ea..5c9b05b23 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java @@ -14,6 +14,10 @@ public class LoginDisconnectPacket implements ServerPacket { this.kickMessage = kickMessage; } + /** + * @deprecated Use {@link #LoginDisconnectPacket(String)} + */ + @Deprecated public LoginDisconnectPacket(@NotNull JsonMessage jsonKickMessage) { this(jsonKickMessage.toString()); } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java index ce868aa22..f8f950692 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java @@ -84,8 +84,8 @@ public class AdvancementsPacket implements ServerPacket { } public static class DisplayData implements Writeable { - public JsonMessage title; // Only text - public JsonMessage description; // Only text + public String title; // Only text + public String description; // Only text public ItemStack icon; public FrameType frameType; public int flags; @@ -93,10 +93,19 @@ public class AdvancementsPacket implements ServerPacket { public float x; public float y; + /** + * @deprecated Use {@link #title} + */ + public @Deprecated JsonMessage titleJson; // Only text + /** + * @deprecated Use {@link #description} + */ + public @Deprecated JsonMessage descriptionJson; // Only text + @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(title.toString()); - writer.writeSizedString(description.toString()); + writer.writeSizedString(titleJson != null ? titleJson.toString() : title); + writer.writeSizedString(descriptionJson != null ? descriptionJson.toString() : description); writer.writeItemStack(icon); writer.writeVarInt(frameType.ordinal()); writer.writeInt(flags); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java index e733ebca3..a80b7724e 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java @@ -15,12 +15,24 @@ public class BossBarPacket implements ServerPacket { public UUID uuid; public Action action; - public String title; + public String title; // Only text public float health; public int color; public int division; public byte flags; + /** + * @deprecated Use {@link #title} + */ + public @Deprecated JsonMessage titleJson; + /** + * @deprecated Use {@link #color} + */ + public @Deprecated BarColor colorOld; + /** + * @deprecated Use {@link #division} + */ + public @Deprecated BarDivision divisionOld; @Override public void write(@NotNull BinaryWriter writer) { @@ -29,10 +41,10 @@ public class BossBarPacket implements ServerPacket { switch (action) { case ADD: - writer.writeSizedString(title); + writer.writeSizedString(titleJson != null ? titleJson.toString() : title); writer.writeFloat(health); - writer.writeVarInt(color); - writer.writeVarInt(division); + writer.writeVarInt(colorOld != null ? colorOld.ordinal() : color); + writer.writeVarInt(divisionOld != null ? divisionOld.ordinal() : division); writer.writeByte(flags); break; case REMOVE: @@ -42,11 +54,11 @@ public class BossBarPacket implements ServerPacket { writer.writeFloat(health); break; case UPDATE_TITLE: - writer.writeSizedString(title); + writer.writeSizedString(titleJson != null ? titleJson.toString() : title); break; case UPDATE_STYLE: - writer.writeVarInt(color); - writer.writeVarInt(division); + writer.writeVarInt(colorOld != null ? colorOld.ordinal() : color); + writer.writeVarInt(divisionOld != null ? divisionOld.ordinal() : division); break; case UPDATE_FLAGS: writer.writeByte(flags); 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 a498275d8..ff0e5eb54 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 @@ -3,6 +3,7 @@ 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.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; @@ -19,10 +20,19 @@ import java.util.UUID; public class ChatMessagePacket implements ServerPacket { private static final UUID NULL_UUID = new UUID(0, 0); - public String jsonMessage; + public String message; public MessageType messageType; public UUID uuid; + /** + * @deprecated Use {@link #message} + */ + public @Deprecated JsonMessage jsonMessage; + /** + * @deprecated Use {@link #messageType} + */ + public @Deprecated Position position; + @Deprecated public ChatMessagePacket(String jsonMessage, Position position, UUID uuid) { this(jsonMessage, position.asMessageType(), uuid); @@ -49,20 +59,20 @@ public class ChatMessagePacket implements ServerPacket { * 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 message 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; + public ChatMessagePacket(String message, MessageType messageType, UUID uuid) { + this.message = message; this.messageType = messageType; this.uuid = uuid; } @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(jsonMessage); - writer.writeByte((byte) (messageType == null ? 3 : messageType.ordinal())); + writer.writeSizedString(jsonMessage != null ? jsonMessage.toString() : message); + writer.writeByte((byte) (position != null ? position.ordinal() : messageType == null ? 3 : messageType.ordinal())); writer.writeUuid(uuid); } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java index 86ddb4df5..5e39bf60f 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java @@ -20,7 +20,7 @@ public class CombatEventPacket implements ServerPacket { private int duration; private int opponent; private int playerID; - private JsonMessage deathMessage; // Only text + private String deathMessage; private CombatEventPacket() { } @@ -39,7 +39,15 @@ public class CombatEventPacket implements ServerPacket { return packet; } + /** + * @deprecated Use {@link #death(Player, Entity, String)} + */ + @Deprecated public static CombatEventPacket death(Player player, Entity killer, JsonMessage message) { + return death(player, killer, message.toString()); + } + + public static CombatEventPacket death(Player player, Entity killer, String message) { CombatEventPacket packet = new CombatEventPacket(); packet.type = EventType.DEATH; packet.playerID = player.getEntityId(); @@ -64,7 +72,7 @@ public class CombatEventPacket implements ServerPacket { case DEATH: writer.writeVarInt(playerID); writer.writeInt(opponent); - writer.writeSizedString(deathMessage.toString()); + writer.writeSizedString(deathMessage); break; } } 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 48a3e3e7b..8d8293b47 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 @@ -9,6 +9,11 @@ import org.jetbrains.annotations.NotNull; public class DisconnectPacket implements ServerPacket { public String message; + /** + * @deprecated Use {@link #message} + */ + @Deprecated public JsonMessage messageJson; + /** * Creates a new disconnect packet with a given string. * @param message the message @@ -27,7 +32,7 @@ public class DisconnectPacket implements ServerPacket { @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(message); + writer.writeSizedString(messageJson != null ? messageJson.toString() : message); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/EntitySoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/EntitySoundEffectPacket.java index 8223ffb3b..a6d762771 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/EntitySoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/EntitySoundEffectPacket.java @@ -1,5 +1,6 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.sound.Sound; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.sound.SoundCategory; @@ -9,15 +10,20 @@ import org.jetbrains.annotations.NotNull; public class EntitySoundEffectPacket implements ServerPacket { public int soundId; - public SoundCategory soundCategory; + public Sound.Source soundSource; public int entityId; public float volume; public float pitch; + /** + * @deprecated Use {@link #soundSource} + */ + @Deprecated public SoundCategory soundCategory; + @Override public void write(@NotNull BinaryWriter writer) { writer.writeVarInt(soundId); - writer.writeVarInt(soundCategory.ordinal()); + writer.writeVarInt(soundCategory != null ? soundCategory.ordinal() : soundSource.ordinal()); writer.writeVarInt(entityId); writer.writeFloat(volume); writer.writeFloat(pitch); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java index 1a3a8ac0a..a1ff4560d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java @@ -63,7 +63,13 @@ public class MapDataPacket implements ServerPacket { public int type; public byte x, z; public byte direction; - public JsonMessage displayName; // Only text + public String displayName; + + /** + * @deprecated Use {@link #displayName} + */ + @Deprecated + public JsonMessage displayNameJson; // Only text private void write(BinaryWriter writer) { writer.writeVarInt(type); @@ -71,10 +77,10 @@ public class MapDataPacket implements ServerPacket { writer.writeByte(z); writer.writeByte(direction); - final boolean hasDisplayName = displayName != null; + final boolean hasDisplayName = displayName != null || displayNameJson != null; writer.writeBoolean(hasDisplayName); if (hasDisplayName) { - writer.writeSizedString(displayName.toString()); + writer.writeSizedString(displayNameJson != null ? displayNameJson.toString() : displayName); } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java index 5173054d7..b4b1114d7 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java @@ -14,10 +14,15 @@ public class NamedSoundEffectPacket implements ServerPacket { public float volume; public float pitch; + /** + * @deprecated Use {@link #soundCategory} + */ + @Deprecated public SoundCategory soundCategoryOld; + @Override public void write(@NotNull BinaryWriter writer) { writer.writeSizedString(soundName); - writer.writeVarInt(soundCategory); + writer.writeVarInt(soundCategoryOld != null ? soundCategoryOld.ordinal() : soundCategory); writer.writeInt(x * 8); writer.writeInt(y * 8); writer.writeInt(z * 8); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java index 40d420319..0587bf88f 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java @@ -75,7 +75,12 @@ public class PlayerInfoPacket implements ServerPacket { public List properties; public GameMode gameMode; public int ping; - public JsonMessage displayName; // Only text + public String displayName; + + /** + * @deprecated Use {@link #displayName} + */ + @Deprecated public JsonMessage displayNameJson; // Only text public AddPlayer(UUID uuid, String name, GameMode gameMode, int ping) { super(uuid); @@ -95,10 +100,10 @@ public class PlayerInfoPacket implements ServerPacket { writer.writeVarInt(gameMode.getId()); writer.writeVarInt(ping); - final boolean hasDisplayName = displayName != null; + final boolean hasDisplayName = displayName != null || displayNameJson != null; writer.writeBoolean(hasDisplayName); if (hasDisplayName) - writer.writeSizedString(displayName.toString()); + writer.writeSizedString(displayNameJson != null ? displayNameJson.toString() : displayName); } public static class Property { @@ -161,9 +166,22 @@ public class PlayerInfoPacket implements ServerPacket { public static class UpdateDisplayName extends PlayerInfo { - public JsonMessage displayName; // Only text + public String displayName; + /** + * @deprecated Use {@link #displayName} + */ + @Deprecated public JsonMessage displayNameJson; // Only text + + /** + * @deprecated Use {@link #UpdateDisplayName(UUID, String)} + */ + @Deprecated public UpdateDisplayName(UUID uuid, JsonMessage displayName) { + this(uuid, displayName.toString()); + } + + public UpdateDisplayName(UUID uuid, String displayName) { super(uuid); this.displayName = displayName; } @@ -173,7 +191,7 @@ public class PlayerInfoPacket implements ServerPacket { final boolean hasDisplayName = displayName != null; writer.writeBoolean(hasDisplayName); if (hasDisplayName) - writer.writeSizedString(displayName.toString()); + writer.writeSizedString(displayNameJson != null ? displayNameJson.toString() : displayName); } } 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 667491d06..b5889aa7f 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 @@ -3,6 +3,7 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; +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; @@ -16,10 +17,20 @@ public class PlayerListHeaderAndFooterPacket implements ServerPacket { public String header; public String footer; + /** + * @deprecated Use {@link #header} + */ + @Deprecated public JsonMessage headerJson; + + /** + @deprecated Use {@link #footer} + */ + @Deprecated public JsonMessage footerJson; + @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(Objects.requireNonNullElse(header, EMPTY_COMPONENT)); - writer.writeSizedString(Objects.requireNonNullElse(footer, EMPTY_COMPONENT)); + writer.writeSizedString(headerJson != null ? headerJson.toString() : Objects.requireNonNullElse(header, EMPTY_COMPONENT)); + writer.writeSizedString(footerJson != null ? footerJson.toString() : Objects.requireNonNullElse(footer, EMPTY_COMPONENT)); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java index c7750df91..7866bde00 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java @@ -21,19 +21,25 @@ public class ScoreboardObjectivePacket implements ServerPacket { /** * The text to be displayed for the score */ - public JsonMessage objectiveValue; // Only text + public String objectiveValue; // Only text /** * The type how the score is displayed */ public Type type; + /** + * @deprecated Use {@link #objectiveValue} + */ + @Deprecated + public JsonMessage objectiveValueJson; + @Override public void write(@NotNull BinaryWriter writer) { writer.writeSizedString(objectiveName); writer.writeByte(mode); if (mode == 0 || mode == 2) { - writer.writeSizedString(objectiveValue.toString()); + writer.writeSizedString(objectiveValueJson != null ? objectiveValueJson.toString() : objectiveValue); writer.writeVarInt(type.ordinal()); } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java index 4db791a6d..60642b809 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java @@ -17,6 +17,11 @@ public class SoundEffectPacket implements ServerPacket { public float volume; public float pitch; + /** + * @deprecated Use {@link #soundCategory} + */ + @Deprecated public SoundCategory soundCategoryOld; + /** * @deprecated Use variables */ @@ -37,7 +42,7 @@ public class SoundEffectPacket implements ServerPacket { @Override public void write(@NotNull BinaryWriter writer) { writer.writeVarInt(soundId); - writer.writeVarInt(soundCategory); + writer.writeVarInt(soundCategoryOld != null ? soundCategoryOld.ordinal() : soundCategory); writer.writeInt(x * 8); writer.writeInt(y * 8); writer.writeInt(z * 8); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java index 1fdb12d12..8b7a7d2d6 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java @@ -24,7 +24,7 @@ public class TabCompletePacket implements ServerPacket { writer.writeSizedString(match.match); writer.writeBoolean(match.hasTooltip); if (match.hasTooltip) - writer.writeSizedString(match.tooltip.toString()); + writer.writeSizedString(match.tooltipJson != null ? match.tooltipJson.toString() : match.tooltip); } } @@ -36,7 +36,12 @@ public class TabCompletePacket implements ServerPacket { public static class Match { public String match; public boolean hasTooltip; - public JsonMessage tooltip; // Only text + public String tooltip; + + /** + * @deprecated Use {@link #tooltip} + */ + @Deprecated public String tooltipJson; } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java index 188c11271..65f7b930f 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java @@ -23,7 +23,7 @@ public class TeamsPacket implements ServerPacket { /** * The display name for the team */ - public JsonMessage teamDisplayName; + public String teamDisplayName; /** * The friendly flags to */ @@ -43,16 +43,29 @@ public class TeamsPacket implements ServerPacket { /** * The prefix of the team */ - public JsonMessage teamPrefix; + public String teamPrefix; /** * The suffix of the team */ - public JsonMessage teamSuffix; + public String teamSuffix; /** * An array with all entities in the team */ public String[] entities; + /** + * @deprecated Use {@link #teamDisplayName} + */ + @Deprecated public JsonMessage teamDisplayNameJson; + /** + @deprecated Use {@link #teamPrefix} + */ + @Deprecated public JsonMessage teamPrefixJson; + /** + @deprecated Use {@link #teamSuffix} + */ + @Deprecated public JsonMessage teamSuffixJson; + /** * Writes data into the {@link BinaryWriter} * @@ -66,13 +79,13 @@ public class TeamsPacket implements ServerPacket { switch (action) { case CREATE_TEAM: case UPDATE_TEAM_INFO: - writer.writeSizedString(this.teamDisplayName.toString()); + writer.writeSizedString(this.teamDisplayNameJson != null ? this.teamDisplayNameJson.toString() : this.teamDisplayName); writer.writeByte(this.friendlyFlags); writer.writeSizedString(this.nameTagVisibility.getIdentifier()); writer.writeSizedString(this.collisionRule.getIdentifier()); writer.writeVarInt(this.teamColor); - writer.writeSizedString(this.teamPrefix.toString()); - writer.writeSizedString(this.teamSuffix.toString()); + writer.writeSizedString(this.teamPrefixJson != null ? this.teamPrefixJson.toString() : this.teamPrefix); + writer.writeSizedString(this.teamSuffixJson != null ? this.teamSuffixJson.toString() : this.teamSuffix); break; case REMOVE_TEAM: 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 51b8e7047..98b2f1961 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,6 +1,5 @@ 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.MinecraftServer; import net.minestom.server.network.packet.server.ServerPacket; diff --git a/src/main/java/net/minestom/server/network/player/PlayerConnection.java b/src/main/java/net/minestom/server/network/player/PlayerConnection.java index b98236b90..77a63c697 100644 --- a/src/main/java/net/minestom/server/network/player/PlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/PlayerConnection.java @@ -1,8 +1,8 @@ package net.minestom.server.network.player; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; -import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; import net.minestom.server.entity.Player; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.listener.manager.ServerPacketConsumer; @@ -29,7 +29,7 @@ public abstract class PlayerConnection { private boolean online; // Text used to kick client sending too many packets - private static final ColoredText rateLimitKickMessage = ColoredText.of(ChatColor.RED + "Too Many Packets"); + private static final Component rateLimitKickMessage = Component.text("Too Many Packets", NamedTextColor.RED); //Connection Stats private final AtomicInteger packetCounter = new AtomicInteger(0); From 7351b9fdd39df35044bb48c41694bf73ada08148 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Wed, 3 Mar 2021 16:32:51 +0000 Subject: [PATCH 076/364] Implement global translation system --- .../server/adventure/Localizable.java | 27 ++++++ .../adventure/SerializationManager.java | 93 +++++++++++++++++-- .../net/minestom/server/entity/Player.java | 19 +++- 3 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/Localizable.java diff --git a/src/main/java/net/minestom/server/adventure/Localizable.java b/src/main/java/net/minestom/server/adventure/Localizable.java new file mode 100644 index 000000000..d854823ae --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/Localizable.java @@ -0,0 +1,27 @@ +package net.minestom.server.adventure; + +import org.jetbrains.annotations.Nullable; + +import java.util.Locale; + +/** + * Represents something which can have a locale. + */ +public interface Localizable { + + /** + * Gets the locale. + * + * @return the locale, or {@code null} if they do not have a locale set + */ + @Nullable Locale getLocale(); + + /** + * Sets the locale. This can be set to {@code null} to remove a locale registration. + * + * @param locale the new locale + */ + default void setLocale(@Nullable Locale locale) { + throw new UnsupportedOperationException("You cannot set the locale for this object!"); + } +} diff --git a/src/main/java/net/minestom/server/adventure/SerializationManager.java b/src/main/java/net/minestom/server/adventure/SerializationManager.java index fb8051990..50d0b328a 100644 --- a/src/main/java/net/minestom/server/adventure/SerializationManager.java +++ b/src/main/java/net/minestom/server/adventure/SerializationManager.java @@ -1,9 +1,14 @@ package net.minestom.server.adventure; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.translation.GlobalTranslator; +import net.kyori.adventure.translation.TranslationRegistry; +import net.kyori.adventure.translation.Translator; import org.jetbrains.annotations.NotNull; +import java.util.Locale; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Function; @@ -15,14 +20,20 @@ import java.util.function.Function; * implementation of Minestom could change this to the plain component serializer. *

* This manager also provides the ability to wrap the serializer in a renderer that - * performs operations on each component before the final serialization. - * - * @see #setSerializer(Function) (Function) - * @see #addRenderer(Function) + * performs operations on each component before the final serialization. This can be + * done using {@link #addRenderer(Function)} and {@link #removeRenderer(Function)}. + *

+ * Finally, this manager also performs translation on all messages and the {@code serialize} + * method should be used when converting {@link Component}s into strings. This allows for + * messages with {@link TranslatableComponent} to be automatically translated into the locale + * of specific players, or other elements which implement {@link Localizable}. To add your + * own translations, use {@link GlobalTranslator#addSource(Translator)} with a + * {@link TranslationRegistry} or your own implementation of {@link Translator}. */ public class SerializationManager { private final Set> renderers = new CopyOnWriteArraySet<>(); private Function serializer = component -> GsonComponentSerializer.gson().serialize(component); + private Locale defaultLocale = Locale.US; /** * Gets the root serializer that is used to convert Components into Strings. @@ -43,6 +54,28 @@ public class SerializationManager { this.serializer = serializer; } + /** + * Gets the default locale used to translate {@link TranslatableComponent} if + * serialized using {@link #serialize(Component)} or when {@link #serialize(Component, Localizable)} + * is used but no translation is found. Note that this is just shorthand for + * + * @return the default locale + */ + public @NotNull Locale getDefaultLocale() { + return defaultLocale; + } + + /** + * Sets the default locale used to translate {@link TranslatableComponent} if + * serialized using {@link #serialize(Component)} or when {@link #serialize(Component, Localizable)} + * is used but no translation is found. + * + * @param defaultLocale the new default locale + */ + public void setDefaultLocale(@NotNull Locale defaultLocale) { + this.defaultLocale = defaultLocale; + } + /** * Adds a renderer that will be applied to each serializer. The order in which * each renderer will be applied is arbitrary. If you want control over the order @@ -71,17 +104,65 @@ public class SerializationManager { } /** - * Serializes a component into a String using the current serializer. + * Gets the global translator object used by this manager. This is just shorthand for + * {@link GlobalTranslator#get()}. + * + * @return the global translator + */ + public @NotNull GlobalTranslator getTranslator() { + return GlobalTranslator.get(); + } + + /** + * Serializes a component into a String using the current serializer. Any registered + * renderers are applied first, followed by the global translator. Finally, the + * serializer set with {@link #setSerializer(Function)} is used to convert the + * component into a String. * * @param component the component * * @return the serialized string */ - public String serialize(Component component) { + public @NotNull String serialize(@NotNull Component component) { + return this.serialize(component, this.defaultLocale); + } + + /** + * Serializes a component into a String using the current serializer. Any registered + * renderers are applied first, followed by the global translator. Finally, the + * serializer set with {@link #setSerializer(Function)} is used to convert the + * component into a String. + * + * @param component the component + * @param localizable a localizable object used to translate components + * + * @return the serialized string + */ + public @NotNull String serialize(@NotNull Component component, @NotNull Localizable localizable) { + return this.serialize(component, localizable.getLocale()); + } + + /** + * Serializes a component into a String using the current serializer. Any registered + * renderers are applied first, followed by the global translator. Finally, the + * serializer set with {@link #setSerializer(Function)} is used to convert the + * component into a String. + * + * @param component the component + * @param locale the locale used to translate components + * + * @return the serialized string + */ + public @NotNull String serialize(@NotNull Component component, @NotNull Locale locale) { + // apply renderers for (Function renderer : this.renderers) { component = renderer.apply(component); } + // apply translation + component = GlobalTranslator.render(component, locale); + + // apply serialisation return this.serializer.apply(component); } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 6e879a84b..258f0d0b7 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -12,6 +12,7 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.AdvancementTab; import net.minestom.server.attribute.Attribute; import net.minestom.server.adventure.AdventureUtils; +import net.minestom.server.adventure.Localizable; import net.minestom.server.attribute.AttributeInstance; import net.minestom.server.bossbar.BossBar; import net.minestom.server.chat.ChatParser; @@ -84,7 +85,7 @@ import java.util.concurrent.atomic.AtomicInteger; *

* You can easily create your own implementation of this and use it with {@link ConnectionManager#setPlayerProvider(PlayerProvider)}. */ -public class Player extends LivingEntity implements CommandSender { +public class Player extends LivingEntity implements CommandSender, Localizable { private long lastKeepAlive; private boolean answerKeepAlive; @@ -2663,6 +2664,22 @@ public class Player extends LivingEntity implements CommandSender { inventory.setBoots(itemStack); } + @Override + public Locale getLocale() { + return settings.locale == null ? null : Locale.forLanguageTag(settings.locale); + } + + /** + * Sets the player's locale. This will only set the locale of the player as it + * is stored in the server. This will also be reset if the settings are refreshed. + * + * @param locale the new locale + */ + @Override + public void setLocale(@Nullable Locale locale) { + settings.locale = locale == null ? null : locale.toLanguageTag(); + } + /** * Represents the main or off hand of the player. */ From 82820bc40b415034b90f54448a9ed54e624edd9c Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Wed, 3 Mar 2021 17:36:52 +0000 Subject: [PATCH 077/364] Remove WrapperAudience --- .../java/net/minestom/server/Viewable.java | 4 +-- .../server/adventure/WrapperAudience.java | 27 ------------------- 2 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 src/main/java/net/minestom/server/adventure/WrapperAudience.java diff --git a/src/main/java/net/minestom/server/Viewable.java b/src/main/java/net/minestom/server/Viewable.java index 861930804..d7c3de2dd 100644 --- a/src/main/java/net/minestom/server/Viewable.java +++ b/src/main/java/net/minestom/server/Viewable.java @@ -1,8 +1,6 @@ package net.minestom.server; import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.ForwardingAudience; -import net.minestom.server.adventure.WrapperAudience; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.utils.PacketUtils; @@ -92,6 +90,6 @@ public interface Viewable { * @return the audience */ default @NotNull Audience asAudience() { - return new WrapperAudience(this.getViewers()); + return Audience.audience(this.getViewers()); } } diff --git a/src/main/java/net/minestom/server/adventure/WrapperAudience.java b/src/main/java/net/minestom/server/adventure/WrapperAudience.java deleted file mode 100644 index c0ca3c400..000000000 --- a/src/main/java/net/minestom/server/adventure/WrapperAudience.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.minestom.server.adventure; - -import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.audience.ForwardingAudience; -import org.jetbrains.annotations.NotNull; - -/** - * Implementation of {@link ForwardingAudience} that acts as a wrapper around another - * iterable collection of audiences. - */ -public class WrapperAudience implements ForwardingAudience { - private final Iterable audiences; - - /** - * Creates a new wrapper audience. - * - * @param audiences the audiences to wrap - */ - public WrapperAudience(@NotNull Iterable audiences) { - this.audiences = audiences; - } - - @Override - public @NotNull Iterable audiences() { - return this.audiences; - } -} From 522367dd7841cd20b45bf0700aee76a4945ae2af Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Wed, 3 Mar 2021 19:27:33 +0000 Subject: [PATCH 078/364] Begin removing deprecated usages --- .../server/advancements/Advancement.java | 88 +++++++- .../server/advancements/AdvancementRoot.java | 25 +++ .../notifications/Notification.java | 40 +++- .../notifications/NotificationCenter.java | 6 +- .../adventure/SerializationManager.java | 18 +- .../server/benchmark/BenchmarkManager.java | 27 ++- .../net/minestom/server/chat/ChatColor.java | 15 ++ .../java/net/minestom/server/color/Color.java | 205 ++++++++++++++++++ .../net/minestom/server/color/DyeColor.java | 53 +++++ .../builder/arguments/ArgumentType.java | 11 +- .../arguments/minecraft/ArgumentColor.java | 2 + .../minecraft/ArgumentTextColor.java | 65 ++++++ .../minecraft/ArgumentTextDecoration.java | 40 ++++ .../net/minestom/server/entity/Entity.java | 26 ++- .../minestom/server/entity/LivingEntity.java | 8 +- .../net/minestom/server/entity/Metadata.java | 18 ++ .../net/minestom/server/entity/Player.java | 72 +++--- .../server/entity/damage/DamageType.java | 32 ++- .../server/entity/hologram/Hologram.java | 68 +++++- .../server/entity/metadata/EntityMeta.java | 23 +- .../minecart/CommandBlockMinecartMeta.java | 22 +- .../server/event/player/PlayerChatEvent.java | 18 +- .../server/event/player/PlayerDeathEvent.java | 65 +++++- .../net/minestom/server/item/ItemDisplay.java | 52 ++++- .../net/minestom/server/item/ItemStack.java | 61 +++++- .../server/item/firework/FireworkEffect.java | 31 ++- .../item/metadata/LeatherArmorMeta.java | 75 ++++--- .../server/item/metadata/MapMeta.java | 41 +++- .../server/item/metadata/PotionMeta.java | 46 ++-- .../packet/server/play/SoundEffectPacket.java | 13 ++ .../net/minestom/server/utils/MathUtils.java | 8 + 31 files changed, 1086 insertions(+), 188 deletions(-) create mode 100644 src/main/java/net/minestom/server/color/Color.java create mode 100644 src/main/java/net/minestom/server/color/DyeColor.java create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextDecoration.java diff --git a/src/main/java/net/minestom/server/advancements/Advancement.java b/src/main/java/net/minestom/server/advancements/Advancement.java index 5dbc64bfd..9466d679f 100644 --- a/src/main/java/net/minestom/server/advancements/Advancement.java +++ b/src/main/java/net/minestom/server/advancements/Advancement.java @@ -1,5 +1,7 @@ package net.minestom.server.advancements; +import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Player; import net.minestom.server.item.ItemStack; @@ -23,8 +25,8 @@ public class Advancement { private boolean achieved; - private JsonMessage title; - private JsonMessage description; + private Component title; + private Component description; private ItemStack icon; @@ -42,9 +44,29 @@ public class Advancement { // Packet private AdvancementsPacket.Criteria criteria; + /** + * @deprecated Use {@link #Advancement(Component, Component, ItemStack, FrameType, float, float)} + */ + @Deprecated public Advancement(@NotNull JsonMessage title, JsonMessage description, @NotNull ItemStack icon, @NotNull FrameType frameType, float x, float y) { + this(title.asComponent(), description.asComponent(), icon, frameType, x, y); + } + + /** + * @deprecated Use {@link #Advancement(Component, Component, Material, FrameType, float, float)} + */ + @Deprecated + public Advancement(@NotNull JsonMessage title, @NotNull JsonMessage description, + @NotNull Material icon, @NotNull FrameType frameType, + float x, float y) { + this(title, description, new ItemStack(icon, (byte) 1), frameType, x, y); + } + + public Advancement(@NotNull Component title, Component description, + @NotNull ItemStack icon, @NotNull FrameType frameType, + float x, float y) { this.title = title; this.description = description; this.icon = icon; @@ -53,7 +75,7 @@ public class Advancement { this.y = y; } - public Advancement(@NotNull JsonMessage title, @NotNull JsonMessage description, + public Advancement(@NotNull Component title, @NotNull Component description, @NotNull Material icon, @NotNull FrameType frameType, float x, float y) { this(title, description, new ItemStack(icon, (byte) 1), frameType, x, y); @@ -94,14 +116,24 @@ public class Advancement { this.tab = tab; } + /** + * Gets the title of the advancement. + * + * @return the title + */ + public Component getTitle() { + return title; + } + /** * Gets the title of the advancement. * * @return the advancement title + * @deprecated Use {@link #getTitle()} */ @NotNull - public JsonMessage getTitle() { - return title; + public JsonMessage getTitleJson() { + return JsonMessage.fromComponent(title); } /** @@ -109,31 +141,67 @@ public class Advancement { * * @param title the new title */ - public void setTitle(@NotNull JsonMessage title) { + public void setTitle(@NotNull Component title) { this.title = title; update(); } + /** + * Changes the advancement title. + * + * @param title the new title + * @deprecated Use {@link #setTitle(Component)} + */ + @Deprecated + public void setTitle(@NotNull JsonMessage title) { + this.title = title.asComponent(); + update(); + } + /** * Gets the description of the advancement. * * @return the description title */ @NotNull - public JsonMessage getDescription() { + public Component getDescription() { return description; } + /** + * Gets the description of the advancement. + * + * @return the description title + * @deprecated Use {@link #getDescription()} + */ + @NotNull + @Deprecated + public JsonMessage getDescriptionJson() { + return JsonMessage.fromComponent(description); + } + /** * Changes the description title. * * @param description the new description */ - public void setDescription(@NotNull JsonMessage description) { + public void setDescription(@NotNull Component description) { this.description = description; update(); } + /** + * Changes the description title. + * + * @param description the new description + * @deprecated Use {@link #setDescription(Component)} + */ + @Deprecated + public void setDescription(@NotNull JsonMessage description) { + this.description = description.asComponent(); + update(); + } + /** * Gets the advancement icon. * @@ -306,8 +374,8 @@ public class Advancement { AdvancementsPacket.DisplayData displayData = new AdvancementsPacket.DisplayData(); displayData.x = x; displayData.y = y; - displayData.title = title; - displayData.description = description; + displayData.title = MinecraftServer.getSerializationManager().serialize(title); + displayData.description = MinecraftServer.getSerializationManager().serialize(description); displayData.icon = icon; displayData.frameType = frameType; displayData.flags = getFlags(); diff --git a/src/main/java/net/minestom/server/advancements/AdvancementRoot.java b/src/main/java/net/minestom/server/advancements/AdvancementRoot.java index b4f1cd92e..3aa224d30 100644 --- a/src/main/java/net/minestom/server/advancements/AdvancementRoot.java +++ b/src/main/java/net/minestom/server/advancements/AdvancementRoot.java @@ -1,5 +1,6 @@ package net.minestom.server.advancements; +import net.kyori.adventure.text.Component; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; import net.minestom.server.item.ItemStack; @@ -15,6 +16,10 @@ import org.jetbrains.annotations.Nullable; */ public class AdvancementRoot extends Advancement { + /** + * @deprecated Use {@link #AdvancementRoot(Component, Component, ItemStack, FrameType, float, float, String)} + */ + @Deprecated public AdvancementRoot(@NotNull JsonMessage title, @NotNull JsonMessage description, @NotNull ItemStack icon, @NotNull FrameType frameType, float x, float y, @@ -23,6 +28,10 @@ public class AdvancementRoot extends Advancement { setBackground(background); } + /** + * @deprecated Use {@link #AdvancementRoot(Component, Component, Material, FrameType, float, float, String)} + */ + @Deprecated public AdvancementRoot(@NotNull JsonMessage title, @NotNull JsonMessage description, @NotNull Material icon, FrameType frameType, float x, float y, @@ -31,4 +40,20 @@ public class AdvancementRoot extends Advancement { setBackground(background); } + public AdvancementRoot(@NotNull Component title, @NotNull Component description, + @NotNull ItemStack icon, @NotNull FrameType frameType, + float x, float y, + @Nullable String background) { + super(title, description, icon, frameType, x, y); + setBackground(background); + } + + public AdvancementRoot(@NotNull Component title, @NotNull Component description, + @NotNull Material icon, FrameType frameType, + float x, float y, + @Nullable String background) { + super(title, description, icon, frameType, x, y); + setBackground(background); + } + } diff --git a/src/main/java/net/minestom/server/advancements/notifications/Notification.java b/src/main/java/net/minestom/server/advancements/notifications/Notification.java index 492ccd819..8c7f40b5b 100644 --- a/src/main/java/net/minestom/server/advancements/notifications/Notification.java +++ b/src/main/java/net/minestom/server/advancements/notifications/Notification.java @@ -1,5 +1,6 @@ package net.minestom.server.advancements.notifications; +import net.kyori.adventure.text.Component; import net.minestom.server.advancements.FrameType; import net.minestom.server.chat.JsonMessage; import net.minestom.server.item.ItemStack; @@ -11,20 +12,46 @@ import org.jetbrains.annotations.NotNull; */ public class Notification { - private final JsonMessage title; + private final Component title; private final FrameType frameType; private final ItemStack icon; + /** + * @deprecated Use {@link #Notification(Component, FrameType, ItemStack)} + */ + @Deprecated public Notification(@NotNull JsonMessage title, @NotNull FrameType frameType, @NotNull ItemStack icon) { + this(title.asComponent(), frameType, icon); + } + + /** + * @deprecated Use {@link #Notification(Component, FrameType, Material)} + */ + @Deprecated + public Notification(@NotNull JsonMessage title, @NotNull FrameType frameType, @NotNull Material icon) { + this(title.asComponent(), frameType, icon); + } + + public Notification(@NotNull Component title, @NotNull FrameType frameType, @NotNull Material icon) { + this(title, frameType, new ItemStack(icon, (byte) 1)); + } + + public Notification(@NotNull Component title, @NotNull FrameType frameType, @NotNull ItemStack icon) { this.title = title; this.frameType = frameType; this.icon = icon; } - public Notification(@NotNull JsonMessage title, @NotNull FrameType frameType, @NotNull Material icon) { - this.title = title; - this.frameType = frameType; - this.icon = new ItemStack(icon, (byte) 1); + /** + * Gets the title of the notification. + * + * @return the notification title + * + * @deprecated Use {@link #getTitle()} + */ + @NotNull + public JsonMessage getTitleJson() { + return JsonMessage.fromComponent(title); } /** @@ -32,8 +59,7 @@ public class Notification { * * @return the notification title */ - @NotNull - public JsonMessage getTitle() { + public Component getTitle() { return title; } diff --git a/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java b/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java index c7f453058..836221493 100644 --- a/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java +++ b/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java @@ -1,6 +1,6 @@ package net.minestom.server.advancements.notifications; -import net.minestom.server.chat.ColoredText; +import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.AdvancementsPacket; import net.minestom.server.network.player.PlayerConnection; @@ -82,9 +82,9 @@ public class NotificationCenter { // Setup display data for the advancement AdvancementsPacket.DisplayData displayData = new AdvancementsPacket.DisplayData(); { - displayData.title = notification.getTitle(); + displayData.title = MinecraftServer.getSerializationManager().serialize(notification.getTitle()); // Description is required, but never shown/seen so, small Easter egg. - displayData.description = ColoredText.of("Articdive was here. #Minestom"); + displayData.description = "Articdive was here. #Minestom"; displayData.icon = notification.getIcon(); displayData.frameType = notification.getFrameType(); displayData.flags = 0x6; diff --git a/src/main/java/net/minestom/server/adventure/SerializationManager.java b/src/main/java/net/minestom/server/adventure/SerializationManager.java index 50d0b328a..01862c10e 100644 --- a/src/main/java/net/minestom/server/adventure/SerializationManager.java +++ b/src/main/java/net/minestom/server/adventure/SerializationManager.java @@ -6,9 +6,12 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.translation.GlobalTranslator; import net.kyori.adventure.translation.TranslationRegistry; import net.kyori.adventure.translation.Translator; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Locale; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Function; @@ -123,7 +126,8 @@ public class SerializationManager { * * @return the serialized string */ - public @NotNull String serialize(@NotNull Component component) { + @Contract("null -> null") + public @Nullable String serialize(@Nullable Component component) { return this.serialize(component, this.defaultLocale); } @@ -138,8 +142,9 @@ public class SerializationManager { * * @return the serialized string */ - public @NotNull String serialize(@NotNull Component component, @NotNull Localizable localizable) { - return this.serialize(component, localizable.getLocale()); + @Contract("null, _ -> null") + public @Nullable String serialize(@Nullable Component component, @NotNull Localizable localizable) { + return this.serialize(component, Objects.requireNonNullElse(localizable.getLocale(), this.defaultLocale)); } /** @@ -153,7 +158,12 @@ public class SerializationManager { * * @return the serialized string */ - public @NotNull String serialize(@NotNull Component component, @NotNull Locale locale) { + @Contract("null, _ -> null") + public @Nullable String serialize(@Nullable Component component, @NotNull Locale locale) { + if (component == null) { + return null; + } + // apply renderers for (Function renderer : this.renderers) { component = renderer.apply(component); diff --git a/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java b/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java index 715cd7cdf..abfc8a524 100644 --- a/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java +++ b/src/main/java/net/minestom/server/benchmark/BenchmarkManager.java @@ -2,8 +2,10 @@ package net.minestom.server.benchmark; import it.unimi.dsi.fastutil.longs.Long2LongMap; import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; -import net.minestom.server.chat.ChatColor; import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.time.UpdateOption; import net.minestom.server.utils.validate.Check; @@ -106,20 +108,25 @@ public final class BenchmarkManager { @NotNull public String getCpuMonitoringMessage() { Check.stateCondition(!enabled, "CPU monitoring is only possible when the benchmark manager is enabled."); - StringBuilder benchmarkMessage = new StringBuilder(); + TextComponent.Builder benchmarkMessage = Component.text(); for (Map.Entry resultEntry : resultMap.entrySet()) { final String name = resultEntry.getKey(); final ThreadResult result = resultEntry.getValue(); - benchmarkMessage.append(ChatColor.GRAY).append(name); - benchmarkMessage.append(": "); - benchmarkMessage.append(ChatColor.YELLOW.toString()).append(MathUtils.round(result.getCpuPercentage(), 2)).append("% CPU "); - benchmarkMessage.append(ChatColor.RED.toString()).append(MathUtils.round(result.getUserPercentage(), 2)).append("% USER "); - benchmarkMessage.append(ChatColor.PINK.toString()).append(MathUtils.round(result.getBlockedPercentage(), 2)).append("% BLOCKED "); - benchmarkMessage.append(ChatColor.BRIGHT_GREEN.toString()).append(MathUtils.round(result.getWaitedPercentage(), 2)).append("% WAITED "); - benchmarkMessage.append("\n"); + benchmarkMessage.append(Component.text(name, NamedTextColor.GRAY)); + benchmarkMessage.append(Component.text(": ")); + benchmarkMessage.append(Component.text(MathUtils.round(result.getCpuPercentage(), 2), NamedTextColor.YELLOW)); + benchmarkMessage.append(Component.text("% CPU ")); + benchmarkMessage.append(Component.text(MathUtils.round(result.getUserPercentage(), 2), NamedTextColor.RED)); + benchmarkMessage.append(Component.text("% USER ")); + benchmarkMessage.append(Component.text(MathUtils.round(result.getBlockedPercentage(), 2), NamedTextColor.LIGHT_PURPLE)); + benchmarkMessage.append(Component.text("% BLOCKED ")); + benchmarkMessage.append(Component.text(MathUtils.round(result.getWaitedPercentage(), 2), NamedTextColor.GREEN)); + benchmarkMessage.append(Component.text("% WAITED ")); + benchmarkMessage.append(Component.newline()); } - return benchmarkMessage.toString(); + + return MinecraftServer.getSerializationManager().serialize(benchmarkMessage.build()); } private void refreshData() { diff --git a/src/main/java/net/minestom/server/chat/ChatColor.java b/src/main/java/net/minestom/server/chat/ChatColor.java index c53a4d0ee..e0246712f 100644 --- a/src/main/java/net/minestom/server/chat/ChatColor.java +++ b/src/main/java/net/minestom/server/chat/ChatColor.java @@ -4,7 +4,10 @@ import it.unimi.dsi.fastutil.chars.Char2ObjectMap; import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.kyori.adventure.text.format.TextColor; +import net.minestom.server.color.Color; import net.minestom.server.utils.validate.Check; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -273,6 +276,18 @@ public final class ChatColor { return id; } + /** + * Gets the Adventure text color from this chat color. + * @return the text color + */ + public @NotNull TextColor asTextColor() { + return TextColor.color(red, blue, green); + } + + public @NotNull Color asColor() { + return new Color(red, green, blue); + } + @NotNull @Override public String toString() { diff --git a/src/main/java/net/minestom/server/color/Color.java b/src/main/java/net/minestom/server/color/Color.java new file mode 100644 index 000000000..fcee18a31 --- /dev/null +++ b/src/main/java/net/minestom/server/color/Color.java @@ -0,0 +1,205 @@ +package net.minestom.server.color; + +import net.kyori.adventure.text.format.TextColor; +import net.minestom.server.chat.ChatColor; +import org.apache.commons.lang3.Validate; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A general purpose class for representing colors. + */ +public class Color { + private static final int BIT_MASK = 0xff; + + private int red, green, blue; + + /** + * Creates a color from an integer. This is done by reading each color component + * from the lowest order 24 bits of the integer, and creating a color from those + * components. + * + * @param rgb the integer + */ + public Color(int rgb) { + this((rgb >> 16) & BIT_MASK, (rgb >> 8) & BIT_MASK, rgb & BIT_MASK); + } + + /** + * Creates a color from an Adventure text color. + * + * @param textColor the text color + */ + public Color(TextColor textColor) { + this(textColor.red(), textColor.blue(), textColor.green()); + } + + /** + * Creates a color from red, green, and blue components. + * + * @param red the red component + * @param green the green component + * @param blue the blue component + * + * @throws IllegalArgumentException if any component value is not between 0-255 (inclusive) + */ + public Color(int red, int green, int blue) { + Validate.isTrue(red >= 0 && red <= 255, "Red is not between 0-255: ", red); + Validate.isTrue(green >= 0 && green <= 255, "Green is not between 0-255: ", green); + Validate.isTrue(blue >= 0 && blue <= 255, "Blue is not between 0-255: ", blue); + + this.red = red; + this.green = green; + this.blue = blue; + } + + /** + * Gets the red component. + * + * @return red component, between 0-255 (inclusive) + */ + public int getRed() { + return this.red; + } + + /** + * Creates a new Color object with specified component + * + * @param red the red component, from 0 to 255 + */ + public void setRed(int red) { + Validate.isTrue(red >= 0 && red <= 255, "Red is not between 0-255: ", red); + this.red = red; + } + + /** + * Gets the green component + * + * @return green component, from 0 to 255 + */ + public int getGreen() { + return this.green; + } + + /** + * Creates a new Color object with specified component + * + * @param green the red component, from 0 to 255 + */ + public void setGreen(int green) { + Validate.isTrue(green >= 0 && green <= 255, "Green is not between 0-255: ", green); + this.green = green; + } + + /** + * Gets the blue component + * + * @return blue component, from 0 to 255 + */ + public int getBlue() { + return this.blue; + } + + /** + * Sets the blue component of this color. + * + * @param blue the red component, from 0 to 255 + */ + public void setBlue(int blue) { + Validate.isTrue(blue >= 0 && blue <= 255, "Blue is not between 0-255: ", blue); + this.blue = blue; + } + + /** + * Gets the color as an RGB integer. + * + * @return An integer representation of this color, as 0xRRGGBB + */ + public int asRGB() { + int rgb = red; + rgb = (rgb << 8) + green; + return (rgb << 8) + blue; + } + + /** + * Shorthand method for {@link #mixWith(Color...)}. This method converts each dye + * color to a color and then mixes this color with the new colors. + * + * @param dyeColors the dye colors + */ + public void mixWith(@NotNull DyeColor... dyeColors) { + Validate.noNullElements(dyeColors, "Colors cannot be null"); + + Color[] colors = new Color[dyeColors.length]; + for (int i = 0; i < colors.length; i++) { + colors[i] = dyeColors[i].getColor(); + } + + this.mixWith(colors); + } + + /** + * Mixes this color with a series of other colors, as if they were combined in a + * crafting table. This function works out the average of each RGB component and then + * multiplies the components by a scale factor that is calculated from the average + * of all maximum values divided by the maximum of each average value. This is how + * Minecraft mixes colors. + * + * @param colors the colors + */ + public void mixWith(@NotNull Color... colors) { + Validate.noNullElements(colors, "Colors cannot be null"); + + // store the current highest component + int max = Math.max(Math.max(this.red, this.green), this.blue); + + // now combine all of the color components, adding to the max + for (Color color : colors) { + this.red += color.getRed(); + this.green += color.getGreen(); + this.blue += color.getBlue(); + max += Math.max(Math.max(color.getRed(), color.getGreen()), color.getBlue()); + } + + // work out the averages + float count = colors.length + 1; + float averageRed = this.red / count; + float averageGreen = this.green / count; + float averageBlue = this.blue / count; + float averageMax = max / count; + + // work out the scale factor + float maximumOfAverages = Math.max(Math.max(averageRed, averageGreen), averageBlue); + float gainFactor = averageMax / maximumOfAverages; + + // round and multiply + this.red = Math.round(averageRed * gainFactor); + this.blue = Math.round(averageBlue * gainFactor); + this.green = Math.round(averageGreen * gainFactor); + } + + /** + * Gets the ChatColor representation of this color. + * + * @return the chat color + * @deprecated ChatColor is deprecated and should not be used + */ + @Deprecated + public ChatColor asLegacyChatColor() { + return ChatColor.fromRGB((byte) red, (byte) blue, (byte) green); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Color color = (Color) o; + return red == color.red && green == color.green && blue == color.blue; + } + + @Override + public int hashCode() { + return Objects.hash(red, green, blue); + } +} diff --git a/src/main/java/net/minestom/server/color/DyeColor.java b/src/main/java/net/minestom/server/color/DyeColor.java new file mode 100644 index 000000000..835326837 --- /dev/null +++ b/src/main/java/net/minestom/server/color/DyeColor.java @@ -0,0 +1,53 @@ +package net.minestom.server.color; + +import org.jetbrains.annotations.NotNull; + +/** + * Color values for dyes, wool and cloth items. + */ +public enum DyeColor { + WHITE(new Color(0xF9FFFE), new Color(0xF0F0F0)), + ORANGE(new Color(0xF9801D), new Color(0xEB8844)), + MAGENTA(new Color(0xC74EBD), new Color(0xC354CD)), + LIGHT_BLUE(new Color(0x3AB3DA), new Color(0x6689D3)), + YELLOW(new Color(0xFED83D), new Color(0xDECF2A)), + LIME(new Color(0x80C71F), new Color(0x41CD34)), + PINK(new Color(0xF38BAA), new Color(0xD88198)), + GRAY(new Color(0x474F52), new Color(0x434343)), + LIGHT_GRAY(new Color(0x9D9D97), new Color(0xABABAB)), + CYAN(new Color(0x169C9C), new Color(0x287697)), + PURPLE(new Color(0x8932B8), new Color(0x7B2FBE)), + BLUE(new Color(0x3C44AA), new Color(0x253192)), + BROWN(new Color(0x835432), new Color(0x51301A)), + GREEN(new Color(0x5E7C16), new Color(0x3B511A)), + RED(new Color(0xB02E26), new Color(0xB3312C)), + BLACK(new Color(0x1D1D21), new Color(0x1E1B1B)); + + private final Color color; + private final Color firework; + + DyeColor(Color color, Color firework) { + this.color = color; + this.firework = firework; + } + + /** + * Gets the color that this dye represents. + * + * @return The {@link Color} that this dye represents + */ + @NotNull + public Color getColor() { + return this.color; + } + + /** + * Gets the firework color that this dye represents. + * + * @return The {@link Color} that this dye represents + */ + @NotNull + public Color getFireworkColor() { + return this.firework; + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java index bf244ba8d..7322c5d38 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java @@ -107,12 +107,21 @@ public class ArgumentType { // Minecraft specific arguments /** - * @see ArgumentColor + * @deprecated Use {@link #TextColor} for colors and {@link #TextDecoration} for styles. */ + @Deprecated public static ArgumentColor Color(@NotNull String id) { return new ArgumentColor(id); } + public static ArgumentTextColor TextColor(@NotNull String id) { + return new ArgumentTextColor(id); + } + + public static ArgumentTextDecoration TextDecoration(@NotNull String id) { + return new ArgumentTextDecoration(id); + } + /** * @see ArgumentTime */ diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java index af3f72554..da8a9c320 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java @@ -11,7 +11,9 @@ import org.jetbrains.annotations.NotNull; * Represents an argument which will give you a {@link ChatColor}. *

* Example: red, white, reset + * @deprecated Use {@link ArgumentTextColor} for colors and {@link ArgumentTextDecoration} for styles. */ +@Deprecated public class ArgumentColor extends Argument { public static final int UNDEFINED_COLOR = -2; diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java new file mode 100644 index 000000000..6c4328084 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java @@ -0,0 +1,65 @@ +package net.minestom.server.command.builder.arguments.minecraft; + +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.minestom.server.command.builder.NodeMaker; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; +import net.minestom.server.utils.MathUtils; +import org.jetbrains.annotations.NotNull; + +/** + * Represents an argument that will give you a {@link TextColor}. Input is parsed + * first as a hex string ({@code #int}), then as a CSS hex string ({@code #rrggbb} or + * {@code #rgb}), then as an integer and finally as a named text colour. The values for + * the named text colours can be found in {@link NamedTextColor}. + */ +public class ArgumentTextColor extends Argument { + + public static final int UNDEFINED_COLOR = -2; + + public ArgumentTextColor(@NotNull String id) { + super(id); + } + + @Override + public @NotNull TextColor parse(@NotNull String input) throws ArgumentSyntaxException { + TextColor textColor = null; + + // first try standard hex + textColor = TextColor.fromHexString(input); + if (textColor != null) { + return textColor; + } + + // now try CSS hex + textColor = TextColor.fromCSSHexString(input); + if (textColor != null) { + return textColor; + } + + // now try int + Integer number = MathUtils.tryParse(input); + if (number != null) { + return TextColor.color(number); + } + + // fallback to legacy colour names + textColor = NamedTextColor.NAMES.value(input.toLowerCase()); + if (textColor != null) { + return textColor; + } + + // throw an error + throw new ArgumentSyntaxException("Undefined color", input, UNDEFINED_COLOR); + } + + @Override + public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); + argumentNode.parser = "minecraft:text_decoration"; + + nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode}); + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextDecoration.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextDecoration.java new file mode 100644 index 000000000..e2ccfb605 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextDecoration.java @@ -0,0 +1,40 @@ +package net.minestom.server.command.builder.arguments.minecraft; + +import net.kyori.adventure.text.format.TextDecoration; +import net.minestom.server.command.builder.NodeMaker; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; +import org.jetbrains.annotations.NotNull; + +/** + * Represents an argument that will give you a {@link TextDecoration}. Valid values can + * be found in the text decoration class. Values are case-insensitive. + */ +public class ArgumentTextDecoration extends Argument { + public static final int UNDEFINED_DECORATION = -2; + + public ArgumentTextDecoration(@NotNull String id) { + super(id); + } + + @NotNull + @Override + public TextDecoration parse(@NotNull String input) throws ArgumentSyntaxException { + TextDecoration decoration = TextDecoration.NAMES.value(input.toLowerCase()); + + if (decoration != null) { + return decoration; + } + + throw new ArgumentSyntaxException("Undefined text decoration", input, UNDEFINED_DECORATION); + } + + @Override + public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); + argumentNode.parser = "minecraft:text_color"; + + nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode}); + } +} diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index a80c8d448..1a751058d 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -1,6 +1,7 @@ package net.minestom.server.entity; import com.google.common.collect.Queues; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.Viewable; import net.minestom.server.chat.JsonMessage; @@ -1189,13 +1190,25 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission this.entityMeta.setPose(pose); } + /** + * Gets the entity custom name. + * + * @return the custom name of the entity, null if there is not + * @deprecated Use {@link #getCustomName()} + */ + @Deprecated + @Nullable + public JsonMessage getCustomNameJson() { + return this.entityMeta.getCustomNameJson(); + } + /** * Gets the entity custom name. * * @return the custom name of the entity, null if there is not */ @Nullable - public JsonMessage getCustomName() { + public Component getCustomName() { return this.entityMeta.getCustomName(); } @@ -1203,11 +1216,22 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission * Changes the entity custom name. * * @param customName the custom name of the entity, null to remove it + * @deprecated Use {@link #setCustomName(Component)} */ + @Deprecated public void setCustomName(@Nullable JsonMessage customName) { this.entityMeta.setCustomName(customName); } + /** + * Changes the entity custom name. + * + * @param customName the custom name of the entity, null to remove it + */ + public void setCustomName(@Nullable Component customName) { + this.entityMeta.setCustomName(customName); + } + /** * Gets the custom name visible metadata field. * diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index 0c6aa37d7..b60b814ca 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -1,5 +1,6 @@ package net.minestom.server.entity; +import net.kyori.adventure.sound.Sound.Source; import net.minestom.server.attribute.Attribute; import net.minestom.server.attribute.AttributeInstance; import net.minestom.server.attribute.Attributes; @@ -20,7 +21,6 @@ import net.minestom.server.network.packet.server.play.*; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.scoreboard.Team; import net.minestom.server.sound.Sound; -import net.minestom.server.sound.SoundCategory; import net.minestom.server.utils.BlockPosition; import net.minestom.server.utils.Position; import net.minestom.server.utils.Vector; @@ -388,12 +388,12 @@ public class LivingEntity extends Entity implements EquipmentHandler { // play damage sound final Sound sound = type.getSound(this); if (sound != null) { - SoundCategory soundCategory; + Source soundCategory; if (this instanceof Player) { - soundCategory = SoundCategory.PLAYERS; + soundCategory = Source.PLAYER; } else { // TODO: separate living entity categories - soundCategory = SoundCategory.HOSTILE; + soundCategory = Source.HOSTILE; } SoundEffectPacket damageSoundPacket = diff --git a/src/main/java/net/minestom/server/entity/Metadata.java b/src/main/java/net/minestom/server/entity/Metadata.java index 11b4c363b..6fb856e35 100644 --- a/src/main/java/net/minestom/server/entity/Metadata.java +++ b/src/main/java/net/minestom/server/entity/Metadata.java @@ -1,5 +1,7 @@ package net.minestom.server.entity; +import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; import net.minestom.server.chat.JsonMessage; import net.minestom.server.item.ItemStack; import net.minestom.server.network.packet.server.play.EntityMetaDataPacket; @@ -36,10 +38,12 @@ public class Metadata { return new Value<>(TYPE_STRING, value, writer -> writer.writeSizedString(value)); } + @Deprecated public static Value Chat(@NotNull JsonMessage value) { return new Value<>(TYPE_CHAT, value, writer -> writer.writeSizedString(value.toString())); } + @Deprecated public static Value OptChat(@Nullable JsonMessage value) { return new Value<>(TYPE_OPTCHAT, value, writer -> { final boolean present = value != null; @@ -50,6 +54,20 @@ public class Metadata { }); } + public static Value Chat(@NotNull Component value) { + return new Value<>(TYPE_CHAT, value, writer -> writer.writeSizedString(MinecraftServer.getSerializationManager().serialize(value))); + } + + public static Value OptChat(@Nullable Component value) { + return new Value<>(TYPE_OPTCHAT, value, writer -> { + final boolean present = value != null; + writer.writeBoolean(present); + if (present) { + writer.writeSizedString(MinecraftServer.getSerializationManager().serialize(value)); + } + }); + } + public static Value Slot(@NotNull ItemStack value) { return new Value<>(TYPE_SLOT, value, writer -> writer.writeItemStack(value)); } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 258f0d0b7..9b47057f2 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -96,7 +96,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { protected final Set viewableEntities = ConcurrentHashMap.newKeySet(); private int latency; - private JsonMessage displayName; + private Component displayName; private PlayerSkin skin; private DimensionType dimensionType; @@ -475,15 +475,15 @@ public class Player extends LivingEntity implements CommandSender, Localizable { public void kill() { if (!isDead()) { - JsonMessage deathText; - JsonMessage chatMessage; + Component deathText; + Component chatMessage; // get death screen text to the killed player { if (lastDamageSource != null) { deathText = lastDamageSource.buildDeathScreenText(this); } else { // may happen if killed by the server without applying damage - deathText = ColoredText.of("Killed by poor programming."); + deathText = Component.text("Killed by poor programming."); } } @@ -492,7 +492,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { if (lastDamageSource != null) { chatMessage = lastDamageSource.buildDeathMessage(this); } else { // may happen if killed by the server without applying damage - chatMessage = ColoredText.of(getUsername() + " was killed by poor programming."); + chatMessage = Component.text(getUsername() + " was killed by poor programming."); } } @@ -505,13 +505,13 @@ public class Player extends LivingEntity implements CommandSender, Localizable { // #buildDeathScreenText can return null, check here if (deathText != null) { - CombatEventPacket deathPacket = CombatEventPacket.death(this, null, deathText); + CombatEventPacket deathPacket = CombatEventPacket.death(this, null, MinecraftServer.getSerializationManager().serialize(deathText, this)); playerConnection.sendPacket(deathPacket); } // #buildDeathMessage can return null, check here if (chatMessage != null) { - MinecraftServer.getConnectionManager().broadcastMessage(chatMessage); + MinecraftServer.getConnectionManager().sendMessage(chatMessage); } } @@ -766,22 +766,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable { sendPluginMessage(channel, bytes); } - @Override - public void sendMessage(@NotNull String message) { - sendMessage(ColoredText.of(message)); - } - - /** - * Sends a message to the player. - * - * @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()); - } - /** * Sends a legacy message with the specified color char. * @@ -819,7 +803,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { @Override public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { - ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().serialize(message), type, source.uuid()); + ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().serialize(message, this), type, source.uuid()); playerConnection.sendPacket(chatMessagePacket); } @@ -1023,8 +1007,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable { @Override public void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) { PlayerListHeaderAndFooterPacket packet = new PlayerListHeaderAndFooterPacket(); - packet.header = MinecraftServer.getSerializationManager().serialize(header); - packet.footer = MinecraftServer.getSerializationManager().serialize(footer); + packet.header = MinecraftServer.getSerializationManager().serialize(header, this); + packet.footer = MinecraftServer.getSerializationManager().serialize(footer, this); playerConnection.sendPacket(packet); } @@ -1105,7 +1089,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { @Override public void sendActionBar(@NotNull Component message) { - TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().serialize(message)); + TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().serialize(message, this)); playerConnection.sendPacket(titlePacket); } @@ -1313,9 +1297,20 @@ public class Player extends LivingEntity implements CommandSender, Localizable { * Gets the player display name in the tab-list. * * @return the player display name, null means that {@link #getUsername()} is displayed + * @deprecated Use {@link #getDisplayName()} */ @Nullable - public JsonMessage getDisplayName() { + public JsonMessage getDisplayNameJson() { + return JsonMessage.fromComponent(displayName); + } + + /** + * Gets the player display name in the tab-list. + * + * @return the player display name, null means that {@link #getUsername()} is displayed + */ + @Nullable + public Component getDisplayName() { return displayName; } @@ -1325,12 +1320,25 @@ public class Player extends LivingEntity implements CommandSender, Localizable { * Sets to null to show the player username. * * @param displayName the display name, null to display the username + * @deprecated Use {@link #setDisplayName(Component)} */ + @Deprecated public void setDisplayName(@Nullable JsonMessage displayName) { + this.setDisplayName(displayName == null ? null : displayName.asComponent()); + } + + /** + * Changes the player display name in the tab-list. + *

+ * Sets to null to show the player username. + * + * @param displayName the display name, null to display the username + */ + public void setDisplayName(@Nullable Component displayName) { this.displayName = displayName; PlayerInfoPacket infoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_DISPLAY_NAME); - infoPacket.playerInfos.add(new PlayerInfoPacket.UpdateDisplayName(getUuid(), displayName)); + infoPacket.playerInfos.add(new PlayerInfoPacket.UpdateDisplayName(getUuid(), MinecraftServer.getSerializationManager().serialize(displayName))); sendPacketToViewersAndSelf(infoPacket); } @@ -1909,9 +1917,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable { // Packet type depends on the current player connection state final ServerPacket disconnectPacket; if (connectionState == ConnectionState.LOGIN) { - disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(component)); + disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(component, this)); } else { - disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().serialize(component)); + disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().serialize(component, this)); } if (playerConnection instanceof NettyPlayerConnection) { @@ -2536,7 +2544,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { PlayerInfoPacket.AddPlayer addPlayer = new PlayerInfoPacket.AddPlayer(getUuid(), getUsername(), getGameMode(), getLatency()); - addPlayer.displayName = displayName; + addPlayer.displayName = MinecraftServer.getSerializationManager().serialize(displayName); // Skin support if (skin != null) { diff --git a/src/main/java/net/minestom/server/entity/damage/DamageType.java b/src/main/java/net/minestom/server/entity/damage/DamageType.java index 4c61be63e..813a48ac9 100644 --- a/src/main/java/net/minestom/server/entity/damage/DamageType.java +++ b/src/main/java/net/minestom/server/entity/damage/DamageType.java @@ -1,5 +1,8 @@ package net.minestom.server.entity.damage; +import com.google.gson.stream.JsonReader; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; import net.minestom.server.chat.RichMessage; @@ -55,18 +58,25 @@ public class DamageType implements DataContainer { return identifier; } + /** + * @deprecated Use {@link #buildDeathMessage(Player)} + */ + @Deprecated + public JsonMessage buildDeathMessageJson(@NotNull Player killed) { + return JsonMessage.fromComponent(this.buildDeathMessage(killed)); + } + /** * Builds the death message linked to this damage type. *

* Used in {@link Player#kill()} to broadcast the proper message. * * @param killed the player who has been killed - * @return the death message, null to do not send anything. - * Can be for instance, of type {@link ColoredText} or {@link RichMessage}. + * @return the death message, null to do not send anything */ @Nullable - public JsonMessage buildDeathMessage(@NotNull Player killed) { - return ColoredText.of("{@death." + identifier + "," + killed.getUsername() + "}"); + public Component buildDeathMessage(@NotNull Player killed) { + return Component.translatable("death." + identifier, Component.text(killed.getUsername())); } /** @@ -103,15 +113,23 @@ public class DamageType implements DataContainer { return new EntityDamage(entity); } + /** + * @deprecated Use {@link #buildDeathScreenText(Player)} + */ + @Deprecated + @Nullable + public JsonMessage buildDeathScreenTextJson(@NotNull Player killed) { + return JsonMessage.fromComponent(this.buildDeathScreenText(killed)); + } + /** * Builds the text sent to a player in his death screen. * * @param killed the player who has been killed * @return the death screen text, null to do not send anything */ - @Nullable - public JsonMessage buildDeathScreenText(@NotNull Player killed) { - return ColoredText.of("{@death." + identifier + "}"); + public Component buildDeathScreenText(@NotNull Player killed) { + return Component.translatable("death." + identifier); } /** diff --git a/src/main/java/net/minestom/server/entity/hologram/Hologram.java b/src/main/java/net/minestom/server/entity/hologram/Hologram.java index 18339950d..79ebb291f 100644 --- a/src/main/java/net/minestom/server/entity/hologram/Hologram.java +++ b/src/main/java/net/minestom/server/entity/hologram/Hologram.java @@ -1,5 +1,6 @@ package net.minestom.server.entity.hologram; +import net.kyori.adventure.text.Component; import net.minestom.server.Viewable; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; @@ -13,7 +14,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Set; /** - * Represents an invisible armor stand showing a {@link JsonMessage}. + * Represents an invisible armor stand showing a {@link Component}. */ public class Hologram implements Viewable { @@ -22,7 +23,7 @@ public class Hologram implements Viewable { private final HologramEntity entity; private Position position; - private JsonMessage text; + private Component text; private boolean removed; @@ -33,14 +34,24 @@ public class Hologram implements Viewable { * @param spawnPosition The spawn position of this hologram. * @param text The text of this hologram. * @param autoViewable {@code true}if the hologram should be visible automatically, otherwise {@code false}. + * @deprecated Use {@link #Hologram(Instance, Position, Component, boolean)} */ + @Deprecated public Hologram(Instance instance, Position spawnPosition, JsonMessage text, boolean autoViewable) { - this.entity = new HologramEntity(spawnPosition.clone().add(0, OFFSET_Y, 0)); - this.entity.setInstance(instance); - this.entity.setAutoViewable(autoViewable); + this(instance, spawnPosition, text.asComponent(), autoViewable); + } - this.position = spawnPosition; - setText(text); + /** + * Constructs a new {@link Hologram} with the given parameters. + * + * @param instance The instance where the hologram should be spawned. + * @param spawnPosition The spawn position of this hologram. + * @param text The text of this hologram. + * @deprecated Use {@link #Hologram(Instance, Position, Component)} + */ + @Deprecated + public Hologram(Instance instance, Position spawnPosition, JsonMessage text) { + this(instance, spawnPosition, text, true); } /** @@ -50,10 +61,27 @@ public class Hologram implements Viewable { * @param spawnPosition The spawn position of this hologram. * @param text The text of this hologram. */ - public Hologram(Instance instance, Position spawnPosition, JsonMessage text) { + public Hologram(Instance instance, Position spawnPosition, Component text) { this(instance, spawnPosition, text, true); } + /** + * Constructs a new {@link Hologram} with the given parameters. + * + * @param instance The instance where the hologram should be spawned. + * @param spawnPosition The spawn position of this hologram. + * @param text The text of this hologram. + * @param autoViewable {@code true}if the hologram should be visible automatically, otherwise {@code false}. + */ + public Hologram(Instance instance, Position spawnPosition, Component text, boolean autoViewable) { + this.entity = new HologramEntity(spawnPosition.clone().add(0, OFFSET_Y, 0)); + this.entity.setInstance(instance); + this.entity.setAutoViewable(autoViewable); + + this.position = spawnPosition; + setText(text); + } + /** * Gets the position of the hologram. * @@ -79,8 +107,19 @@ public class Hologram implements Viewable { * Gets the hologram text. * * @return the hologram text + * @deprecated Use {@link #getText()} */ - public JsonMessage getText() { + @Deprecated + public JsonMessage getTextJson() { + return JsonMessage.fromComponent(text); + } + + /** + * Gets the hologram text. + * + * @return the hologram text + */ + public Component getText() { return text; } @@ -88,8 +127,19 @@ public class Hologram implements Viewable { * Changes the hologram text. * * @param text the new hologram text + * @deprecated Use {@link #setText(Component)} */ + @Deprecated public void setText(JsonMessage text) { + this.setText(text.asComponent()); + } + + /** + * Changes the hologram text. + * + * @param text the new hologram text + */ + public void setText(Component text) { checkRemoved(); this.text = text; this.entity.setCustomName(text); diff --git a/src/main/java/net/minestom/server/entity/metadata/EntityMeta.java b/src/main/java/net/minestom/server/entity/metadata/EntityMeta.java index 9d535e594..1ef9e39d2 100644 --- a/src/main/java/net/minestom/server/entity/metadata/EntityMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/EntityMeta.java @@ -1,5 +1,6 @@ package net.minestom.server.entity.metadata; +import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Metadata; @@ -106,11 +107,29 @@ public class EntityMeta { this.metadata.setIndex((byte) 1, Metadata.VarInt(value)); } - public JsonMessage getCustomName() { + /** + * @deprecated Use {@link #getCustomName()} + */ + @Deprecated + public JsonMessage getCustomNameJson() { + return JsonMessage.fromComponent(this.getCustomName()); + } + + /** + * @deprecated Use {@link #setCustomName(Component)} + */ + @Deprecated + public void setCustomName(JsonMessage value) { + if (value != null) { + this.setCustomName(value.asComponent()); + } + } + + public Component getCustomName() { return this.metadata.getIndex((byte) 2, null); } - public void setCustomName(JsonMessage value) { + public void setCustomName(Component value) { this.metadata.setIndex((byte) 2, Metadata.OptChat(value)); } diff --git a/src/main/java/net/minestom/server/entity/metadata/minecart/CommandBlockMinecartMeta.java b/src/main/java/net/minestom/server/entity/metadata/minecart/CommandBlockMinecartMeta.java index acac0c2fe..fdb994154 100644 --- a/src/main/java/net/minestom/server/entity/metadata/minecart/CommandBlockMinecartMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/minecart/CommandBlockMinecartMeta.java @@ -1,5 +1,6 @@ package net.minestom.server.entity.metadata.minecart; +import net.kyori.adventure.text.Component; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Entity; @@ -21,12 +22,29 @@ public class CommandBlockMinecartMeta extends AbstractMinecartMeta { super.metadata.setIndex((byte) 13, Metadata.String(value)); } + /** + * @deprecated Use {@link #getLastOutput()} + */ + @Deprecated @NotNull - public JsonMessage getLastOutput() { - return super.metadata.getIndex((byte) 14, ColoredText.of("")); + public JsonMessage getLastOutputJson() { + return JsonMessage.fromComponent(getLastOutput()); } + @NotNull + public Component getLastOutput() { + return super.metadata.getIndex((byte) 14, Component.empty()); + } + + /** + * @deprecated Use {@link #setLastOutput(Component)} + */ + @Deprecated public void setLastOutput(@NotNull JsonMessage value) { + this.setLastOutput(value.asComponent()); + } + + public void setLastOutput(@NotNull Component value) { super.metadata.setIndex((byte) 14, Metadata.Chat(value)); } diff --git a/src/main/java/net/minestom/server/event/player/PlayerChatEvent.java b/src/main/java/net/minestom/server/event/player/PlayerChatEvent.java index 24e84a477..4be97291f 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerChatEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerChatEvent.java @@ -1,5 +1,6 @@ package net.minestom.server.event.player; +import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Player; import net.minestom.server.event.CancellableEvent; @@ -19,7 +20,7 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent { private final Collection recipients; private String message; - private Function chatFormat; + private Function chatFormat; private boolean cancelled; @@ -33,8 +34,19 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent { * Changes the chat format. * * @param chatFormat the custom chat format, null to use the default one + * @deprecated Use {@link #setChatFormat(Function)} */ - public void setChatFormat(@Nullable Function chatFormat) { + @Deprecated + public void setChatFormatJson(@Nullable Function chatFormat) { + this.chatFormat = chatFormat == null ? null : chatFormat.andThen(JsonMessage::asComponent); + } + + /** + * Changes the chat format. + * + * @param chatFormat the custom chat format, null to use the default one + */ + public void setChatFormat(@Nullable Function chatFormat) { this.chatFormat = chatFormat; } @@ -77,7 +89,7 @@ public class PlayerChatEvent extends PlayerEvent implements CancellableEvent { * @return the chat format which will be used, null if this is the default one */ @Nullable - public Function getChatFormatFunction() { + public Function getChatFormatFunction() { return chatFormat; } diff --git a/src/main/java/net/minestom/server/event/player/PlayerDeathEvent.java b/src/main/java/net/minestom/server/event/player/PlayerDeathEvent.java index 14b0b44d4..477e8deca 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerDeathEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerDeathEvent.java @@ -1,6 +1,7 @@ package net.minestom.server.event.player; -import net.minestom.server.chat.ColoredText; +import com.google.gson.stream.JsonReader; +import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Player; import net.minestom.server.event.PlayerEvent; @@ -12,10 +13,18 @@ import org.jetbrains.annotations.Nullable; */ public class PlayerDeathEvent extends PlayerEvent { - private JsonMessage deathText; - private JsonMessage chatMessage; + private Component deathText; + private Component chatMessage; + /** + * @deprecated Use {@link #PlayerDeathEvent(Player, Component, Component)} + */ + @Deprecated public PlayerDeathEvent(@NotNull Player player, JsonMessage deathText, JsonMessage chatMessage) { + this(player, deathText.asComponent(), chatMessage.asComponent()); + } + + public PlayerDeathEvent(@NotNull Player player, Component deathText, Component chatMessage) { super(player); this.deathText = deathText; this.chatMessage = chatMessage; @@ -25,9 +34,21 @@ public class PlayerDeathEvent extends PlayerEvent { * Gets the text displayed in the death screen. * * @return the death text, can be null + * @deprecated Use {@link #getDeathText()} */ @Nullable - public JsonMessage getDeathText() { + @Deprecated + public JsonMessage getDeathTextJson() { + return JsonMessage.fromComponent(deathText); + } + + /** + * Gets the text displayed in the death screen. + * + * @return the death text, can be null + */ + @Nullable + public Component getDeathText() { return deathText; } @@ -35,18 +56,41 @@ public class PlayerDeathEvent extends PlayerEvent { * Changes the text displayed in the death screen. * * @param deathText the death text to display, null to remove + * @deprecated Use {@link #setDeathText(Component)} */ + @Deprecated public void setDeathText(@Nullable JsonMessage deathText) { + this.deathText = deathText == null ? null : deathText.asComponent(); + } + + /** + * Changes the text displayed in the death screen. + * + * @param deathText the death text to display, null to remove + */ + public void setDeathText(@Nullable Component deathText) { this.deathText = deathText; } + /** + * Gets the message sent to chat. + * + * @return the death chat message + * @deprecated Use {@link #getChatMessage()} + */ + @Deprecated + @Nullable + public JsonMessage getChatMessageJson() { + return JsonMessage.fromComponent(chatMessage); + } + /** * Gets the message sent to chat. * * @return the death chat message */ @Nullable - public JsonMessage getChatMessage() { + public Component getChatMessage() { return chatMessage; } @@ -54,8 +98,19 @@ public class PlayerDeathEvent extends PlayerEvent { * Changes the text sent in chat * * @param chatMessage the death message to send, null to remove + * @deprecated Use {@link #setChatMessage(Component)} */ + @Deprecated public void setChatMessage(@Nullable JsonMessage chatMessage) { + this.chatMessage = chatMessage == null ? null : chatMessage.asComponent(); + } + + /** + * Changes the text sent in chat + * + * @param chatMessage the death message to send, null to remove + */ + public void setChatMessage(@Nullable Component chatMessage) { this.chatMessage = chatMessage; } } diff --git a/src/main/java/net/minestom/server/item/ItemDisplay.java b/src/main/java/net/minestom/server/item/ItemDisplay.java index 74344bb75..b88013b33 100644 --- a/src/main/java/net/minestom/server/item/ItemDisplay.java +++ b/src/main/java/net/minestom/server/item/ItemDisplay.java @@ -1,13 +1,31 @@ package net.minestom.server.item; +import com.google.gson.JsonNull; +import com.google.gson.stream.JsonReader; +import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; +import java.util.stream.Stream; + public class ItemDisplay { - private JsonMessage displayName; - private JsonMessage[] lore; + private Component displayName; + private Component[] lore; + /** + * @deprecated Use {@link #ItemDisplay(Component, Component[])} + */ + @Deprecated public ItemDisplay(JsonMessage displayName, JsonMessage[] lore) { + this.displayName = displayName.asComponent(); + this.lore = new Component[lore.length]; + + for (int i = 0; i < lore.length; i++) { + this.lore[i] = lore[i].asComponent(); + } + } + + public ItemDisplay(Component displayName, Component[] lore) { this.displayName = displayName; this.lore = lore; } @@ -16,8 +34,34 @@ public class ItemDisplay { * Gets the item display name. * * @return the item display name + * @deprecated Use {@link #getDisplayName()} */ - public JsonMessage getDisplayName() { + @Deprecated + public JsonMessage getDisplayNameJson() { + return JsonMessage.fromComponent(displayName); + } + + /** + * Gets the item lore. + * + * @return the item lore + * @deprecated Use {@link #getLore()} + */ + @Deprecated + public JsonMessage[] getLoreJson() { + JsonMessage[] loreOld = new JsonMessage[lore.length]; + for (int i = 0; i < lore.length; i++) { + loreOld[i] = JsonMessage.fromComponent(lore[i]); + } + return loreOld; + } + + /** + * Gets the item display name. + * + * @return the item display name + */ + public Component getDisplayName() { return displayName; } @@ -26,7 +70,7 @@ public class ItemDisplay { * * @return the item lore */ - public JsonMessage[] getLore() { + public Component[] getLore() { return lore; } } diff --git a/src/main/java/net/minestom/server/item/ItemStack.java b/src/main/java/net/minestom/server/item/ItemStack.java index 01f0ae172..937b571ee 100644 --- a/src/main/java/net/minestom/server/item/ItemStack.java +++ b/src/main/java/net/minestom/server/item/ItemStack.java @@ -2,6 +2,7 @@ package net.minestom.server.item; import it.unimi.dsi.fastutil.objects.Object2ShortMap; import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.JsonMessage; import net.minestom.server.data.Data; @@ -26,6 +27,7 @@ import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.*; +import java.util.stream.Collectors; // TODO should we cache a ByteBuf of this item for faster packet write @@ -54,9 +56,9 @@ public class ItemStack implements DataContainer, PublicCloneable { private byte amount; private int damage; - private JsonMessage displayName; + private Component displayName; private boolean unbreakable; - private List lore; + private List lore; private Object2ShortMap enchantmentMap; private List attributes; @@ -176,7 +178,7 @@ public class ItemStack implements DataContainer, PublicCloneable { return true; } - final JsonMessage itemDisplayName = itemStack.getDisplayName(); + final Component itemDisplayName = itemStack.getDisplayName(); final boolean displayNameCheck = (displayName == null && itemDisplayName == null) || (displayName != null && displayName.equals(itemDisplayName)); @@ -309,13 +311,25 @@ public class ItemStack implements DataContainer, PublicCloneable { this.itemMeta = itemMeta; } + /** + * Gets the item display name. + * + * @return the item display name, can be null if not present + * @deprecated Use {@link #getDisplayName()} + */ + @Deprecated + @Nullable + public JsonMessage getDisplayNameJson() { + return JsonMessage.fromComponent(displayName); + } + /** * Gets the item display name. * * @return the item display name, can be null if not present */ @Nullable - public JsonMessage getDisplayName() { + public Component getDisplayName() { return displayName; } @@ -323,8 +337,19 @@ public class ItemStack implements DataContainer, PublicCloneable { * Sets the item display name. * * @param displayName the item display name + * @deprecated Use {@link #setDisplayName(Component)} */ + @Deprecated public void setDisplayName(@Nullable JsonMessage displayName) { + this.setDisplayName(displayName == null ? null : displayName.asComponent()); + } + + /** + * Sets the item display name. + * + * @param displayName the item display name + */ + public void setDisplayName(@Nullable Component displayName) { this.displayName = displayName; } @@ -337,13 +362,25 @@ public class ItemStack implements DataContainer, PublicCloneable { return displayName != null; } + /** + * Gets the item lore. + * + * @return a modifiable list containing the item lore, can be empty if not present + * @deprecated Use {@link #getLore()} + */ + @Deprecated + @NotNull + public List getLoreJson() { + return lore.stream().map(JsonMessage::fromComponent).collect(Collectors.toList()); + } + /** * Gets the item lore. * * @return a modifiable list containing the item lore, can be empty if not present */ @NotNull - public List getLore() { + public List getLore() { return lore; } @@ -351,8 +388,20 @@ public class ItemStack implements DataContainer, PublicCloneable { * Sets the item lore. * * @param lore the item lore, can be empty to remove + * @deprecated Use {@link #setLore} */ - public void setLore(@NotNull List lore) { + @Deprecated + public void setLoreJson(@NotNull List lore) { + this.lore = lore.stream().map(JsonMessage::asComponent).collect(Collectors.toList()); + } + + /** + * Sets the item lore. + * + * @param lore the item lore, can be empty to remove + */ + @NotNull + public void setLore(List lore) { this.lore = lore; } diff --git a/src/main/java/net/minestom/server/item/firework/FireworkEffect.java b/src/main/java/net/minestom/server/item/firework/FireworkEffect.java index f160aea43..59c8a3c7c 100644 --- a/src/main/java/net/minestom/server/item/firework/FireworkEffect.java +++ b/src/main/java/net/minestom/server/item/firework/FireworkEffect.java @@ -1,6 +1,8 @@ package net.minestom.server.item.firework; +import net.kyori.adventure.text.format.TextColor; import net.minestom.server.chat.ChatColor; +import net.minestom.server.color.Color; import org.jetbrains.annotations.NotNull; import org.jglrxavpok.hephaistos.nbt.NBTCompound; @@ -11,8 +13,23 @@ public class FireworkEffect { private final boolean flicker; private final boolean trail; private final FireworkEffectType type; - private final ChatColor color; - private final ChatColor fadeColor; + private final Color color; + private final Color fadeColor; + + /** + * Initializes a new firework effect. + * + * @param flicker {@code true} if this explosion has the Twinkle effect (glowstone dust), otherwise {@code false}. + * @param trail {@code true} if this explosion hsa the Trail effect (diamond), otherwise {@code false}. + * @param type The shape of this firework's explosion. + * @param color The primary color of this firework effect. + * @param fadeColor The secondary color of this firework effect. + * @deprecated Use {@link #FireworkEffect(boolean, boolean, FireworkEffectType, Color, Color)} + */ + @Deprecated + public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, ChatColor color, ChatColor fadeColor) { + this(flicker, trail, type, color.asColor(), fadeColor.asColor()); + } /** * Initializes a new firework effect. @@ -23,7 +40,7 @@ public class FireworkEffect { * @param color The primary color of this firework effect. * @param fadeColor The secondary color of this firework effect. */ - public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, ChatColor color, ChatColor fadeColor) { + public FireworkEffect(boolean flicker, boolean trail, FireworkEffectType type, Color color, Color fadeColor) { this.flicker = flicker; this.trail = trail; this.type = type; @@ -39,17 +56,17 @@ public class FireworkEffect { */ public static FireworkEffect fromCompound(@NotNull NBTCompound compound) { - ChatColor primaryColor = null; - ChatColor secondaryColor = null; + Color primaryColor = null; + Color secondaryColor = null; if (compound.containsKey("Colors")) { int[] color = compound.getIntArray("Colors"); - primaryColor = ChatColor.fromRGB((byte) color[0], (byte) color[1], (byte) color[2]); + primaryColor = new Color(color[0], color[1], color[2]); } if (compound.containsKey("FadeColors")) { int[] fadeColor = compound.getIntArray("FadeColors"); - secondaryColor = ChatColor.fromRGB((byte) fadeColor[0], (byte) fadeColor[1], (byte) fadeColor[2]); + secondaryColor = new Color(fadeColor[0], fadeColor[1], fadeColor[2]); } diff --git a/src/main/java/net/minestom/server/item/metadata/LeatherArmorMeta.java b/src/main/java/net/minestom/server/item/metadata/LeatherArmorMeta.java index 202327ae5..0926074aa 100644 --- a/src/main/java/net/minestom/server/item/metadata/LeatherArmorMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/LeatherArmorMeta.java @@ -1,6 +1,7 @@ package net.minestom.server.item.metadata; import net.minestom.server.chat.ChatColor; +import net.minestom.server.color.Color; import org.jetbrains.annotations.NotNull; import org.jglrxavpok.hephaistos.nbt.NBTCompound; @@ -8,25 +9,18 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; * Represents the item meta for leather armor parts. */ public class LeatherArmorMeta extends ItemMeta { - - private static final int BIT_MASK = 0xFF; - private boolean modified; - private byte red; - private byte green; - private byte blue; + private Color color; /** * Sets the color of the leather armor piece. * * @param color the color of the leather armor + * @deprecated Use {@link #setColor(Color)} */ + @Deprecated public void setColor(ChatColor color) { - // TODO using "CHAT color" is pretty weird, maybe that the class should be renamed to "Color" - this.red = color.getRed(); - this.green = color.getGreen(); - this.blue = color.getBlue(); - this.modified = true; + this.setColor(color.asColor()); } /** @@ -35,21 +29,37 @@ public class LeatherArmorMeta extends ItemMeta { * @param red The red color of the leather armor piece. * @param green The green color of the leather armor piece. * @param blue The blue color of the leather armor piece. + * @deprecated Use {@link #setColor(Color)} */ + @Deprecated public void setColor(byte red, byte green, byte blue) { - this.red = red; - this.green = green; - this.blue = blue; - this.modified = true; + this.setColor(new Color(red, green, blue)); + } + + /** + * Sets the color of this leather armor piece. + * + * @param color the new color + */ + public void setColor(@NotNull Color color) { + this.modified = !color.equals(this.color); + this.color = color; + } + + /** + * Gets the color of this leather armor piece. + * + * @return the color + */ + public @NotNull Color getColor() { + return this.color; } /** * Resets the color to the default leather one. */ public void reset() { - this.red = 0; - this.green = 0; - this.blue = 0; + this.color = new Color(0, 0, 0); this.modified = false; } @@ -57,27 +67,33 @@ public class LeatherArmorMeta extends ItemMeta { * Gets the red component. * * @return the red component + * @deprecated Use {@link #getColor} */ + @Deprecated public int getRed() { - return BIT_MASK & red; + return this.color.getRed(); } /** * Gets the green component. * * @return the green component + * @deprecated Use {@link #getColor} */ + @Deprecated public int getGreen() { - return BIT_MASK & green; + return this.color.getGreen(); } /** * Gets the blue component. * * @return the blue component + * @deprecated Use {@link #getColor} */ + @Deprecated public int getBlue() { - return BIT_MASK & blue; + return this.color.getBlue(); } /** @@ -105,9 +121,7 @@ public class LeatherArmorMeta extends ItemMeta { if (!(itemMeta instanceof LeatherArmorMeta)) return false; final LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) itemMeta; return leatherArmorMeta.isModified() == isModified() - && leatherArmorMeta.getRed() == getRed() - && leatherArmorMeta.getGreen() == getGreen() - && leatherArmorMeta.getBlue() == getBlue(); + && leatherArmorMeta.getColor().equals(getColor()); } /** @@ -123,10 +137,7 @@ public class LeatherArmorMeta extends ItemMeta { // Sets the color of the leather armor piece // This also fixes that the armor pieces do not decolorize again when you are in creative // mode. - this.setColor( - (byte) ((color >> 16) & BIT_MASK), - (byte) ((color >> 8) & BIT_MASK), - (byte) ((color) & BIT_MASK)); + this.setColor(new Color(color)); } } } @@ -143,9 +154,7 @@ public class LeatherArmorMeta extends ItemMeta { } else { displayCompound = compound.getCompound("display"); } - final int color = this.getRed() << 16 | this.getGreen() << 8 | this.getBlue(); - - displayCompound.setInt("color", color); + displayCompound.setInt("color", color.asRGB()); // Adds the color compound to the display compound compound.set("display", displayCompound); } @@ -159,9 +168,7 @@ public class LeatherArmorMeta extends ItemMeta { public ItemMeta clone() { LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) super.clone(); leatherArmorMeta.modified = this.isModified(); - leatherArmorMeta.red = (byte) this.getRed(); - leatherArmorMeta.green = (byte) this.getGreen(); - leatherArmorMeta.blue = (byte) this.getBlue(); + leatherArmorMeta.color = color; return leatherArmorMeta; } diff --git a/src/main/java/net/minestom/server/item/metadata/MapMeta.java b/src/main/java/net/minestom/server/item/metadata/MapMeta.java index 991c21716..f29e25e97 100644 --- a/src/main/java/net/minestom/server/item/metadata/MapMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/MapMeta.java @@ -1,7 +1,10 @@ package net.minestom.server.item.metadata; +import net.kyori.adventure.text.format.TextColor; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; +import net.minestom.server.color.Color; +import net.minestom.server.color.DyeColor; import net.minestom.server.utils.clone.CloneUtils; import net.minestom.server.utils.clone.PublicCloneable; import org.jetbrains.annotations.NotNull; @@ -17,7 +20,7 @@ public class MapMeta extends ItemMeta { private int mapId; private int mapScaleDirection = 1; private List decorations = new CopyOnWriteArrayList<>(); - private ChatColor mapColor = ChatColor.NO_COLOR; + private Color mapColor = new Color(0, 0, 0); public MapMeta() { } @@ -84,21 +87,40 @@ public class MapMeta extends ItemMeta { * Gets the map color. * * @return the map color + * @deprecated Use {@link #getMapColor()} */ - public ChatColor getMapColor() { - return mapColor; + @Deprecated + public ChatColor getLegacyMapColor() { + return this.mapColor.asLegacyChatColor(); + } + + /** + * Gets the map color. + * + * @return the map color + */ + public @NotNull Color getMapColor() { + return this.mapColor; } /** * Changes the map color. - *

- * WARNING: RGB colors are not supported. * * @param mapColor the new map color + * @deprecated Use {@link #setMapColor(Color)} */ + @Deprecated public void setMapColor(ChatColor mapColor) { - mapColor.getId(); // used to throw an error if rgb color is used - this.mapColor = mapColor; + this.setMapColor(mapColor.asColor()); + } + + /** + * Changes the map color. + * + * @param color the new map color + */ + public void setMapColor(@NotNull Color color) { + this.mapColor = color; } @Override @@ -156,8 +178,7 @@ public class MapMeta extends ItemMeta { if (compound.containsKey("display")) { final NBTCompound displayCompound = compound.getCompound("display"); if (displayCompound.containsKey("MapColor")) { - final int color = displayCompound.getAsInt("MapColor"); - this.mapColor = ChatColor.fromId(color); + this.mapColor = new Color(displayCompound.getAsInt("MapColor")); } } @@ -191,7 +212,7 @@ public class MapMeta extends ItemMeta { } else { displayCompound = new NBTCompound(); } - displayCompound.setInt("MapColor", mapColor.getId()); + displayCompound.setInt("MapColor", mapColor.asRGB()); } } diff --git a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java index a88545481..b7ac76db9 100644 --- a/src/main/java/net/minestom/server/item/metadata/PotionMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/PotionMeta.java @@ -1,6 +1,8 @@ package net.minestom.server.item.metadata; +import net.kyori.adventure.text.format.TextColor; import net.minestom.server.chat.ChatColor; +import net.minestom.server.color.Color; import net.minestom.server.potion.CustomPotionEffect; import net.minestom.server.potion.PotionType; import net.minestom.server.registry.Registries; @@ -29,8 +31,7 @@ public class PotionMeta extends ItemMeta { // Not final because of #clone() private List customPotionEffects = new CopyOnWriteArrayList<>(); - private boolean hasColor; - private byte red, green, blue; + private Color color; /** * Gets the potion type. @@ -65,19 +66,20 @@ public class PotionMeta extends ItemMeta { * Changes the color of the potion. * * @param color the new color of the potion + * @deprecated Use {@link #setColor(Color)} */ + @Deprecated public void setColor(ChatColor color) { - // FIXME: weird usage of ChatColor, should maybe rename + this.setColor(color.asColor()); + } - if (color == null) { - this.hasColor = false; - return; - } - - this.red = color.getRed(); - this.green = color.getGreen(); - this.blue = color.getBlue(); - this.hasColor = true; + /** + * Changes the color of the potion. + * + * @param color the new color of the potion + */ + public void setColor(@Nullable Color color) { + this.color = color; } @Override @@ -93,10 +95,7 @@ public class PotionMeta extends ItemMeta { PotionMeta potionMeta = (PotionMeta) itemMeta; return potionMeta.potionType == potionType && potionMeta.customPotionEffects.equals(customPotionEffects) && - potionMeta.hasColor == hasColor && - potionMeta.red == red && - potionMeta.green == green && - potionMeta.blue == blue; + potionMeta.color.equals(color); } @Override @@ -121,10 +120,7 @@ public class PotionMeta extends ItemMeta { } if (compound.containsKey("CustomPotionColor")) { - final int color = compound.getInt("CustomPotionColor"); - this.red = (byte) ((color >> 16) & 0x000000FF); - this.green = (byte) ((color >> 8) & 0x000000FF); - this.blue = (byte) ((color) & 0x000000FF); + this.color = new Color(compound.getInt("CustomPotionColor")); } } @@ -151,9 +147,8 @@ public class PotionMeta extends ItemMeta { compound.set("CustomPotionEffects", potionList); } - if (hasColor) { - final int color = red << 16 + green << 8 + blue; - compound.setInt("CustomPotionColor", color); + if (color != null) { + compound.setInt("CustomPotionColor", color.asRGB()); } } @@ -165,10 +160,7 @@ public class PotionMeta extends ItemMeta { potionMeta.potionType = potionType; potionMeta.customPotionEffects = CloneUtils.cloneCopyOnWriteArrayList(customPotionEffects); - potionMeta.hasColor = hasColor; - potionMeta.red = red; - potionMeta.green = green; - potionMeta.blue = blue; + potionMeta.color = color; return potionMeta; } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java index 60642b809..35880697c 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java @@ -39,6 +39,19 @@ public class SoundEffectPacket implements ServerPacket { return packet; } + @NotNull + public static SoundEffectPacket create(Source category, Sound sound, Position position, float volume, float pitch) { + SoundEffectPacket packet = new SoundEffectPacket(); + packet.soundId = sound.getId(); + packet.soundCategory = category.ordinal(); + // *8 converts to fixed-point representation with 3 bits for fractional part + packet.x = (int) position.getX(); + packet.y = (int) position.getY(); + packet.z = (int) position.getZ(); + packet.volume = volume; + packet.pitch = pitch; + return packet; + } @Override public void write(@NotNull BinaryWriter writer) { writer.writeVarInt(soundId); diff --git a/src/main/java/net/minestom/server/utils/MathUtils.java b/src/main/java/net/minestom/server/utils/MathUtils.java index 54aac1217..788ec6a20 100644 --- a/src/main/java/net/minestom/server/utils/MathUtils.java +++ b/src/main/java/net/minestom/server/utils/MathUtils.java @@ -94,4 +94,12 @@ public final class MathUtils { public static double mod(final double a, final double b) { return (a % b + b) % b; } + + public static Integer tryParse(String string) { + try { + return Integer.parseInt(string); + } catch (NumberFormatException ignored) { + return null; + } + } } From 5ebd9058e2dcf028962de9a17ab02acbfb642c75 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 12:11:30 +0000 Subject: [PATCH 079/364] Update colours --- .../java/net/minestom/server/color/Color.java | 19 ++- .../net/minestom/server/color/DyeColor.java | 19 ++- .../net/minestom/server/color/TeamFormat.java | 133 ++++++++++++++++++ 3 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/minestom/server/color/TeamFormat.java diff --git a/src/main/java/net/minestom/server/color/Color.java b/src/main/java/net/minestom/server/color/Color.java index fcee18a31..f22620db6 100644 --- a/src/main/java/net/minestom/server/color/Color.java +++ b/src/main/java/net/minestom/server/color/Color.java @@ -1,8 +1,10 @@ package net.minestom.server.color; import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.util.RGBLike; import net.minestom.server.chat.ChatColor; import org.apache.commons.lang3.Validate; +import org.checkerframework.common.value.qual.IntRange; import org.jetbrains.annotations.NotNull; import java.util.Objects; @@ -10,7 +12,7 @@ import java.util.Objects; /** * A general purpose class for representing colors. */ -public class Color { +public class Color implements RGBLike { private static final int BIT_MASK = 0xff; private int red, green, blue; @@ -202,4 +204,19 @@ public class Color { public int hashCode() { return Objects.hash(red, green, blue); } + + @Override + public int red() { + return this.red; + } + + @Override + public int green() { + return this.green; + } + + @Override + public int blue() { + return this.blue; + } } diff --git a/src/main/java/net/minestom/server/color/DyeColor.java b/src/main/java/net/minestom/server/color/DyeColor.java index 835326837..56e7cd51c 100644 --- a/src/main/java/net/minestom/server/color/DyeColor.java +++ b/src/main/java/net/minestom/server/color/DyeColor.java @@ -1,11 +1,13 @@ package net.minestom.server.color; +import net.kyori.adventure.util.RGBLike; +import org.checkerframework.common.value.qual.IntRange; import org.jetbrains.annotations.NotNull; /** * Color values for dyes, wool and cloth items. */ -public enum DyeColor { +public enum DyeColor implements RGBLike { WHITE(new Color(0xF9FFFE), new Color(0xF0F0F0)), ORANGE(new Color(0xF9801D), new Color(0xEB8844)), MAGENTA(new Color(0xC74EBD), new Color(0xC354CD)), @@ -50,4 +52,19 @@ public enum DyeColor { public Color getFireworkColor() { return this.firework; } + + @Override + public int red() { + return this.color.red(); + } + + @Override + public int green() { + return this.color.green(); + } + + @Override + public int blue() { + return this.color.blue(); + } } diff --git a/src/main/java/net/minestom/server/color/TeamFormat.java b/src/main/java/net/minestom/server/color/TeamFormat.java new file mode 100644 index 000000000..c8991c494 --- /dev/null +++ b/src/main/java/net/minestom/server/color/TeamFormat.java @@ -0,0 +1,133 @@ +package net.minestom.server.color; + +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.util.RGBLike; +import org.jetbrains.annotations.Nullable; + +/** + * The format used for teams. Note that this is often referred to as "team color". This + * is misleading as teams can also use text decoration like bold, italics, etc. + */ +public enum TeamFormat implements RGBLike { + BLACK(NamedTextColor.BLACK), + DARK_BLUE(NamedTextColor.DARK_BLUE), + DARK_GREEN(NamedTextColor.DARK_GREEN), + DARK_AQUA(NamedTextColor.DARK_AQUA), + DARK_RED(NamedTextColor.DARK_RED), + DARK_PURPLE(NamedTextColor.DARK_PURPLE), + GOLD(NamedTextColor.GOLD), + GRAY(NamedTextColor.GRAY), + DARK_GRAY(NamedTextColor.DARK_GRAY), + BLUE(NamedTextColor.BLUE), + GREEN(NamedTextColor.GREEN), + AQUA(NamedTextColor.AQUA), + RED(NamedTextColor.RED), + LIGHT_PURPLE(NamedTextColor.LIGHT_PURPLE), + YELLOW(NamedTextColor.YELLOW), + WHITE(NamedTextColor.WHITE), + OBFUSCATED(TextDecoration.OBFUSCATED), + BOLD(TextDecoration.BOLD), + STRIKETHROUGH(TextDecoration.STRIKETHROUGH), + UNDERLINE(TextDecoration.UNDERLINED), + ITALIC(TextDecoration.ITALIC), + RESET(null, null); + + private final TextDecoration decoration; + private final TextColor color; + + TeamFormat(TextDecoration decoration) { + this(decoration, null); + } + + TeamFormat(TextColor color) { + this(null, color); + } + + TeamFormat(TextDecoration decoration, TextColor color) { + this.decoration = decoration; + this.color = color; + } + + /** + * Checks if this team color is a color. + * + * @return if it is a color + */ + public boolean isColor() { + return this.color != null; + } + + /** + * Gets the text color equivalent to this team color, if any. + * + * @return the text color + */ + public @Nullable TextColor getTextColor() { + return this.color; + } + + /** + * Gets the color equivalent to this team color, if any. + * + * @return the color + */ + public @Nullable Color getColor() { + return this.color == null ? null : new Color(this.color); + } + + /** + * Checks if this team color is a text decoration. + * + * @return if it is a decoration + */ + public boolean isDecoration() { + return this.decoration != null; + } + + /** + * Gets the text decoration equivalent to this team color, if any. + * + * @return the decoration + */ + public @Nullable TextDecoration getDecoration() { + return this.decoration; + } + + /** + * @throws IllegalStateException if this team format is not a color + */ + @Override + public int red() { + if (!this.isColor()) { + throw new IllegalStateException("This TeamFormat does not represent a color"); + } + + return this.color.red(); + } + + /** + * @throws IllegalStateException if this team format is not a color + */ + @Override + public int green() { + if (!this.isColor()) { + throw new IllegalStateException("This TeamFormat does not represent a color"); + } + + return this.color.green(); + } + + /** + * @throws IllegalStateException if this team format is not a color + */ + @Override + public int blue() { + if (!this.isColor()) { + throw new IllegalStateException("This TeamFormat does not represent a color"); + } + + return this.color.blue(); + } +} From 3b2d5fd7d036befad00ea14e1e48214f41a2cc09 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 12:31:06 +0000 Subject: [PATCH 080/364] more colour cleanup --- src/main/java/net/minestom/server/color/Color.java | 1 - src/main/java/net/minestom/server/color/DyeColor.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/net/minestom/server/color/Color.java b/src/main/java/net/minestom/server/color/Color.java index f22620db6..ad55a483e 100644 --- a/src/main/java/net/minestom/server/color/Color.java +++ b/src/main/java/net/minestom/server/color/Color.java @@ -4,7 +4,6 @@ import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.util.RGBLike; import net.minestom.server.chat.ChatColor; import org.apache.commons.lang3.Validate; -import org.checkerframework.common.value.qual.IntRange; import org.jetbrains.annotations.NotNull; import java.util.Objects; diff --git a/src/main/java/net/minestom/server/color/DyeColor.java b/src/main/java/net/minestom/server/color/DyeColor.java index 56e7cd51c..a773aea51 100644 --- a/src/main/java/net/minestom/server/color/DyeColor.java +++ b/src/main/java/net/minestom/server/color/DyeColor.java @@ -1,7 +1,6 @@ package net.minestom.server.color; import net.kyori.adventure.util.RGBLike; -import org.checkerframework.common.value.qual.IntRange; import org.jetbrains.annotations.NotNull; /** From edbd244a07bf70fcf277896c97b7a6bf0231692f Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 13:20:28 +0000 Subject: [PATCH 081/364] Optimise JsonMessage <-> Component --- src/main/java/net/minestom/server/chat/JsonMessage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/chat/JsonMessage.java b/src/main/java/net/minestom/server/chat/JsonMessage.java index 44e7257fc..0ccfabb66 100644 --- a/src/main/java/net/minestom/server/chat/JsonMessage.java +++ b/src/main/java/net/minestom/server/chat/JsonMessage.java @@ -55,11 +55,11 @@ public abstract class JsonMessage implements ComponentLike { @Override public @NotNull Component asComponent() { - return GsonComponentSerializer.gson().deserialize(this.toString()); + return GsonComponentSerializer.gson().serializer().fromJson(this.getJsonObject(), Component.class); } public static @NotNull JsonMessage fromComponent(@NotNull Component component) { - return new RawJsonMessage(GsonComponentSerializer.gson().serialize(component)); + return new RawJsonMessage(GsonComponentSerializer.gson().serializer().toJsonTree(component).getAsJsonObject()); } /** From 3b0aa2d63bbf75b68776e11664510c52bb890527 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 13:20:52 +0000 Subject: [PATCH 082/364] Deprecate legacy chat functions --- .../net/minestom/server/chat/ChatColor.java | 5 + .../server/entity/hologram/Hologram.java | 2 +- .../server/listener/ChatMessageListener.java | 25 ++- .../server/listener/KeepAliveListener.java | 6 +- .../server/network/ConnectionManager.java | 8 +- .../packet/server/play/TeamsPacket.java | 10 +- .../server/scoreboard/BelowNameTag.java | 16 +- .../server/scoreboard/Scoreboard.java | 31 ++- .../minestom/server/scoreboard/Sidebar.java | 109 +++++++--- .../minestom/server/scoreboard/TabList.java | 3 +- .../net/minestom/server/scoreboard/Team.java | 193 +++++++++++++++--- .../server/scoreboard/TeamBuilder.java | 148 ++++++++++++-- .../server/scoreboard/TeamManager.java | 37 +++- .../net/minestom/server/utils/NBTUtils.java | 14 +- .../server/utils/binary/BinaryReader.java | 11 + 15 files changed, 511 insertions(+), 107 deletions(-) diff --git a/src/main/java/net/minestom/server/chat/ChatColor.java b/src/main/java/net/minestom/server/chat/ChatColor.java index e0246712f..0de726436 100644 --- a/src/main/java/net/minestom/server/chat/ChatColor.java +++ b/src/main/java/net/minestom/server/chat/ChatColor.java @@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.kyori.adventure.text.format.TextColor; import net.minestom.server.color.Color; +import net.minestom.server.color.TeamFormat; import net.minestom.server.utils.validate.Check; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -288,6 +289,10 @@ public final class ChatColor { return new Color(red, green, blue); } + public TeamFormat asTeamFormat() { + return TeamFormat.values()[this.getId()]; + } + @NotNull @Override public String toString() { diff --git a/src/main/java/net/minestom/server/entity/hologram/Hologram.java b/src/main/java/net/minestom/server/entity/hologram/Hologram.java index 79ebb291f..9f7fa0e12 100644 --- a/src/main/java/net/minestom/server/entity/hologram/Hologram.java +++ b/src/main/java/net/minestom/server/entity/hologram/Hologram.java @@ -216,7 +216,7 @@ public class Hologram implements Viewable { setSmall(true); setNoGravity(true); - setCustomName(ColoredText.of("")); + setCustomName(Component.empty()); setCustomNameVisible(true); setInvisible(true); } diff --git a/src/main/java/net/minestom/server/listener/ChatMessageListener.java b/src/main/java/net/minestom/server/listener/ChatMessageListener.java index 36ac8e027..41a817802 100644 --- a/src/main/java/net/minestom/server/listener/ChatMessageListener.java +++ b/src/main/java/net/minestom/server/listener/ChatMessageListener.java @@ -1,7 +1,9 @@ package net.minestom.server.listener; +import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; import net.minestom.server.MinecraftServer; -import net.minestom.server.chat.*; import net.minestom.server.command.CommandManager; import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerChatEvent; @@ -9,7 +11,6 @@ import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.packet.client.play.ClientChatMessagePacket; import net.minestom.server.network.packet.server.play.ChatMessagePacket; import net.minestom.server.utils.PacketUtils; -import org.apache.commons.lang3.StringUtils; import java.util.Collection; import java.util.function.Function; @@ -39,9 +40,9 @@ public class ChatMessageListener { // Call the event player.callCancellableEvent(PlayerChatEvent.class, playerChatEvent, () -> { - final Function formatFunction = playerChatEvent.getChatFormatFunction(); + final Function formatFunction = playerChatEvent.getChatFormatFunction(); - JsonMessage textObject; + Component textObject; if (formatFunction != null) { // Custom format @@ -53,11 +54,11 @@ public class ChatMessageListener { final Collection recipients = playerChatEvent.getRecipients(); if (!recipients.isEmpty()) { - final String jsonMessage = textObject.toString(); + final String jsonMessage = MinecraftServer.getSerializationManager().serialize(textObject); // Send the message with the correct player UUID ChatMessagePacket chatMessagePacket = - new ChatMessagePacket(jsonMessage, ChatMessagePacket.Position.CHAT, player.getUuid()); + new ChatMessagePacket(jsonMessage, MessageType.CHAT, player.getUuid()); PacketUtils.sendGroupedPacket(recipients, chatMessagePacket); } @@ -66,15 +67,13 @@ public class ChatMessageListener { } - private static RichMessage buildDefaultChatMessage(PlayerChatEvent chatEvent) { + private static Component buildDefaultChatMessage(PlayerChatEvent chatEvent) { final String username = chatEvent.getPlayer().getUsername(); - final ColoredText usernameText = ColoredText.of(String.format("<%s>", username)); - - return RichMessage.of(usernameText) - .setHoverEvent(ChatHoverEvent.showText("Click to send a message to " + username)) - .setClickEvent(ChatClickEvent.suggestCommand("/msg " + username + StringUtils.SPACE)) - .append(ColoredText.of(StringUtils.SPACE + chatEvent.getMessage())); + return Component.text("<" + username + ">") + .hoverEvent(Component.text("Click to send a message to " + username)) + .clickEvent(ClickEvent.suggestCommand("/msg " + username + " ")) + .append(Component.text(" " + chatEvent.getMessage())); } } diff --git a/src/main/java/net/minestom/server/listener/KeepAliveListener.java b/src/main/java/net/minestom/server/listener/KeepAliveListener.java index 25f91c495..f280ff9a1 100644 --- a/src/main/java/net/minestom/server/listener/KeepAliveListener.java +++ b/src/main/java/net/minestom/server/listener/KeepAliveListener.java @@ -1,7 +1,7 @@ package net.minestom.server.listener; -import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.client.play.ClientKeepAlivePacket; @@ -12,7 +12,7 @@ public class KeepAliveListener { final long playerId = player.getLastKeepAlive(); final boolean equals = packetId == playerId; if (!equals) { - player.kick(ColoredText.of(ChatColor.RED + "Bad Keep Alive packet")); + player.kick(Component.text("Bad Keep Alive packet", NamedTextColor.RED)); return; } diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 5bef23706..091755835 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -3,13 +3,10 @@ package net.minestom.server.network; import io.netty.channel.Channel; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; +import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.Style; -import net.kyori.adventure.text.format.TextColor; import net.minestom.server.MinecraftServer; -import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Player; import net.minestom.server.entity.fakeplayer.FakePlayer; @@ -175,8 +172,7 @@ public final class ConnectionManager implements ForwardingAudience { } private void broadcastJson(@NotNull String json, @NotNull Collection recipients) { - ChatMessagePacket chatMessagePacket = - new ChatMessagePacket(json, ChatMessagePacket.Position.SYSTEM_MESSAGE); + ChatMessagePacket chatMessagePacket = new ChatMessagePacket(json, MessageType.SYSTEM); PacketUtils.sendGroupedPacket(recipients, chatMessagePacket); } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java index 65f7b930f..4fd7a971f 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java @@ -1,6 +1,7 @@ package net.minestom.server.network.packet.server.play; import net.minestom.server.chat.JsonMessage; +import net.minestom.server.color.TeamFormat; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; @@ -39,7 +40,7 @@ public class TeamsPacket implements ServerPacket { /** * The color of the team */ - public int teamColor; + public TeamFormat teamFormat; /** * The prefix of the team */ @@ -53,6 +54,11 @@ public class TeamsPacket implements ServerPacket { */ public String[] entities; + + /** + * @deprecated Use {@link #teamFormat} + */ + @Deprecated public int teamColor = -1; /** * @deprecated Use {@link #teamDisplayName} */ @@ -83,7 +89,7 @@ public class TeamsPacket implements ServerPacket { writer.writeByte(this.friendlyFlags); writer.writeSizedString(this.nameTagVisibility.getIdentifier()); writer.writeSizedString(this.collisionRule.getIdentifier()); - writer.writeVarInt(this.teamColor); + writer.writeVarInt(this.teamColor != -1 ? this.teamColor : this.teamFormat.ordinal()); writer.writeSizedString(this.teamPrefixJson != null ? this.teamPrefixJson.toString() : this.teamPrefix); writer.writeSizedString(this.teamSuffixJson != null ? this.teamSuffixJson.toString() : this.teamSuffix); break; diff --git a/src/main/java/net/minestom/server/scoreboard/BelowNameTag.java b/src/main/java/net/minestom/server/scoreboard/BelowNameTag.java index d0b0466eb..a8cfe9162 100644 --- a/src/main/java/net/minestom/server/scoreboard/BelowNameTag.java +++ b/src/main/java/net/minestom/server/scoreboard/BelowNameTag.java @@ -1,5 +1,6 @@ package net.minestom.server.scoreboard; +import net.kyori.adventure.text.Component; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket; import net.minestom.server.network.player.PlayerConnection; @@ -30,15 +31,26 @@ public class BelowNameTag implements Scoreboard { * * @param name The objective name of the scoreboard * @param value The value of the scoreboard + * @deprecated Use {@link #BelowNameTag(String, Component)} */ + @Deprecated public BelowNameTag(String name, String value) { - this.objectiveName = BELOW_NAME_TAG_PREFIX + name; + this(name, Component.text(value)); + } + /** + * Creates a new below name scoreboard. + * + * @param name The objective name of the scoreboard + * @param value The value of the scoreboard + */ + public BelowNameTag(String name, Component value) { + this.objectiveName = BELOW_NAME_TAG_PREFIX + name; this.scoreboardObjectivePacket = this.getCreationObjectivePacket(value, ScoreboardObjectivePacket.Type.INTEGER); } @Override - public String getObjectiveName() { + public @NotNull String getObjectiveName() { return this.objectiveName; } diff --git a/src/main/java/net/minestom/server/scoreboard/Scoreboard.java b/src/main/java/net/minestom/server/scoreboard/Scoreboard.java index 4e648e1cc..e75178bf6 100644 --- a/src/main/java/net/minestom/server/scoreboard/Scoreboard.java +++ b/src/main/java/net/minestom/server/scoreboard/Scoreboard.java @@ -1,17 +1,36 @@ package net.minestom.server.scoreboard; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.audience.ForwardingAudience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; +import net.minestom.server.MinecraftServer; import net.minestom.server.Viewable; -import net.minestom.server.chat.ColoredText; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.DisplayScoreboardPacket; import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket; import net.minestom.server.network.packet.server.play.UpdateScorePacket; +import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; /** * This interface represents all scoreboard of Minecraft. */ -public interface Scoreboard extends Viewable { +public interface Scoreboard extends Viewable, ForwardingAudience { + + /** + * Creates a creation objective packet. + * + * @param value The value for the objective + * @param type The type for the objective + * @return the creation objective packet + * @deprecated Use {@link #getCreationObjectivePacket(Component, ScoreboardObjectivePacket.Type)} + */ + @Deprecated + @NotNull + default ScoreboardObjectivePacket getCreationObjectivePacket(String value, ScoreboardObjectivePacket.Type type) { + return this.getCreationObjectivePacket(Component.text(value), type); + } /** * Creates a creation objective packet. @@ -21,11 +40,11 @@ public interface Scoreboard extends Viewable { * @return the creation objective packet */ @NotNull - default ScoreboardObjectivePacket getCreationObjectivePacket(String value, ScoreboardObjectivePacket.Type type) { + default ScoreboardObjectivePacket getCreationObjectivePacket(Component value, ScoreboardObjectivePacket.Type type) { final ScoreboardObjectivePacket packet = new ScoreboardObjectivePacket(); packet.objectiveName = this.getObjectiveName(); packet.mode = 0; // Create Scoreboard - packet.objectiveValue = ColoredText.of(value); + packet.objectiveValue = MinecraftServer.getSerializationManager().serialize(value); packet.type = type; return packet; @@ -84,4 +103,8 @@ public interface Scoreboard extends Viewable { @NotNull String getObjectiveName(); + @Override + @NonNull default Iterable audiences() { + return this.getViewers(); + } } diff --git a/src/main/java/net/minestom/server/scoreboard/Sidebar.java b/src/main/java/net/minestom/server/scoreboard/Sidebar.java index bd53192cd..c016a3389 100644 --- a/src/main/java/net/minestom/server/scoreboard/Sidebar.java +++ b/src/main/java/net/minestom/server/scoreboard/Sidebar.java @@ -1,9 +1,10 @@ package net.minestom.server.scoreboard; import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; -import net.minestom.server.chat.ChatParser; -import net.minestom.server.chat.ColoredText; +import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; import net.minestom.server.chat.JsonMessage; +import net.minestom.server.color.TeamFormat; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.DisplayScoreboardPacket; import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket; @@ -27,7 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger; * and remove him later with {@link #removeViewer(Player)}. *

* Lines can be modified using their respective identifier using - * {@link #updateLineContent(String, JsonMessage)} and {@link #updateLineScore(String, int)}. + * {@link #updateLineContent(String, Component)} and {@link #updateLineScore(String, int)}. */ public class Sidebar implements Scoreboard { @@ -51,14 +52,25 @@ public class Sidebar implements Scoreboard { private final String objectiveName; - private String title; + private Component title; + + /** + * Creates a new sidebar + * + * @param title The title of the sidebar + * @deprecated Use {@link #Sidebar(Component)} + */ + @Deprecated + public Sidebar(@NotNull String title) { + this(Component.text(title)); + } /** * Creates a new sidebar * * @param title The title of the sidebar */ - public Sidebar(@NotNull String title) { + public Sidebar(@NotNull Component title) { this.title = title; this.objectiveName = SCOREBOARD_PREFIX + COUNTER.incrementAndGet(); @@ -73,14 +85,25 @@ public class Sidebar implements Scoreboard { * Changes the {@link Sidebar} title * * @param title The new sidebar title + * @deprecated Use {@link #setTitle(Component)} */ + @Deprecated public void setTitle(@NotNull String title) { + this.setTitle(Component.text(title)); + } + + /** + * Changes the {@link Sidebar} title + * + * @param title The new sidebar title + */ + public void setTitle(@NotNull Component title) { this.title = title; ScoreboardObjectivePacket scoreboardObjectivePacket = new ScoreboardObjectivePacket(); scoreboardObjectivePacket.objectiveName = objectiveName; scoreboardObjectivePacket.mode = 2; // Update display text - scoreboardObjectivePacket.objectiveValue = ColoredText.of(title); + scoreboardObjectivePacket.objectiveValue = MinecraftServer.getSerializationManager().serialize(title); scoreboardObjectivePacket.type = ScoreboardObjectivePacket.Type.INTEGER; sendPacketToViewers(scoreboardObjectivePacket); @@ -122,8 +145,20 @@ public class Sidebar implements Scoreboard { * * @param id The identifier of the {@link ScoreboardLine} * @param content The new content for the {@link ScoreboardLine} + * @deprecated Use {@link #updateLineContent(String, Component)} */ + @Deprecated public void updateLineContent(@NotNull String id, @NotNull JsonMessage content) { + this.updateLineContent(id, content.asComponent()); + } + + /** + * Updates a {@link ScoreboardLine} content through the given identifier. + * + * @param id The identifier of the {@link ScoreboardLine} + * @param content The new content for the {@link ScoreboardLine} + */ + public void updateLineContent(@NotNull String id, @NotNull Component content) { final ScoreboardLine scoreboardLine = getLine(id); if (scoreboardLine != null) { scoreboardLine.refreshContent(content); @@ -228,7 +263,7 @@ public class Sidebar implements Scoreboard { } @Override - public String getObjectiveName() { + public @NotNull String getObjectiveName() { return this.objectiveName; } @@ -244,7 +279,7 @@ public class Sidebar implements Scoreboard { /** * The content for the line */ - private final JsonMessage content; + private final Component content; /** * The score of the line */ @@ -261,7 +296,15 @@ public class Sidebar implements Scoreboard { */ private SidebarTeam sidebarTeam; + /** + * @deprecated Use {@link #ScoreboardLine(String, Component, int)} + */ + @Deprecated public ScoreboardLine(@NotNull String id, @NotNull JsonMessage content, int line) { + this(id, content.asComponent(), line); + } + + public ScoreboardLine(@NotNull String id, @NotNull Component content, int line) { this.id = id; this.content = content; this.line = line; @@ -283,9 +326,21 @@ public class Sidebar implements Scoreboard { * Gets the content of the line * * @return The line content + * @deprecated Use {@link #getContent()} */ @NotNull - public JsonMessage getContent() { + @Deprecated + public JsonMessage getContentJson() { + return JsonMessage.fromComponent(getContent()); + } + + /** + * Gets the content of the line + * + * @return The line content + */ + @NotNull + public Component getContent() { return sidebarTeam == null ? content : sidebarTeam.getPrefix(); } @@ -308,9 +363,9 @@ public class Sidebar implements Scoreboard { * Creates a new {@link SidebarTeam} */ private void createTeam() { - this.entityName = ChatParser.COLOR_CHAR + Integer.toHexString(colorName); + this.entityName = ((char) 0xA7) + Integer.toHexString(colorName); - this.sidebarTeam = new SidebarTeam(teamName, content, ColoredText.of(""), entityName); + this.sidebarTeam = new SidebarTeam(teamName, content, Component.empty(), entityName); } private void returnName(IntLinkedOpenHashSet colors) { @@ -366,7 +421,7 @@ public class Sidebar implements Scoreboard { * * @param content The new content */ - private void refreshContent(JsonMessage content) { + private void refreshContent(Component content) { this.sidebarTeam.refreshPrefix(content); } @@ -378,14 +433,14 @@ public class Sidebar implements Scoreboard { private static class SidebarTeam { private final String teamName; - private JsonMessage prefix, suffix; + private Component prefix, suffix; private final String entityName; - private final JsonMessage teamDisplayName = ColoredText.of("displaynametest"); + private final Component teamDisplayName = Component.text("displaynametest"); private final byte friendlyFlags = 0x00; private final TeamsPacket.NameTagVisibility nameTagVisibility = TeamsPacket.NameTagVisibility.NEVER; private final TeamsPacket.CollisionRule collisionRule = TeamsPacket.CollisionRule.NEVER; - private final int teamColor = 2; + private final TeamFormat teamformat = TeamFormat.DARK_GREEN; /** @@ -396,7 +451,7 @@ public class Sidebar implements Scoreboard { * @param suffix The team suffix * @param entityName The team entity name */ - private SidebarTeam(String teamName, JsonMessage prefix, JsonMessage suffix, String entityName) { + private SidebarTeam(String teamName, Component prefix, Component suffix, String entityName) { this.teamName = teamName; this.prefix = prefix; this.suffix = suffix; @@ -412,13 +467,13 @@ public class Sidebar implements Scoreboard { TeamsPacket teamsPacket = new TeamsPacket(); teamsPacket.teamName = teamName; teamsPacket.action = TeamsPacket.Action.CREATE_TEAM; - teamsPacket.teamDisplayName = teamDisplayName; + teamsPacket.teamDisplayName = MinecraftServer.getSerializationManager().serialize(teamDisplayName); teamsPacket.friendlyFlags = friendlyFlags; teamsPacket.nameTagVisibility = nameTagVisibility; teamsPacket.collisionRule = collisionRule; - teamsPacket.teamColor = teamColor; - teamsPacket.teamPrefix = prefix; - teamsPacket.teamSuffix = suffix; + teamsPacket.teamFormat = teamformat; + teamsPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(prefix); + teamsPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(suffix); teamsPacket.entities = new String[]{entityName}; return teamsPacket; } @@ -441,17 +496,17 @@ public class Sidebar implements Scoreboard { * @param prefix The new prefix * @return a {@link TeamsPacket} with the updated prefix */ - private TeamsPacket updatePrefix(JsonMessage prefix) { + private TeamsPacket updatePrefix(Component prefix) { TeamsPacket teamsPacket = new TeamsPacket(); teamsPacket.teamName = teamName; teamsPacket.action = TeamsPacket.Action.UPDATE_TEAM_INFO; - teamsPacket.teamDisplayName = teamDisplayName; + teamsPacket.teamDisplayName = MinecraftServer.getSerializationManager().serialize(teamDisplayName); teamsPacket.friendlyFlags = friendlyFlags; teamsPacket.nameTagVisibility = nameTagVisibility; teamsPacket.collisionRule = collisionRule; - teamsPacket.teamColor = teamColor; - teamsPacket.teamPrefix = prefix; - teamsPacket.teamSuffix = suffix; + teamsPacket.teamFormat = teamformat; + teamsPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(prefix); + teamsPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(suffix); return teamsPacket; } @@ -469,7 +524,7 @@ public class Sidebar implements Scoreboard { * * @return the prefix */ - private JsonMessage getPrefix() { + private Component getPrefix() { return prefix; } @@ -478,7 +533,7 @@ public class Sidebar implements Scoreboard { * * @param prefix The refreshed prefix */ - private void refreshPrefix(@NotNull JsonMessage prefix) { + private void refreshPrefix(@NotNull Component prefix) { this.prefix = prefix; } } diff --git a/src/main/java/net/minestom/server/scoreboard/TabList.java b/src/main/java/net/minestom/server/scoreboard/TabList.java index 5dff60ddc..c1c205036 100644 --- a/src/main/java/net/minestom/server/scoreboard/TabList.java +++ b/src/main/java/net/minestom/server/scoreboard/TabList.java @@ -1,5 +1,6 @@ package net.minestom.server.scoreboard; +import net.kyori.adventure.text.Component; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket; import net.minestom.server.network.player.PlayerConnection; @@ -55,7 +56,7 @@ public class TabList implements Scoreboard { PlayerConnection connection = player.getPlayerConnection(); if (result) { - connection.sendPacket(this.getCreationObjectivePacket("", this.type)); + connection.sendPacket(this.getCreationObjectivePacket(Component.empty(), this.type)); connection.sendPacket(this.getDisplayScoreboardPacket((byte) 0)); } diff --git a/src/main/java/net/minestom/server/scoreboard/Team.java b/src/main/java/net/minestom/server/scoreboard/Team.java index a342e836d..14beb15ee 100644 --- a/src/main/java/net/minestom/server/scoreboard/Team.java +++ b/src/main/java/net/minestom/server/scoreboard/Team.java @@ -3,12 +3,15 @@ package net.minestom.server.scoreboard; import com.google.common.collect.MapMaker; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; +import net.minestom.server.color.TeamFormat; import net.minestom.server.entity.LivingEntity; import net.minestom.server.entity.Player; +import net.minestom.server.instance.ChunkPopulator; import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.packet.server.play.TeamsPacket; import net.minestom.server.network.packet.server.play.TeamsPacket.CollisionRule; @@ -39,7 +42,7 @@ public class Team implements ForwardingAudience { /** * The display name of the team. */ - private JsonMessage teamDisplayName; + private Component teamDisplayName; /** * A BitMask. */ @@ -54,19 +57,19 @@ public class Team implements ForwardingAudience { private CollisionRule collisionRule; /** - * Used to color the name of players on the team
- * The color of a team defines how the names of the team members are visualized. + * Used to format the name of players on the team
+ * The format of a team defines how the names of the team members are visualized. */ - private ChatColor teamColor; + private TeamFormat teamFormat; /** * Shown before the names of the players who belong to this team. */ - private JsonMessage prefix; + private Component prefix; /** * Shown after the names of the player who belong to this team. */ - private JsonMessage suffix; + private Component suffix; private final Set playerMembers = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); private boolean isPlayerMembersUpToDate; @@ -79,14 +82,14 @@ public class Team implements ForwardingAudience { protected Team(@NotNull String teamName) { this.teamName = teamName; - this.teamDisplayName = ColoredText.of(""); + this.teamDisplayName = Component.empty(); this.friendlyFlags = 0x00; this.nameTagVisibility = NameTagVisibility.ALWAYS; this.collisionRule = CollisionRule.ALWAYS; - this.teamColor = ChatColor.WHITE; - this.prefix = ColoredText.of(""); - this.suffix = ColoredText.of(""); + this.teamFormat = TeamFormat.WHITE; + this.prefix = Component.empty(); + this.suffix = Component.empty(); this.members = new CopyOnWriteArraySet<>(); } @@ -143,8 +146,21 @@ public class Team implements ForwardingAudience { * Warning: This is only changed server side. * * @param teamDisplayName The new display name + * @deprecated Use {@link #setTeamDisplayName(Component)} */ + @Deprecated public void setTeamDisplayName(JsonMessage teamDisplayName) { + this.setTeamDisplayName(teamDisplayName.asComponent()); + } + + /** + * Changes the display name of the team. + *

+ * Warning: This is only changed server side. + * + * @param teamDisplayName The new display name + */ + public void setTeamDisplayName(Component teamDisplayName) { this.teamDisplayName = teamDisplayName; } @@ -152,8 +168,19 @@ public class Team implements ForwardingAudience { * Changes the display name of the team and sends an update packet. * * @param teamDisplayName The new display name + * @deprecated Use {@link #updateTeamDisplayName(Component)} */ + @Deprecated public void updateTeamDisplayName(JsonMessage teamDisplayName) { + this.updateTeamDisplayName(teamDisplayName.asComponent()); + } + + /** + * Changes the display name of the team and sends an update packet. + * + * @param teamDisplayName The new display name + */ + public void updateTeamDisplayName(Component teamDisplayName) { this.setTeamDisplayName(teamDisplayName); sendUpdatePacket(); } @@ -209,18 +236,43 @@ public class Team implements ForwardingAudience { * * @param color The new team color * @see #updateTeamColor(ChatColor) + * @deprecated Use {@link #setTeamFormat(TeamFormat)} */ + @Deprecated public void setTeamColor(@NotNull ChatColor color) { - this.teamColor = color; + this.setTeamFormat(color.asTeamFormat()); + } + + /** + * Changes the color of the team. + *

+ * Warning: This is only changed on the server side. + * + * @param format The new team color + * @see #updateTeamFormat(TeamFormat) + */ + public void setTeamFormat(@NotNull TeamFormat format) { + this.teamFormat = format; } /** * Changes the color of the team and sends an update packet. * * @param teamColor The new team color + * @deprecated Use {@link #updateTeamFormat(TeamFormat)} */ + @Deprecated public void updateTeamColor(@NotNull ChatColor teamColor) { - this.setTeamColor(teamColor); + this.updateTeamFormat(teamColor.asTeamFormat()); + } + + /** + * Changes the color of the team and sends an update packet. + * + * @param format The new team color + */ + public void updateTeamFormat(@NotNull TeamFormat format) { + this.setTeamFormat(format); sendUpdatePacket(); } @@ -230,8 +282,21 @@ public class Team implements ForwardingAudience { * Warning: This is only changed on the server side. * * @param prefix The new prefix + * @deprecated Use {@link #setPrefix(Component)} */ + @Deprecated public void setPrefix(JsonMessage prefix) { + this.setPrefix(prefix.asComponent()); + } + + /** + * Changes the prefix of the team. + *

+ * Warning: This is only changed on the server side. + * + * @param prefix The new prefix + */ + public void setPrefix(Component prefix) { this.prefix = prefix; } @@ -239,8 +304,19 @@ public class Team implements ForwardingAudience { * Changes the prefix of the team and sends an update packet. * * @param prefix The new prefix + * @deprecated Use {@link #updatePrefix(Component)} */ + @Deprecated public void updatePrefix(JsonMessage prefix) { + this.updatePrefix(prefix.asComponent()); + } + + /** + * Changes the prefix of the team and sends an update packet. + * + * @param prefix The new prefix + */ + public void updatePrefix(Component prefix) { this.setPrefix(prefix); sendUpdatePacket(); } @@ -251,8 +327,21 @@ public class Team implements ForwardingAudience { * Warning: This is only changed on the server side. * * @param suffix The new suffix + * @deprecated Use {@link #setSuffix(Component)} */ + @Deprecated public void setSuffix(JsonMessage suffix) { + this.setSuffix(suffix.asComponent()); + } + + /** + * Changes the suffix of the team. + *

+ * Warning: This is only changed on the server side. + * + * @param suffix The new suffix + */ + public void setSuffix(Component suffix) { this.suffix = suffix; } @@ -260,8 +349,19 @@ public class Team implements ForwardingAudience { * Changes the suffix of the team and sends an update packet. * * @param suffix The new suffix + * @deprecated Use {@link #updateSuffix(Component)} */ + @Deprecated public void updateSuffix(JsonMessage suffix) { + this.updateSuffix(suffix.asComponent()); + } + + /** + * Changes the suffix of the team and sends an update packet. + * + * @param suffix The new suffix + */ + public void updateSuffix(Component suffix) { this.setSuffix(suffix); sendUpdatePacket(); } @@ -306,13 +406,13 @@ public class Team implements ForwardingAudience { TeamsPacket teamsCreationPacket = new TeamsPacket(); teamsCreationPacket.teamName = teamName; teamsCreationPacket.action = TeamsPacket.Action.CREATE_TEAM; - teamsCreationPacket.teamDisplayName = this.teamDisplayName; + teamsCreationPacket.teamDisplayName = MinecraftServer.getSerializationManager().serialize(this.teamDisplayName); teamsCreationPacket.friendlyFlags = this.friendlyFlags; teamsCreationPacket.nameTagVisibility = this.nameTagVisibility; teamsCreationPacket.collisionRule = this.collisionRule; - teamsCreationPacket.teamColor = this.teamColor.getId(); - teamsCreationPacket.teamPrefix = this.prefix; - teamsCreationPacket.teamSuffix = this.suffix; + teamsCreationPacket.teamFormat = this.teamFormat; + teamsCreationPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(this.prefix); + teamsCreationPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(this.suffix); teamsCreationPacket.entities = this.members.toArray(new String[0]); return teamsCreationPacket; @@ -345,8 +445,19 @@ public class Team implements ForwardingAudience { * Gets the display name of the team. * * @return the display name + * @deprecated Use {@link #getTeamDisplayName()} */ - public JsonMessage getTeamDisplayName() { + @Deprecated + public JsonMessage getTeamDisplayNameJson() { + return JsonMessage.fromComponent(this.teamDisplayName); + } + + /** + * Gets the display name of the team. + * + * @return the display name + */ + public Component getTeamDisplayName() { return teamDisplayName; } @@ -383,10 +494,33 @@ public class Team implements ForwardingAudience { * Gets the color of the team. * * @return the team color + * @deprecated Use {@link #getTeamFormat()} */ + @Deprecated @NotNull public ChatColor getTeamColor() { - return teamColor; + return ChatColor.fromName(teamFormat.name()); + } + + /** + * Gets the color of the team. + * + * @return the team color + */ + @NotNull + public TeamFormat getTeamFormat() { + return teamFormat; + } + + /** + * Gets the prefix of the team. + * + * @return the team prefix + * @deprecated Use {@link #getPrefix()} + */ + @Deprecated + public JsonMessage getPrefixJson() { + return JsonMessage.fromComponent(prefix); } /** @@ -394,7 +528,7 @@ public class Team implements ForwardingAudience { * * @return the team prefix */ - public JsonMessage getPrefix() { + public Component getPrefix() { return prefix; } @@ -402,8 +536,19 @@ public class Team implements ForwardingAudience { * Gets the suffix of the team. * * @return the suffix team + * @deprecated Use {@link #getSuffix()} */ - public JsonMessage getSuffix() { + @Deprecated + public JsonMessage getSuffixJson() { + return JsonMessage.fromComponent(suffix); + } + + /** + * Gets the suffix of the team. + * + * @return the suffix team + */ + public Component getSuffix() { return suffix; } @@ -414,13 +559,13 @@ public class Team implements ForwardingAudience { final TeamsPacket updatePacket = new TeamsPacket(); updatePacket.teamName = this.teamName; updatePacket.action = TeamsPacket.Action.UPDATE_TEAM_INFO; - updatePacket.teamDisplayName = this.teamDisplayName; + updatePacket.teamDisplayName = MinecraftServer.getSerializationManager().serialize(this.teamDisplayName); updatePacket.friendlyFlags = this.friendlyFlags; updatePacket.nameTagVisibility = this.nameTagVisibility; updatePacket.collisionRule = this.collisionRule; - updatePacket.teamColor = this.teamColor.getId(); - updatePacket.teamPrefix = this.prefix; - updatePacket.teamSuffix = this.suffix; + updatePacket.teamFormat = this.teamFormat; + updatePacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(this.prefix); + updatePacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(this.suffix); PacketUtils.sendGroupedPacket(MinecraftServer.getConnectionManager().getOnlinePlayers(), updatePacket); } diff --git a/src/main/java/net/minestom/server/scoreboard/TeamBuilder.java b/src/main/java/net/minestom/server/scoreboard/TeamBuilder.java index 9a4ad4345..5ff35100a 100644 --- a/src/main/java/net/minestom/server/scoreboard/TeamBuilder.java +++ b/src/main/java/net/minestom/server/scoreboard/TeamBuilder.java @@ -1,8 +1,9 @@ package net.minestom.server.scoreboard; +import net.kyori.adventure.text.Component; import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; +import net.minestom.server.color.TeamFormat; import net.minestom.server.network.packet.server.play.TeamsPacket.CollisionRule; import net.minestom.server.network.packet.server.play.TeamsPacket.NameTagVisibility; @@ -51,9 +52,23 @@ public class TeamBuilder { * * @param prefix The new prefix * @return this builder, for chaining + * @deprecated Use {@link #updatePrefix(Component)}} */ + @Deprecated public TeamBuilder updatePrefix(String prefix) { - return this.updatePrefix(ColoredText.of(prefix)); + return this.updatePrefix(Component.text(prefix)); + } + + /** + * Updates the prefix of the {@link Team}. + * + * @param prefix The new prefix + * @return this builder, for chaining + * @deprecated Use {@link #updatePrefix(Component)} + */ + @Deprecated + public TeamBuilder updatePrefix(JsonMessage prefix) { + return this.updatePrefix(prefix.asComponent()); } /** @@ -62,7 +77,7 @@ public class TeamBuilder { * @param prefix The new prefix * @return this builder, for chaining */ - public TeamBuilder updatePrefix(JsonMessage prefix) { + public TeamBuilder updatePrefix(Component prefix) { this.team.updatePrefix(prefix); return this; } @@ -72,9 +87,21 @@ public class TeamBuilder { * * @param color The new color * @return this builder, for chaining + * @deprecated Use {@link #updateTeamFormat(TeamFormat)} */ + @Deprecated public TeamBuilder updateTeamColor(ChatColor color) { - this.team.updateTeamColor(color); + return this.updateTeamFormat(color.asTeamFormat()); + } + + /** + * Updates the color of the {@link Team}. + * + * @param color The new color + * @return this builder, for chaining + */ + public TeamBuilder updateTeamFormat(TeamFormat color) { + this.team.updateTeamFormat(color); return this; } @@ -83,9 +110,23 @@ public class TeamBuilder { * * @param suffix The new suffix * @return this builder, for chaining + * @deprecated Use {@link #updateSuffix(Component)} */ + @Deprecated public TeamBuilder updateSuffix(String suffix) { - return updateSuffix(ColoredText.of(suffix)); + return this.updateSuffix(Component.text(suffix)); + } + + /** + * Updates the suffix of the {@link Team}. + * + * @param suffix The new suffix + * @return this builder, for chaining + * @deprecated Use {@link #updateSuffix(Component)} + */ + @Deprecated + public TeamBuilder updateSuffix(JsonMessage suffix) { + return this.updateSuffix(suffix.asComponent()); } /** @@ -94,7 +135,7 @@ public class TeamBuilder { * @param suffix The new suffix * @return this builder, for chaining */ - public TeamBuilder updateSuffix(JsonMessage suffix) { + public TeamBuilder updateSuffix(Component suffix) { this.team.updateSuffix(suffix); return this; } @@ -104,9 +145,23 @@ public class TeamBuilder { * * @param displayName The new display name * @return this builder, for chaining + * @deprecated Use {@link #updateTeamDisplayName(Component)} */ + @Deprecated public TeamBuilder updateTeamDisplayName(String displayName) { - return this.updateTeamDisplayName(ColoredText.of(displayName)); + return this.updateTeamDisplayName(Component.text(displayName)); + } + + /** + * Updates the display name of the {@link Team}. + * + * @param displayName The new display name + * @return this builder, for chaining + * @deprecated Use {@link #updateTeamDisplayName(Component)} + */ + @Deprecated + public TeamBuilder updateTeamDisplayName(JsonMessage displayName) { + return this.updateTeamDisplayName(displayName.asComponent()); } /** @@ -115,7 +170,7 @@ public class TeamBuilder { * @param displayName The new display name * @return this builder, for chaining */ - public TeamBuilder updateTeamDisplayName(JsonMessage displayName) { + public TeamBuilder updateTeamDisplayName(Component displayName) { this.team.updateTeamDisplayName(displayName); return this; } @@ -178,9 +233,25 @@ public class TeamBuilder { * * @param prefix The new prefix * @return this builder, for chaining + * @deprecated Use {@link #prefix(Component)} */ + @Deprecated public TeamBuilder prefix(String prefix) { - return this.prefix(ColoredText.of(prefix)); + return this.prefix(Component.text(prefix)); + } + + /** + * Changes the prefix of the {@link Team} without an update packet. + *

+ * Warning: If you do not call {@link #updateTeamPacket()}, this is only changed of the server side. + * + * @param prefix The new prefix + * @return this builder, for chaining + * @deprecated Use {@link #prefix(Component)} + */ + @Deprecated + public TeamBuilder prefix(JsonMessage prefix) { + return this.prefix(prefix.asComponent()); } /** @@ -191,7 +262,7 @@ public class TeamBuilder { * @param prefix The new prefix * @return this builder, for chaining */ - public TeamBuilder prefix(JsonMessage prefix) { + public TeamBuilder prefix(Component prefix) { this.team.setPrefix(prefix); return this; } @@ -203,10 +274,25 @@ public class TeamBuilder { * * @param suffix The new suffix * @return this builder, for chaining + * @deprecated Use {@link #suffix(Component)} */ + @Deprecated public TeamBuilder suffix(String suffix) { - this.team.setSuffix(ColoredText.of(suffix)); - return this; + return this.suffix(Component.text(suffix)); + } + + /** + * Changes the suffix of the {@link Team} without an update packet. + *

+ * Warning: If you do not call {@link #updateTeamPacket()}, this is only changed of the server side. + * + * @param suffix The new suffix + * @return this builder, for chaining + * @deprecated Use {@link #suffix(Component)} + */ + @Deprecated + public TeamBuilder suffix(JsonMessage suffix) { + return this.suffix(suffix.asComponent()); } /** @@ -217,7 +303,7 @@ public class TeamBuilder { * @param suffix The new suffix * @return this builder, for chaining */ - public TeamBuilder suffix(JsonMessage suffix) { + public TeamBuilder suffix(Component suffix) { this.team.setSuffix(suffix); return this; } @@ -229,9 +315,23 @@ public class TeamBuilder { * * @param color The new team color * @return this builder, for chaining + * @deprecated Use {@link #teamFormat(TeamFormat)} */ + @Deprecated public TeamBuilder teamColor(ChatColor color) { - this.team.setTeamColor(color); + return this.teamFormat(color.asTeamFormat()); + } + + /** + * Changes the format of the {@link Team} without an update packet. + *

+ * Warning: If you do not call {@link #updateTeamPacket()}, this is only changed of the server side. + * + * @param format The new team format + * @return this builder, for chaining + */ + public TeamBuilder teamFormat(TeamFormat format) { + this.team.setTeamFormat(format); return this; } @@ -242,9 +342,25 @@ public class TeamBuilder { * * @param displayName The new display name * @return this builder, for chaining + * @deprecated Use {@link #teamDisplayName(Component)} */ + @Deprecated public TeamBuilder teamDisplayName(String displayName) { - return this.teamDisplayName(ColoredText.of(displayName)); + return this.teamDisplayName(Component.text(displayName)); + } + + /** + * Changes the display name of the {@link Team} without an update packet. + *

+ * Warning: If you do not call {@link #updateTeamPacket()}, this is only changed of the server side. + * + * @param displayName The new display name + * @return this builder, for chaining + * @deprecated Use {@link #teamDisplayName(Component)} + */ + @Deprecated + public TeamBuilder teamDisplayName(JsonMessage displayName) { + return this.teamDisplayName(displayName.asComponent()); } /** @@ -255,7 +371,7 @@ public class TeamBuilder { * @param displayName The new display name * @return this builder, for chaining */ - public TeamBuilder teamDisplayName(JsonMessage displayName) { + public TeamBuilder teamDisplayName(Component displayName) { this.team.setTeamDisplayName(displayName); return this; } diff --git a/src/main/java/net/minestom/server/scoreboard/TeamManager.java b/src/main/java/net/minestom/server/scoreboard/TeamManager.java index a9d02beec..c76d61423 100644 --- a/src/main/java/net/minestom/server/scoreboard/TeamManager.java +++ b/src/main/java/net/minestom/server/scoreboard/TeamManager.java @@ -1,8 +1,10 @@ package net.minestom.server.scoreboard; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.JsonMessage; +import net.minestom.server.color.TeamFormat; import net.minestom.server.entity.LivingEntity; import net.minestom.server.entity.Player; import net.minestom.server.network.ConnectionManager; @@ -97,9 +99,24 @@ public final class TeamManager { * @param teamColor The team color * @param suffix The team suffix * @return the created {@link Team} with a prefix, teamColor and suffix + * @deprecated Use {@link #createTeam(String, Component, TeamFormat, Component)} */ + @Deprecated public Team createTeam(String name, JsonMessage prefix, ChatColor teamColor, JsonMessage suffix) { - return this.createBuilder(name).prefix(prefix).teamColor(teamColor).suffix(suffix).updateTeamPacket().build(); + return this.createTeam(name, prefix.asComponent(), teamColor.asTeamFormat(), suffix.asComponent()); + } + + /** + * Creates a {@link Team} with the registry name, prefix, suffix and the team format + * + * @param name The registry name + * @param prefix The team prefix + * @param teamFormat The team format + * @param suffix The team suffix + * @return the created {@link Team} with a prefix, teamColor and suffix + */ + public Team createTeam(String name, Component prefix, TeamFormat teamFormat, Component suffix) { + return this.createBuilder(name).prefix(prefix).teamFormat(teamFormat).suffix(suffix).updateTeamPacket().build(); } /** @@ -111,9 +128,25 @@ public final class TeamManager { * @param teamColor The team color * @param suffix The team suffix * @return the created {@link Team} with a prefix, teamColor, suffix and the display name + * @deprecated Use {@link #createTeam(String, Component, Component, TeamFormat, Component)} */ + @Deprecated public Team createTeam(String name, JsonMessage displayName, JsonMessage prefix, ChatColor teamColor, JsonMessage suffix) { - return this.createBuilder(name).teamDisplayName(displayName).prefix(prefix).teamColor(teamColor).suffix(suffix).updateTeamPacket().build(); + return this.createTeam(name, displayName.asComponent(), prefix.asComponent(), teamColor.asTeamFormat(), suffix.asComponent()); + } + + /** + * Creates a {@link Team} with the registry name, display name, prefix, suffix and the team colro + * + * @param name The registry name + * @param displayName The display name + * @param prefix The team prefix + * @param teamFormat The team color + * @param suffix The team suffix + * @return the created {@link Team} with a prefix, teamColor, suffix and the display name + */ + public Team createTeam(String name, Component displayName, Component prefix, TeamFormat teamFormat, Component suffix) { + return this.createBuilder(name).teamDisplayName(displayName).prefix(prefix).teamFormat(teamFormat).suffix(suffix).updateTeamPacket().build(); } /** diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java index e63446a67..d46e21736 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -1,5 +1,7 @@ package net.minestom.server.utils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minestom.server.MinecraftServer; import net.minestom.server.attribute.Attribute; import net.minestom.server.attribute.AttributeOperation; @@ -130,14 +132,14 @@ public final class NBTUtils { final NBTCompound display = nbt.getCompound("display"); if (display.containsKey("Name")) { final String rawName = display.getString("Name"); - final ColoredText displayName = ChatParser.toColoredText(rawName); + final Component displayName = GsonComponentSerializer.gson().deserialize(rawName); item.setDisplayName(displayName); } if (display.containsKey("Lore")) { NBTList loreList = display.getList("Lore"); - List lore = new ArrayList<>(); + List lore = new ArrayList<>(); for (NBTString s : loreList) { - lore.add(ChatParser.toColoredText(s.getValue())); + lore.add(GsonComponentSerializer.gson().deserialize(s.getValue())); } item.setLore(lore); } @@ -300,11 +302,11 @@ public final class NBTUtils { } if (hasLore) { - final List lore = itemStack.getLore(); + final List lore = itemStack.getLore(); final NBTList loreNBT = new NBTList<>(NBTTypes.TAG_String); - for (JsonMessage line : lore) { - loreNBT.add(new NBTString(line.toString())); + for (Component line : lore) { + loreNBT.add(new NBTString(GsonComponentSerializer.gson().serialize(line))); } displayNBT.set("Lore", loreNBT); } diff --git a/src/main/java/net/minestom/server/utils/binary/BinaryReader.java b/src/main/java/net/minestom/server/utils/binary/BinaryReader.java index b8cb1b97e..5b37b31fa 100644 --- a/src/main/java/net/minestom/server/utils/binary/BinaryReader.java +++ b/src/main/java/net/minestom/server/utils/binary/BinaryReader.java @@ -2,6 +2,8 @@ package net.minestom.server.utils.binary; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minestom.server.chat.JsonMessage; import net.minestom.server.item.ItemStack; import net.minestom.server.utils.BlockPosition; @@ -149,11 +151,20 @@ public class BinaryReader extends InputStream { return itemStack; } + /** + * Use {@link #readComponent(int)} + */ + @Deprecated public JsonMessage readJsonMessage(int maxLength) { final String jsonObject = readSizedString(maxLength); return new JsonMessage.RawJsonMessage(jsonObject); } + public Component readComponent(int maxLength) { + final String jsonObject = readSizedString(maxLength); + return GsonComponentSerializer.gson().deserialize(jsonObject); + } + public ByteBuf getBuffer() { return buffer; } From 87b30a836144610fb846808c991b8600b9ee17ae Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 13:39:16 +0000 Subject: [PATCH 083/364] correct SerializationManager method contracts --- .../server/adventure/SerializationManager.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/minestom/server/adventure/SerializationManager.java b/src/main/java/net/minestom/server/adventure/SerializationManager.java index 01862c10e..ecc87ff07 100644 --- a/src/main/java/net/minestom/server/adventure/SerializationManager.java +++ b/src/main/java/net/minestom/server/adventure/SerializationManager.java @@ -126,8 +126,8 @@ public class SerializationManager { * * @return the serialized string */ - @Contract("null -> null") - public @Nullable String serialize(@Nullable Component component) { + @Contract("null -> null; !null -> !null") + public String serialize(@Nullable Component component) { return this.serialize(component, this.defaultLocale); } @@ -142,8 +142,8 @@ public class SerializationManager { * * @return the serialized string */ - @Contract("null, _ -> null") - public @Nullable String serialize(@Nullable Component component, @NotNull Localizable localizable) { + @Contract("null, _ -> null; !null, _ -> !null") + public String serialize(@Nullable Component component, @NotNull Localizable localizable) { return this.serialize(component, Objects.requireNonNullElse(localizable.getLocale(), this.defaultLocale)); } @@ -158,8 +158,8 @@ public class SerializationManager { * * @return the serialized string */ - @Contract("null, _ -> null") - public @Nullable String serialize(@Nullable Component component, @NotNull Locale locale) { + @Contract("null, _ -> null; !null, _ -> !null") + public String serialize(@Nullable Component component, @NotNull Locale locale) { if (component == null) { return null; } From f2897eedcaad763d05f5c69c51389b596daa7c9b Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 13:41:25 +0000 Subject: [PATCH 084/364] fix title ticks --- .../packet/server/play/TitlePacket.java | 5 +++-- .../net/minestom/server/utils/TickUtils.java | 22 ++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) 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 98b2f1961..262423b8b 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 @@ -115,8 +115,9 @@ public class TitlePacket implements ServerPacket { // 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()))); + packets.add(new TitlePacket(TickUtils.fromDuration(times.fadeIn(), TickUtils.CLIENT_TICK_MS), + TickUtils.fromDuration(times.stay(), TickUtils.CLIENT_TICK_MS), + TickUtils.fromDuration(times.fadeOut(), TickUtils.CLIENT_TICK_MS))); } return packets; diff --git a/src/main/java/net/minestom/server/utils/TickUtils.java b/src/main/java/net/minestom/server/utils/TickUtils.java index 6b6bfcc89..aa5c98500 100644 --- a/src/main/java/net/minestom/server/utils/TickUtils.java +++ b/src/main/java/net/minestom/server/utils/TickUtils.java @@ -10,6 +10,15 @@ import java.time.Duration; * Tick related utilities. */ public class TickUtils { + /** + * Number of ticks per second for the default Java-edition client. + */ + public static final int CLIENT_TPS = 20; + + /** + * Length of time per tick for the default Java-edition client. + */ + public static final int CLIENT_TICK_MS = 50; /** * Creates a number of ticks from a given duration, based on {@link MinecraftServer#TICK_MS}. @@ -18,8 +27,19 @@ public class TickUtils { * @throws IllegalArgumentException if duration is negative */ public static int fromDuration(@NotNull Duration duration) { + return TickUtils.fromDuration(duration, MinecraftServer.TICK_MS); + } + + /** + * Creates a number of ticks from a given duration. + * @param duration the duration + * @param msPerTick the number of milliseconds per tick + * @return the number of ticks + * @throws IllegalArgumentException if duration is negative + */ + public static int fromDuration(@NotNull Duration duration, int msPerTick) { Validate.isTrue(!duration.isNegative(), "Duration cannot be negative"); - return (int) (duration.toMillis() / MinecraftServer.TICK_MS); + return (int) (duration.toMillis() / msPerTick); } } From 35f9161d19f909b03b31e4f983ecf05735a34932 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 13:41:41 +0000 Subject: [PATCH 085/364] update demos --- src/test/java/demo/commands/BookCommand.java | 27 +++++++------------ .../java/demo/commands/DimensionCommand.java | 7 ++--- .../java/demo/commands/GamemodeCommand.java | 16 ++++++----- .../java/demo/commands/HealthCommand.java | 15 ++++++----- src/test/java/demo/commands/HorseCommand.java | 9 ++++--- .../demo/commands/LoadExtensionCommand.java | 21 ++++++++------- .../java/demo/commands/PlayersCommand.java | 9 ++++--- .../java/demo/commands/PotionCommand.java | 7 ++--- .../demo/commands/ReloadExtensionCommand.java | 9 ++++--- src/test/java/demo/commands/ShootCommand.java | 7 ++--- .../java/demo/commands/TeleportCommand.java | 7 ++--- src/test/java/demo/commands/TestCommand.java | 4 ++- src/test/java/demo/commands/TitleCommand.java | 15 ++++------- .../demo/commands/UnloadExtensionCommand.java | 9 ++++--- 14 files changed, 83 insertions(+), 79 deletions(-) diff --git a/src/test/java/demo/commands/BookCommand.java b/src/test/java/demo/commands/BookCommand.java index 9744075a3..6381d1f4c 100644 --- a/src/test/java/demo/commands/BookCommand.java +++ b/src/test/java/demo/commands/BookCommand.java @@ -1,14 +1,12 @@ package demo.commands; -import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; +import net.kyori.adventure.inventory.Book; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.CommandContext; import net.minestom.server.entity.Player; -import net.minestom.server.item.metadata.WrittenBookMeta; - -import java.util.List; public class BookCommand extends Command { public BookCommand() { @@ -21,7 +19,7 @@ public class BookCommand extends Command { private boolean playerCondition(CommandSender sender, String commandString) { if (!sender.isPlayer()) { - sender.sendMessage("The command is only available for players"); + sender.sendMessage(Component.text("The command is only available for players")); return false; } return true; @@ -30,16 +28,11 @@ public class BookCommand extends Command { private void execute(CommandSender sender, CommandContext context) { Player player = sender.asPlayer(); - final WrittenBookMeta bookMeta = new WrittenBookMeta(); - bookMeta.setAuthor(player.getUsername()); - bookMeta.setGeneration(WrittenBookMeta.WrittenBookGeneration.ORIGINAL); - bookMeta.setTitle(player.getUsername() + "'s Book"); - bookMeta.setPages(List.of( - ColoredText.of(ChatColor.RED, "Page one"), - ColoredText.of(ChatColor.BRIGHT_GREEN, "Page two"), - ColoredText.of(ChatColor.BLUE, "Page three") - )); - - player.openBook(bookMeta); + player.openBook(Book.builder() + .author(Component.text(player.getUsername())) + .title(Component.text(player.getUsername() + "'s Book")) + .pages(Component.text("Page one", NamedTextColor.RED), + Component.text("Page two", NamedTextColor.GREEN), + Component.text("Page three", NamedTextColor.BLUE))); } } diff --git a/src/test/java/demo/commands/DimensionCommand.java b/src/test/java/demo/commands/DimensionCommand.java index 285fba90f..f590a0adf 100644 --- a/src/test/java/demo/commands/DimensionCommand.java +++ b/src/test/java/demo/commands/DimensionCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandProcessor; import net.minestom.server.command.CommandSender; @@ -38,11 +39,11 @@ public class DimensionCommand implements CommandProcessor { Optional targetInstance = MinecraftServer.getInstanceManager().getInstances().stream().filter(in -> in.getDimensionType() == targetDimensionType).findFirst(); if (targetInstance.isPresent()) { - player.sendMessage("You were in " + instance.getDimensionType()); + player.sendMessage(Component.text("You were in " + instance.getDimensionType())); player.setInstance(targetInstance.get()); - player.sendMessage("You are now in " + targetDimensionType); + player.sendMessage(Component.text("You are now in " + targetDimensionType)); } else { - player.sendMessage("Could not find instance with dimension " + targetDimensionType); + player.sendMessage(Component.text("Could not find instance with dimension " + targetDimensionType)); } return true; diff --git a/src/test/java/demo/commands/GamemodeCommand.java b/src/test/java/demo/commands/GamemodeCommand.java index 9a25ce049..540ceb5ae 100644 --- a/src/test/java/demo/commands/GamemodeCommand.java +++ b/src/test/java/demo/commands/GamemodeCommand.java @@ -1,5 +1,7 @@ package demo.commands; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.CommandContext; @@ -36,7 +38,9 @@ public class GamemodeCommand extends Command { } private void usage(CommandSender sender, CommandContext context) { - sender.sendMessage("Usage: /gamemode [player] "); + sender.sendMessage(Component.text("Usage: /gamemode [player] ") + .hoverEvent(Component.text("Click to get this command.")) + .clickEvent(ClickEvent.suggestCommand("/gamemode player gamemode"))); } private void executeOnSelf(CommandSender sender, CommandContext context) { @@ -45,7 +49,7 @@ public class GamemodeCommand extends Command { GameMode gamemode = context.get("gamemode"); assert gamemode != null; // mode is not supposed to be null, because gamemodeName will be valid player.setGameMode(gamemode); - player.sendMessage("You are now playing in " + gamemode.toString().toLowerCase()); + player.sendMessage(Component.text("You are now playing in " + gamemode.toString().toLowerCase())); } private void executeOnOther(CommandSender sender, CommandContext context) { @@ -55,20 +59,20 @@ public class GamemodeCommand extends Command { assert gamemode != null; // mode is not supposed to be null, because gamemodeName will be valid assert target != null; target.setGameMode(gamemode); - target.sendMessage("You are now playing in " + gamemode.toString().toLowerCase()); + target.sendMessage(Component.text("You are now playing in " + gamemode.toString().toLowerCase())); } private void targetCallback(CommandSender sender, ArgumentSyntaxException exception) { - sender.sendMessage("'" + exception.getInput() + "' is not a valid player name."); + sender.sendMessage(Component.text("'" + exception.getInput() + "' is not a valid player name.")); } private void gameModeCallback(CommandSender sender, ArgumentSyntaxException exception) { - sender.sendMessage("'" + exception.getInput() + "' is not a valid gamemode!"); + sender.sendMessage(Component.text("'" + exception.getInput() + "' is not a valid gamemode!")); } private boolean isAllowed(CommandSender sender, String commandString) { if (!sender.isPlayer()) { - sender.sendMessage("The command is only available for player"); + sender.sendMessage(Component.text("The command is only available for player")); return false; } return true; diff --git a/src/test/java/demo/commands/HealthCommand.java b/src/test/java/demo/commands/HealthCommand.java index 7f20ebc74..d8279bb82 100644 --- a/src/test/java/demo/commands/HealthCommand.java +++ b/src/test/java/demo/commands/HealthCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.CommandContext; @@ -30,18 +31,18 @@ public class HealthCommand extends Command { private boolean condition(CommandSender sender, String commandString) { if (!sender.isPlayer()) { - sender.sendMessage("The command is only available for player"); + sender.sendMessage(Component.text("The command is only available for player")); return false; } return true; } private void defaultExecutor(CommandSender sender, CommandContext context) { - sender.sendMessage("Correct usage: health [set/add] [number]"); + sender.sendMessage(Component.text("Correct usage: health [set/add] [number]")); } private void onModeError(CommandSender sender, ArgumentSyntaxException exception) { - sender.sendMessage("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by 'set' or 'add'"); + sender.sendMessage(Component.text("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by 'set' or 'add'")); } private void onValueError(CommandSender sender, ArgumentSyntaxException exception) { @@ -49,16 +50,16 @@ public class HealthCommand extends Command { final String input = exception.getInput(); switch (error) { case ArgumentNumber.NOT_NUMBER_ERROR: - sender.sendMessage("SYNTAX ERROR: '" + input + "' isn't a number!"); + sender.sendMessage(Component.text("SYNTAX ERROR: '" + input + "' isn't a number!")); break; case ArgumentNumber.RANGE_ERROR: - sender.sendMessage("SYNTAX ERROR: " + input + " is not between 0 and 100"); + sender.sendMessage(Component.text("SYNTAX ERROR: " + input + " is not between 0 and 100")); break; } } private void sendSuggestionMessage(CommandSender sender, CommandContext context) { - sender.sendMessage("/health " + context.get("mode") + " [Integer]"); + sender.sendMessage(Component.text("/health " + args.get("mode") + " [Integer]")); } private void onHealthCommand(CommandSender sender, CommandContext context) { @@ -75,7 +76,7 @@ public class HealthCommand extends Command { break; } - player.sendMessage("You have now " + player.getHealth() + " health"); + player.sendMessage(Component.text("You have now " + player.getHealth() + " health")); } } \ No newline at end of file diff --git a/src/test/java/demo/commands/HorseCommand.java b/src/test/java/demo/commands/HorseCommand.java index 34f20b4cb..72485d8a1 100644 --- a/src/test/java/demo/commands/HorseCommand.java +++ b/src/test/java/demo/commands/HorseCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.CommandContext; @@ -31,7 +32,7 @@ public class HorseCommand extends Command { private boolean condition(CommandSender sender, String commandString) { if (!sender.isPlayer()) { - sender.sendMessage("The command is only available for player"); + sender.sendMessage(Component.text("The command is only available for player")); return false; } return true; @@ -42,21 +43,21 @@ public class HorseCommand extends Command { } private void onBabyError(CommandSender sender, ArgumentSyntaxException exception) { - sender.sendMessage("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by 'true' or 'false'"); + sender.sendMessage(Component.text("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by 'true' or 'false'")); } private void onMarkingError(CommandSender sender, ArgumentSyntaxException exception) { String values = Stream.of(HorseMeta.Marking.values()) .map(value -> "'" + value.name().toLowerCase(Locale.ROOT) + "'") .collect(Collectors.joining(", ")); - sender.sendMessage("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by " + values + "."); + sender.sendMessage(Component.text("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by " + values + ".")); } private void onColorError(CommandSender sender, ArgumentSyntaxException exception) { String values = Stream.of(HorseMeta.Color.values()) .map(value -> "'" + value.name().toLowerCase(Locale.ROOT) + "'") .collect(Collectors.joining(", ")); - sender.sendMessage("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by " + values + "."); + sender.sendMessage(Component.text("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by " + values + ".")); } private void onHorseCommand(CommandSender sender, CommandContext context) { diff --git a/src/test/java/demo/commands/LoadExtensionCommand.java b/src/test/java/demo/commands/LoadExtensionCommand.java index 7f39cf47b..e5440920c 100644 --- a/src/test/java/demo/commands/LoadExtensionCommand.java +++ b/src/test/java/demo/commands/LoadExtensionCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; @@ -26,42 +27,42 @@ public class LoadExtensionCommand extends Command { } private void usage(CommandSender sender, CommandContext context) { - sender.sendMessage("Usage: /load "); + sender.sendMessage(Component.text("Usage: /load ")); } private void execute(CommandSender sender, CommandContext context) { String name = join(context.getStringArray("extensionName")); - sender.sendMessage("extensionFile = " + name + "...."); + sender.sendMessage(Component.text("extensionFile = "+name+"....")); ExtensionManager extensionManager = MinecraftServer.getExtensionManager(); Path extensionFolder = extensionManager.getExtensionFolder().toPath().toAbsolutePath(); Path extensionJar = extensionFolder.resolve(name); try { - if (!extensionJar.toFile().getCanonicalPath().startsWith(extensionFolder.toFile().getCanonicalPath())) { - sender.sendMessage("File name '" + name + "' does not represent a file inside the extensions folder. Will not load"); + if(!extensionJar.toFile().getCanonicalPath().startsWith(extensionFolder.toFile().getCanonicalPath())) { + sender.sendMessage(Component.text("File name '"+name+"' does not represent a file inside the extensions folder. Will not load")); return; } } catch (IOException e) { e.printStackTrace(); - sender.sendMessage("Failed to load extension: " + e.getMessage()); + sender.sendMessage(Component.text("Failed to load extension: "+e.getMessage())); return; } try { boolean managed = extensionManager.loadDynamicExtension(extensionJar.toFile()); - if (managed) { - sender.sendMessage("Extension loaded!"); + if(managed) { + sender.sendMessage(Component.text("Extension loaded!")); } else { - sender.sendMessage("Failed to load extension, check your logs."); + sender.sendMessage(Component.text("Failed to load extension, check your logs.")); } } catch (Exception e) { e.printStackTrace(); - sender.sendMessage("Failed to load extension: " + e.getMessage()); + sender.sendMessage(Component.text("Failed to load extension: "+e.getMessage())); } } private void extensionCallback(CommandSender sender, ArgumentSyntaxException exception) { - sender.sendMessage("'" + exception.getInput() + "' is not a valid extension name!"); + sender.sendMessage(Component.text("'" + exception.getInput() + "' is not a valid extension name!")); } private String join(String[] extensionNameParts) { diff --git a/src/test/java/demo/commands/PlayersCommand.java b/src/test/java/demo/commands/PlayersCommand.java index 4efe05a0c..7567cbb98 100644 --- a/src/test/java/demo/commands/PlayersCommand.java +++ b/src/test/java/demo/commands/PlayersCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; @@ -19,17 +20,17 @@ public class PlayersCommand extends Command { private void usage(CommandSender sender, CommandContext context) { final Collection players = MinecraftServer.getConnectionManager().getOnlinePlayers(); final int playerCount = players.size(); - sender.sendMessage("Total players: " + playerCount); + sender.sendMessage(Component.text("Total players: " + playerCount)); final int limit = 15; if (playerCount <= limit) { for (final Player player : players) { - sender.sendMessage(player.getUsername()); + sender.sendMessage(Component.text(player.getUsername())); } } else { for (final Player player : players.stream().limit(limit).collect(Collectors.toList())) { - sender.sendMessage(player.getUsername()); + sender.sendMessage(Component.text(player.getUsername())); } - sender.sendMessage("..."); + sender.sendMessage(Component.text("...")); } } diff --git a/src/test/java/demo/commands/PotionCommand.java b/src/test/java/demo/commands/PotionCommand.java index 2a38b31b3..e89ba2151 100644 --- a/src/test/java/demo/commands/PotionCommand.java +++ b/src/test/java/demo/commands/PotionCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; @@ -17,7 +18,7 @@ public class PotionCommand extends Command { setCondition(this::condition); setDefaultExecutor(((sender, args) -> { - sender.sendMessage("Usage: /potion [type] [duration (seconds)]"); + sender.sendMessage(Component.text("Usage: /potion [type] [duration (seconds)]")); })); var potionArg = ArgumentType.Potion("potion"); @@ -28,7 +29,7 @@ public class PotionCommand extends Command { private boolean condition(CommandSender sender, String commandString) { if (!sender.isPlayer()) { - sender.sendMessage("The command is only available for players"); + sender.sendMessage(Component.text("The command is only available for players")); return false; } return true; @@ -39,7 +40,7 @@ public class PotionCommand extends Command { final PotionEffect potion = context.get("potion"); final int duration = context.get("duration"); - player.sendMessage(player.getActiveEffects().toString()); + player.sendMessage(Component.text(player.getActiveEffects().toString())); player.addEffect(new Potion( potion, (byte) 0, diff --git a/src/test/java/demo/commands/ReloadExtensionCommand.java b/src/test/java/demo/commands/ReloadExtensionCommand.java index 198a2cf55..ba5bdd388 100644 --- a/src/test/java/demo/commands/ReloadExtensionCommand.java +++ b/src/test/java/demo/commands/ReloadExtensionCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; @@ -42,12 +43,12 @@ public class ReloadExtensionCommand extends Command { } private void usage(CommandSender sender, CommandContext context) { - sender.sendMessage("Usage: /reload "); + sender.sendMessage(Component.text("Usage: /reload ")); } private void execute(CommandSender sender, CommandContext context) { String name = join(context.getStringArray("extensionName")); - sender.sendMessage("extensionName = " + name + "...."); + sender.sendMessage(Component.text("extensionName = " + name + "....")); ExtensionManager extensionManager = MinecraftServer.getExtensionManager(); Extension ext = extensionManager.getExtension(name); @@ -68,12 +69,12 @@ public class ReloadExtensionCommand extends Command { } } } else { - sender.sendMessage("Extension '" + name + "' does not exist."); + sender.sendMessage(Component.text("Extension '" + name + "' does not exist.")); } } private void gameModeCallback(CommandSender sender, ArgumentSyntaxException argumentSyntaxException) { - sender.sendMessage("'" + argumentSyntaxException.getInput() + "' is not a valid extension name!"); + sender.sendMessage(Component.text("'" + argumentSyntaxException.getInput() + "' is not a valid extension name!")); } @Nullable diff --git a/src/test/java/demo/commands/ShootCommand.java b/src/test/java/demo/commands/ShootCommand.java index eef52c2f0..8477de211 100644 --- a/src/test/java/demo/commands/ShootCommand.java +++ b/src/test/java/demo/commands/ShootCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.CommandContext; @@ -25,18 +26,18 @@ public class ShootCommand extends Command { private boolean condition(CommandSender sender, String commandString) { if (!sender.isPlayer()) { - sender.sendMessage("The command is only available for player"); + sender.sendMessage(Component.text("The command is only available for player")); return false; } return true; } private void defaultExecutor(CommandSender sender, CommandContext context) { - sender.sendMessage("Correct usage: shoot [default/spectral/colored]"); + sender.sendMessage(Component.text("Correct usage: shoot [default/spectral/colored]")); } private void onTypeError(CommandSender sender, ArgumentSyntaxException exception) { - sender.sendMessage("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by 'default', 'spectral' or 'colored'"); + sender.sendMessage(Component.text("SYNTAX ERROR: '" + exception.getInput() + "' should be replaced by 'default', 'spectral' or 'colored'")); } private void onShootCommand(CommandSender sender, CommandContext context) { diff --git a/src/test/java/demo/commands/TeleportCommand.java b/src/test/java/demo/commands/TeleportCommand.java index 1be4d5780..27888b9ff 100644 --- a/src/test/java/demo/commands/TeleportCommand.java +++ b/src/test/java/demo/commands/TeleportCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; @@ -14,7 +15,7 @@ public class TeleportCommand extends Command { public TeleportCommand() { super("tp"); - setDefaultExecutor((source, context) -> source.sendMessage("Usage: /tp x y z")); + setDefaultExecutor((source, context) -> source.sendMessage(Component.text("Usage: /tp x y z"))); var posArg = ArgumentType.RelativeVec3("pos"); var playerArg = ArgumentType.Word("player"); @@ -30,7 +31,7 @@ public class TeleportCommand extends Command { Player player = (Player) sender; player.teleport(pl.getPosition()); } - sender.sendMessage("Teleported to player " + playerName); + sender.sendMessage(Component.text("Teleported to player "+playerName)); } private void onPositionTeleport(CommandSender sender, CommandContext context) { @@ -40,7 +41,7 @@ public class TeleportCommand extends Command { final Position position = relativeVec.from(player).toPosition(); player.teleport(position); - player.sendMessage("You have been teleported to " + position); + player.sendMessage(Component.text("You have been teleported to " + position)); } } diff --git a/src/test/java/demo/commands/TestCommand.java b/src/test/java/demo/commands/TestCommand.java index 88332aee3..c339f12d2 100644 --- a/src/test/java/demo/commands/TestCommand.java +++ b/src/test/java/demo/commands/TestCommand.java @@ -1,5 +1,7 @@ package demo.commands; +import net.minestom.server.chat.JsonMessage; +import net.kyori.adventure.text.Component; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.CommandContext; @@ -22,7 +24,7 @@ public class TestCommand extends Command { } private void usage(CommandSender sender, CommandContext context) { - sender.sendMessage("Incorrect usage"); + sender.sendMessage(Component.text("Incorrect usage")); } } diff --git a/src/test/java/demo/commands/TitleCommand.java b/src/test/java/demo/commands/TitleCommand.java index 82b80b4a3..971009c76 100644 --- a/src/test/java/demo/commands/TitleCommand.java +++ b/src/test/java/demo/commands/TitleCommand.java @@ -3,6 +3,8 @@ package demo.commands; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; import net.minestom.server.command.CommandSender; @@ -15,7 +17,7 @@ public class TitleCommand extends Command { public TitleCommand() { super("title"); setDefaultExecutor((source, args) -> { - source.sendMessage("Unknown syntax (note: title must be quoted)"); + source.sendMessage(Component.text("Unknown syntax (note: title must be quoted)")); }); var content = ArgumentType.String("content"); @@ -25,20 +27,13 @@ public class TitleCommand extends Command { private void handleTitle(CommandSender source, CommandContext context) { if (!source.isPlayer()) { - source.sendMessage("Only players can run this command!"); + source.sendMessage(Component.text("Only players can run this command!")); return; } Player player = source.asPlayer(); String titleContent = context.get("content"); - player.sendTitleTime(10, 100, 10); - try { - JsonElement parsed = JsonParser.parseString(titleContent); - JsonMessage message = new JsonMessage.RawJsonMessage(parsed.getAsJsonObject()); - player.sendTitleMessage(message); - } catch (JsonParseException | IllegalStateException ignored) { - player.sendTitleMessage(ColoredText.of(titleContent)); - } + player.showTitle(Title.title(Component.text(titleContent), Component.empty(), Title.DEFAULT_TIMES)); } } diff --git a/src/test/java/demo/commands/UnloadExtensionCommand.java b/src/test/java/demo/commands/UnloadExtensionCommand.java index 0c350fb20..e5223716c 100644 --- a/src/test/java/demo/commands/UnloadExtensionCommand.java +++ b/src/test/java/demo/commands/UnloadExtensionCommand.java @@ -1,5 +1,6 @@ package demo.commands; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandSender; import net.minestom.server.command.builder.Command; @@ -30,12 +31,12 @@ public class UnloadExtensionCommand extends Command { } private void usage(CommandSender sender, CommandContext context) { - sender.sendMessage("Usage: /unload "); + sender.sendMessage(Component.text("Usage: /unload ")); } private void execute(CommandSender sender, CommandContext context) { String name = join(context.getStringArray("extensionName")); - sender.sendMessage("extensionName = " + name + "...."); + sender.sendMessage(Component.text("extensionName = " + name + "....")); ExtensionManager extensionManager = MinecraftServer.getExtensionManager(); Extension ext = extensionManager.getExtension(name); @@ -56,12 +57,12 @@ public class UnloadExtensionCommand extends Command { } } } else { - sender.sendMessage("Extension '" + name + "' does not exist."); + sender.sendMessage(Component.text("Extension '" + name + "' does not exist.")); } } private void extensionCallback(CommandSender sender, ArgumentSyntaxException exception) { - sender.sendMessage("'" + exception.getInput() + "' is not a valid extension name!"); + sender.sendMessage(Component.text("'" + exception.getInput() + "' is not a valid extension name!")); } private String join(String[] extensionNameParts) { From abae740cbc3b6874f98ab2c1b4a3ec4a9fdbc3c8 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 13:47:17 +0000 Subject: [PATCH 086/364] fix javadocs --- src/main/java/net/minestom/server/advancements/Advancement.java | 1 + .../minestom/server/advancements/notifications/Notification.java | 1 + src/main/java/net/minestom/server/entity/Player.java | 1 + 3 files changed, 3 insertions(+) diff --git a/src/main/java/net/minestom/server/advancements/Advancement.java b/src/main/java/net/minestom/server/advancements/Advancement.java index 9466d679f..9b10a074c 100644 --- a/src/main/java/net/minestom/server/advancements/Advancement.java +++ b/src/main/java/net/minestom/server/advancements/Advancement.java @@ -132,6 +132,7 @@ public class Advancement { * @deprecated Use {@link #getTitle()} */ @NotNull + @Deprecated public JsonMessage getTitleJson() { return JsonMessage.fromComponent(title); } diff --git a/src/main/java/net/minestom/server/advancements/notifications/Notification.java b/src/main/java/net/minestom/server/advancements/notifications/Notification.java index 8c7f40b5b..456028ad0 100644 --- a/src/main/java/net/minestom/server/advancements/notifications/Notification.java +++ b/src/main/java/net/minestom/server/advancements/notifications/Notification.java @@ -50,6 +50,7 @@ public class Notification { * @deprecated Use {@link #getTitle()} */ @NotNull + @Deprecated public JsonMessage getTitleJson() { return JsonMessage.fromComponent(title); } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 9b47057f2..adebaab5e 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -1300,6 +1300,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { * @deprecated Use {@link #getDisplayName()} */ @Nullable + @Deprecated public JsonMessage getDisplayNameJson() { return JsonMessage.fromComponent(displayName); } From 8b82d61c0cc131df85262179750ab291c7380431 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 13:47:26 +0000 Subject: [PATCH 087/364] fix demo build --- src/test/java/demo/PlayerInit.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index 7be5363a3..3e277bd67 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -2,6 +2,7 @@ package demo; import demo.generator.ChunkGeneratorDemo; import demo.generator.NoiseTestGenerator; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.chat.ColoredText; @@ -78,13 +79,13 @@ public class PlayerInit { long ramUsage = benchmarkManager.getUsedMemory(); ramUsage /= 1e6; // bytes to MB - final ColoredText header = ColoredText.of("RAM USAGE: " + ramUsage + " MB"); - final ColoredText footer = ColoredText.of(benchmarkManager.getCpuMonitoringMessage()); + final Component header = Component.text("RAM USAGE: " + ramUsage + " MB"); + final Component footer = Component.text(benchmarkManager.getCpuMonitoringMessage()); { PlayerListHeaderAndFooterPacket playerListHeaderAndFooterPacket = new PlayerListHeaderAndFooterPacket(); - playerListHeaderAndFooterPacket.header = header; - playerListHeaderAndFooterPacket.footer = footer; + playerListHeaderAndFooterPacket.header = MinecraftServer.getSerializationManager().serialize(header); + playerListHeaderAndFooterPacket.footer = MinecraftServer.getSerializationManager().serialize(footer); PacketUtils.sendGroupedPacket(players, playerListHeaderAndFooterPacket); } From e3d135e1f2411ca1f961a8c7fb60f3dad193c401 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 14:03:53 +0000 Subject: [PATCH 088/364] update arguments to reflect new color options --- .../builder/arguments/ArgumentType.java | 19 ++++++-- .../minecraft/ArgumentChatColor.java | 43 +++++++++++++++++++ .../arguments/minecraft/ArgumentColor.java | 31 +++++++------ .../arguments/minecraft/ArgumentDyeColor.java | 32 ++++++++++++++ .../minecraft/ArgumentTeamFormat.java | 40 +++++++++++++++++ .../minecraft/ArgumentTextColor.java | 4 +- 6 files changed, 148 insertions(+), 21 deletions(-) create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentDyeColor.java create mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamFormat.java diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java index 7322c5d38..4df154b4c 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java @@ -107,11 +107,12 @@ public class ArgumentType { // Minecraft specific arguments /** - * @deprecated Use {@link #TextColor} for colors and {@link #TextDecoration} for styles. + * @deprecated Use {@link ArgumentTextColor} for colors, {@link ArgumentTextDecoration} for styles, {@link ArgumentColor} for raw colors, + * {@link ArgumentDyeColor} for dye colors and {@link ArgumentTeamFormat} for team formats */ @Deprecated - public static ArgumentColor Color(@NotNull String id) { - return new ArgumentColor(id); + public static ArgumentChatColor ChatColor(@NotNull String id) { + return new ArgumentChatColor(id); } public static ArgumentTextColor TextColor(@NotNull String id) { @@ -122,6 +123,18 @@ public class ArgumentType { return new ArgumentTextDecoration(id); } + public static ArgumentColor Color(@NotNull String id) { + return new ArgumentColor(id); + } + + public static ArgumentDyeColor DyeColor(@NotNull String id) { + return new ArgumentDyeColor(id); + } + + public static ArgumentTeamFormat TeamFormat(@NotNull String id) { + return new ArgumentTeamFormat(id); + } + /** * @see ArgumentTime */ diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java new file mode 100644 index 000000000..6dea09d7e --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java @@ -0,0 +1,43 @@ +package net.minestom.server.command.builder.arguments.minecraft; + +import net.minestom.server.chat.ChatColor; +import net.minestom.server.command.builder.NodeMaker; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; +import org.jetbrains.annotations.NotNull; + +/** + * Represents an argument which will give you a {@link ChatColor}. + *

+ * Example: red, white, reset + * @deprecated Use {@link ArgumentTextColor} for colors, {@link ArgumentTextDecoration} for styles, {@link ArgumentColor} for raw colors, + * {@link ArgumentDyeColor} for dye colors and {@link ArgumentTeamFormat} for team formats + */ +@Deprecated +public class ArgumentChatColor extends Argument { + + public static final int UNDEFINED_COLOR = -2; + + public ArgumentChatColor(String id) { + super(id); + } + + @NotNull + @Override + public ChatColor parse(@NotNull String input) throws ArgumentSyntaxException { + final ChatColor color = ChatColor.fromName(input); + if (color == ChatColor.NO_COLOR) + throw new ArgumentSyntaxException("Undefined color", input, UNDEFINED_COLOR); + + return color; + } + + @Override + public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); + argumentNode.parser = "minecraft:chat_color"; + + nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode}); + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java index da8a9c320..b8701d9e0 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java @@ -1,6 +1,7 @@ package net.minestom.server.command.builder.arguments.minecraft; -import net.minestom.server.chat.ChatColor; +import net.kyori.adventure.text.format.NamedTextColor; +import net.minestom.server.color.Color; import net.minestom.server.command.builder.NodeMaker; import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; @@ -8,28 +9,26 @@ import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; import org.jetbrains.annotations.NotNull; /** - * Represents an argument which will give you a {@link ChatColor}. - *

- * Example: red, white, reset - * @deprecated Use {@link ArgumentTextColor} for colors and {@link ArgumentTextDecoration} for styles. + * Represents an argument that will give you a {@link Color}. Input is parsed + * first as a hex string ({@code #int}), then as a CSS hex string ({@code #rrggbb} or + * {@code #rgb}), then as an integer and finally as a named text colour. The values for + * the named text colours can be found in {@link NamedTextColor}. + *

+ * This class is essentially a wrapper around {@link ArgumentTextColor}. */ -@Deprecated -public class ArgumentColor extends Argument { +public class ArgumentColor extends Argument { + private final ArgumentTextColor argumentTextColor; - public static final int UNDEFINED_COLOR = -2; + public static int UNDEFINED_COLOR = ArgumentTextColor.UNDEFINED_COLOR; - public ArgumentColor(String id) { + public ArgumentColor(@NotNull String id) { super(id); + argumentTextColor = new ArgumentTextColor(id); } - @NotNull @Override - public ChatColor parse(@NotNull String input) throws ArgumentSyntaxException { - final ChatColor color = ChatColor.fromName(input); - if (color == ChatColor.NO_COLOR) - throw new ArgumentSyntaxException("Undefined color", input, UNDEFINED_COLOR); - - return color; + public @NotNull Color parse(@NotNull String input) throws ArgumentSyntaxException { + return new Color(this.argumentTextColor.parse(input)); } @Override diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentDyeColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentDyeColor.java new file mode 100644 index 000000000..f703194f4 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentDyeColor.java @@ -0,0 +1,32 @@ +package net.minestom.server.command.builder.arguments.minecraft; + +import net.minestom.server.color.DyeColor; +import net.minestom.server.command.builder.NodeMaker; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import org.jetbrains.annotations.NotNull; + +/** + * An argument that returns a {@link DyeColor} from the name of the dye color. + */ +public class ArgumentDyeColor extends Argument { + public static int UNDEFINED_DYE_COLOR = -2; + + public ArgumentDyeColor(@NotNull String id) { + super(id); + } + + @Override + public @NotNull DyeColor parse(@NotNull String input) throws ArgumentSyntaxException { + try { + return DyeColor.valueOf(input.toUpperCase().replace(' ', '_').trim()); + } catch (IllegalArgumentException ignored) { + throw new ArgumentSyntaxException("Undefined dye color", input, UNDEFINED_DYE_COLOR); + } + } + + @Override + public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { + + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamFormat.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamFormat.java new file mode 100644 index 000000000..dbadeafc8 --- /dev/null +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamFormat.java @@ -0,0 +1,40 @@ +package net.minestom.server.command.builder.arguments.minecraft; + +import net.minestom.server.color.TeamFormat; +import net.minestom.server.command.builder.NodeMaker; +import net.minestom.server.command.builder.arguments.Argument; +import net.minestom.server.command.builder.exception.ArgumentSyntaxException; +import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; +import org.jetbrains.annotations.NotNull; + +/** + * An argument that will give you a {@link TeamFormat} from it's name or the int code. + */ +public class ArgumentTeamFormat extends Argument { + public static final int UNDEFINED_TEAM_FORMAT = -2; + + public ArgumentTeamFormat(@NotNull String id) { + super(id); + } + + @Override + public @NotNull TeamFormat parse(@NotNull String input) throws ArgumentSyntaxException { + try { + return TeamFormat.valueOf(input.toUpperCase().trim().replace(' ', '_')); + } catch (IllegalArgumentException ignored) { + try { + return TeamFormat.values()[Integer.parseInt(input)]; + } catch (NumberFormatException | ArrayIndexOutOfBoundsException alsoIgnored) { + throw new ArgumentSyntaxException("Undefined team format!", input, UNDEFINED_TEAM_FORMAT); + } + } + } + + @Override + public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { + DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); + argumentNode.parser = "minecraft:team_format"; + + nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode}); + } +} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java index 6c4328084..6d8cfb272 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java @@ -25,7 +25,7 @@ public class ArgumentTextColor extends Argument { @Override public @NotNull TextColor parse(@NotNull String input) throws ArgumentSyntaxException { - TextColor textColor = null; + TextColor textColor; // first try standard hex textColor = TextColor.fromHexString(input); @@ -58,7 +58,7 @@ public class ArgumentTextColor extends Argument { @Override public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); - argumentNode.parser = "minecraft:text_decoration"; + argumentNode.parser = "minecraft:text_color"; nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode}); } From aa7703c05660c5bd1988352230bd5a7aadf2c433 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 17:26:10 +0000 Subject: [PATCH 089/364] make color take an RGBLike instead of a TextColor --- src/main/java/net/minestom/server/color/Color.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/color/Color.java b/src/main/java/net/minestom/server/color/Color.java index ad55a483e..56a942ae5 100644 --- a/src/main/java/net/minestom/server/color/Color.java +++ b/src/main/java/net/minestom/server/color/Color.java @@ -28,12 +28,12 @@ public class Color implements RGBLike { } /** - * Creates a color from an Adventure text color. + * Creates a color from an RGB-like color. * - * @param textColor the text color + * @param rgbLike the color */ - public Color(TextColor textColor) { - this(textColor.red(), textColor.blue(), textColor.green()); + public Color(RGBLike rgbLike) { + this(rgbLike.red(), rgbLike.blue(), rgbLike.green()); } /** From d419bf94017f173070922534fb536789e7d3be1b Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 4 Mar 2021 18:29:28 +0000 Subject: [PATCH 090/364] fix TeamColor allowing unsupported formats --- .../net/minestom/server/chat/ChatColor.java | 16 ++- .../net/minestom/server/color/TeamColor.java | 77 ++++++++++ .../net/minestom/server/color/TeamFormat.java | 133 ------------------ .../builder/arguments/ArgumentType.java | 6 +- .../minecraft/ArgumentChatColor.java | 2 +- ...TeamFormat.java => ArgumentTeamColor.java} | 14 +- .../packet/server/play/TeamsPacket.java | 10 +- .../minestom/server/scoreboard/Sidebar.java | 8 +- .../net/minestom/server/scoreboard/Team.java | 57 ++++---- .../server/scoreboard/TeamBuilder.java | 22 +-- .../server/scoreboard/TeamManager.java | 22 +-- 11 files changed, 161 insertions(+), 206 deletions(-) create mode 100644 src/main/java/net/minestom/server/color/TeamColor.java delete mode 100644 src/main/java/net/minestom/server/color/TeamFormat.java rename src/main/java/net/minestom/server/command/builder/arguments/minecraft/{ArgumentTeamFormat.java => ArgumentTeamColor.java} (69%) diff --git a/src/main/java/net/minestom/server/chat/ChatColor.java b/src/main/java/net/minestom/server/chat/ChatColor.java index 0de726436..4d2fa0dfa 100644 --- a/src/main/java/net/minestom/server/chat/ChatColor.java +++ b/src/main/java/net/minestom/server/chat/ChatColor.java @@ -6,9 +6,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.kyori.adventure.text.format.TextColor; import net.minestom.server.color.Color; -import net.minestom.server.color.TeamFormat; +import net.minestom.server.color.TeamColor; import net.minestom.server.utils.validate.Check; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -289,8 +288,17 @@ public final class ChatColor { return new Color(red, green, blue); } - public TeamFormat asTeamFormat() { - return TeamFormat.values()[this.getId()]; + /** + * Gets the TeamColor of this chat color. + * + * @return the team color, or null if there is no team color for this chat color + */ + public @Nullable TeamColor asTeamColor() { + try { + return TeamColor.values()[this.getId()]; + } catch (ArrayIndexOutOfBoundsException ignored) { + return null; + } } @NotNull diff --git a/src/main/java/net/minestom/server/color/TeamColor.java b/src/main/java/net/minestom/server/color/TeamColor.java new file mode 100644 index 000000000..7f62ad4f2 --- /dev/null +++ b/src/main/java/net/minestom/server/color/TeamColor.java @@ -0,0 +1,77 @@ +package net.minestom.server.color; + +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.util.RGBLike; +import org.jetbrains.annotations.Nullable; + +/** + * The format used for teams. Note that this is often referred to as "team color". This + * is misleading as teams can also use text decoration like bold, italics, etc. + */ +public enum TeamColor implements RGBLike { + BLACK(NamedTextColor.BLACK), + DARK_BLUE(NamedTextColor.DARK_BLUE), + DARK_GREEN(NamedTextColor.DARK_GREEN), + DARK_AQUA(NamedTextColor.DARK_AQUA), + DARK_RED(NamedTextColor.DARK_RED), + DARK_PURPLE(NamedTextColor.DARK_PURPLE), + GOLD(NamedTextColor.GOLD), + GRAY(NamedTextColor.GRAY), + DARK_GRAY(NamedTextColor.DARK_GRAY), + BLUE(NamedTextColor.BLUE), + GREEN(NamedTextColor.GREEN), + AQUA(NamedTextColor.AQUA), + RED(NamedTextColor.RED), + LIGHT_PURPLE(NamedTextColor.LIGHT_PURPLE), + YELLOW(NamedTextColor.YELLOW), + WHITE(NamedTextColor.WHITE); + + private final TextColor color; + + TeamColor(TextColor color) { + this.color = color; + } + + /** + * Gets the text color equivalent to this team color, if any. + * + * @return the text color + */ + public @Nullable TextColor getTextColor() { + return this.color; + } + + /** + * Gets the color equivalent to this team color, if any. + * + * @return the color + */ + public @Nullable Color getColor() { + return this.color == null ? null : new Color(this.color); + } + + /** + * Gets the ID number of this team color. + * + * @return the id number + */ + public int getId() { + return this.ordinal(); + } + + @Override + public int red() { + return this.color.red(); + } + + @Override + public int green() { + return this.color.green(); + } + + @Override + public int blue() { + return this.color.blue(); + } +} diff --git a/src/main/java/net/minestom/server/color/TeamFormat.java b/src/main/java/net/minestom/server/color/TeamFormat.java deleted file mode 100644 index c8991c494..000000000 --- a/src/main/java/net/minestom/server/color/TeamFormat.java +++ /dev/null @@ -1,133 +0,0 @@ -package net.minestom.server.color; - -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextColor; -import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.util.RGBLike; -import org.jetbrains.annotations.Nullable; - -/** - * The format used for teams. Note that this is often referred to as "team color". This - * is misleading as teams can also use text decoration like bold, italics, etc. - */ -public enum TeamFormat implements RGBLike { - BLACK(NamedTextColor.BLACK), - DARK_BLUE(NamedTextColor.DARK_BLUE), - DARK_GREEN(NamedTextColor.DARK_GREEN), - DARK_AQUA(NamedTextColor.DARK_AQUA), - DARK_RED(NamedTextColor.DARK_RED), - DARK_PURPLE(NamedTextColor.DARK_PURPLE), - GOLD(NamedTextColor.GOLD), - GRAY(NamedTextColor.GRAY), - DARK_GRAY(NamedTextColor.DARK_GRAY), - BLUE(NamedTextColor.BLUE), - GREEN(NamedTextColor.GREEN), - AQUA(NamedTextColor.AQUA), - RED(NamedTextColor.RED), - LIGHT_PURPLE(NamedTextColor.LIGHT_PURPLE), - YELLOW(NamedTextColor.YELLOW), - WHITE(NamedTextColor.WHITE), - OBFUSCATED(TextDecoration.OBFUSCATED), - BOLD(TextDecoration.BOLD), - STRIKETHROUGH(TextDecoration.STRIKETHROUGH), - UNDERLINE(TextDecoration.UNDERLINED), - ITALIC(TextDecoration.ITALIC), - RESET(null, null); - - private final TextDecoration decoration; - private final TextColor color; - - TeamFormat(TextDecoration decoration) { - this(decoration, null); - } - - TeamFormat(TextColor color) { - this(null, color); - } - - TeamFormat(TextDecoration decoration, TextColor color) { - this.decoration = decoration; - this.color = color; - } - - /** - * Checks if this team color is a color. - * - * @return if it is a color - */ - public boolean isColor() { - return this.color != null; - } - - /** - * Gets the text color equivalent to this team color, if any. - * - * @return the text color - */ - public @Nullable TextColor getTextColor() { - return this.color; - } - - /** - * Gets the color equivalent to this team color, if any. - * - * @return the color - */ - public @Nullable Color getColor() { - return this.color == null ? null : new Color(this.color); - } - - /** - * Checks if this team color is a text decoration. - * - * @return if it is a decoration - */ - public boolean isDecoration() { - return this.decoration != null; - } - - /** - * Gets the text decoration equivalent to this team color, if any. - * - * @return the decoration - */ - public @Nullable TextDecoration getDecoration() { - return this.decoration; - } - - /** - * @throws IllegalStateException if this team format is not a color - */ - @Override - public int red() { - if (!this.isColor()) { - throw new IllegalStateException("This TeamFormat does not represent a color"); - } - - return this.color.red(); - } - - /** - * @throws IllegalStateException if this team format is not a color - */ - @Override - public int green() { - if (!this.isColor()) { - throw new IllegalStateException("This TeamFormat does not represent a color"); - } - - return this.color.green(); - } - - /** - * @throws IllegalStateException if this team format is not a color - */ - @Override - public int blue() { - if (!this.isColor()) { - throw new IllegalStateException("This TeamFormat does not represent a color"); - } - - return this.color.blue(); - } -} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java index 4df154b4c..4dc0e1841 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java @@ -108,7 +108,7 @@ public class ArgumentType { /** * @deprecated Use {@link ArgumentTextColor} for colors, {@link ArgumentTextDecoration} for styles, {@link ArgumentColor} for raw colors, - * {@link ArgumentDyeColor} for dye colors and {@link ArgumentTeamFormat} for team formats + * {@link ArgumentDyeColor} for dye colors and {@link ArgumentTeamColor} for team formats */ @Deprecated public static ArgumentChatColor ChatColor(@NotNull String id) { @@ -131,8 +131,8 @@ public class ArgumentType { return new ArgumentDyeColor(id); } - public static ArgumentTeamFormat TeamFormat(@NotNull String id) { - return new ArgumentTeamFormat(id); + public static ArgumentTeamColor TeamColor(@NotNull String id) { + return new ArgumentTeamColor(id); } /** diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java index 6dea09d7e..f34a36205 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; *

* Example: red, white, reset * @deprecated Use {@link ArgumentTextColor} for colors, {@link ArgumentTextDecoration} for styles, {@link ArgumentColor} for raw colors, - * {@link ArgumentDyeColor} for dye colors and {@link ArgumentTeamFormat} for team formats + * {@link ArgumentDyeColor} for dye colors and {@link ArgumentTeamColor} for team formats */ @Deprecated public class ArgumentChatColor extends Argument { diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamFormat.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamColor.java similarity index 69% rename from src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamFormat.java rename to src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamColor.java index dbadeafc8..dc04a24e7 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamFormat.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamColor.java @@ -1,6 +1,6 @@ package net.minestom.server.command.builder.arguments.minecraft; -import net.minestom.server.color.TeamFormat; +import net.minestom.server.color.TeamColor; import net.minestom.server.command.builder.NodeMaker; import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; @@ -8,22 +8,22 @@ import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; import org.jetbrains.annotations.NotNull; /** - * An argument that will give you a {@link TeamFormat} from it's name or the int code. + * An argument that will give you a {@link TeamColor} from it's name or the int code. */ -public class ArgumentTeamFormat extends Argument { +public class ArgumentTeamColor extends Argument { public static final int UNDEFINED_TEAM_FORMAT = -2; - public ArgumentTeamFormat(@NotNull String id) { + public ArgumentTeamColor(@NotNull String id) { super(id); } @Override - public @NotNull TeamFormat parse(@NotNull String input) throws ArgumentSyntaxException { + public @NotNull TeamColor parse(@NotNull String input) throws ArgumentSyntaxException { try { - return TeamFormat.valueOf(input.toUpperCase().trim().replace(' ', '_')); + return TeamColor.valueOf(input.toUpperCase().trim().replace(' ', '_')); } catch (IllegalArgumentException ignored) { try { - return TeamFormat.values()[Integer.parseInt(input)]; + return TeamColor.values()[Integer.parseInt(input)]; } catch (NumberFormatException | ArrayIndexOutOfBoundsException alsoIgnored) { throw new ArgumentSyntaxException("Undefined team format!", input, UNDEFINED_TEAM_FORMAT); } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java index 4fd7a971f..6e861c8d1 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java @@ -1,7 +1,7 @@ package net.minestom.server.network.packet.server.play; import net.minestom.server.chat.JsonMessage; -import net.minestom.server.color.TeamFormat; +import net.minestom.server.color.TeamColor; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; @@ -40,7 +40,7 @@ public class TeamsPacket implements ServerPacket { /** * The color of the team */ - public TeamFormat teamFormat; + public TeamColor teamColor; /** * The prefix of the team */ @@ -56,9 +56,9 @@ public class TeamsPacket implements ServerPacket { /** - * @deprecated Use {@link #teamFormat} + * @deprecated Use {@link #teamColor} */ - @Deprecated public int teamColor = -1; + @Deprecated public int teamColorOld = -1; /** * @deprecated Use {@link #teamDisplayName} */ @@ -89,7 +89,7 @@ public class TeamsPacket implements ServerPacket { writer.writeByte(this.friendlyFlags); writer.writeSizedString(this.nameTagVisibility.getIdentifier()); writer.writeSizedString(this.collisionRule.getIdentifier()); - writer.writeVarInt(this.teamColor != -1 ? this.teamColor : this.teamFormat.ordinal()); + writer.writeVarInt(this.teamColorOld != -1 ? this.teamColorOld : this.teamColor.getId()); writer.writeSizedString(this.teamPrefixJson != null ? this.teamPrefixJson.toString() : this.teamPrefix); writer.writeSizedString(this.teamSuffixJson != null ? this.teamSuffixJson.toString() : this.teamSuffix); break; diff --git a/src/main/java/net/minestom/server/scoreboard/Sidebar.java b/src/main/java/net/minestom/server/scoreboard/Sidebar.java index c016a3389..7043b073d 100644 --- a/src/main/java/net/minestom/server/scoreboard/Sidebar.java +++ b/src/main/java/net/minestom/server/scoreboard/Sidebar.java @@ -4,7 +4,7 @@ import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.JsonMessage; -import net.minestom.server.color.TeamFormat; +import net.minestom.server.color.TeamColor; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.DisplayScoreboardPacket; import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket; @@ -440,7 +440,7 @@ public class Sidebar implements Scoreboard { private final byte friendlyFlags = 0x00; private final TeamsPacket.NameTagVisibility nameTagVisibility = TeamsPacket.NameTagVisibility.NEVER; private final TeamsPacket.CollisionRule collisionRule = TeamsPacket.CollisionRule.NEVER; - private final TeamFormat teamformat = TeamFormat.DARK_GREEN; + private final TeamColor teamColor = TeamColor.DARK_GREEN; /** @@ -471,7 +471,7 @@ public class Sidebar implements Scoreboard { teamsPacket.friendlyFlags = friendlyFlags; teamsPacket.nameTagVisibility = nameTagVisibility; teamsPacket.collisionRule = collisionRule; - teamsPacket.teamFormat = teamformat; + teamsPacket.teamColor = teamColor; teamsPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(prefix); teamsPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(suffix); teamsPacket.entities = new String[]{entityName}; @@ -504,7 +504,7 @@ public class Sidebar implements Scoreboard { teamsPacket.friendlyFlags = friendlyFlags; teamsPacket.nameTagVisibility = nameTagVisibility; teamsPacket.collisionRule = collisionRule; - teamsPacket.teamFormat = teamformat; + teamsPacket.teamColor = teamColor; teamsPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(prefix); teamsPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(suffix); return teamsPacket; diff --git a/src/main/java/net/minestom/server/scoreboard/Team.java b/src/main/java/net/minestom/server/scoreboard/Team.java index 14beb15ee..18c412e2d 100644 --- a/src/main/java/net/minestom/server/scoreboard/Team.java +++ b/src/main/java/net/minestom/server/scoreboard/Team.java @@ -6,17 +6,16 @@ import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; -import net.minestom.server.color.TeamFormat; +import net.minestom.server.color.TeamColor; import net.minestom.server.entity.LivingEntity; import net.minestom.server.entity.Player; -import net.minestom.server.instance.ChunkPopulator; import net.minestom.server.network.ConnectionManager; import net.minestom.server.network.packet.server.play.TeamsPacket; import net.minestom.server.network.packet.server.play.TeamsPacket.CollisionRule; import net.minestom.server.network.packet.server.play.TeamsPacket.NameTagVisibility; import net.minestom.server.utils.PacketUtils; +import org.apache.commons.lang3.Validate; import org.jetbrains.annotations.NotNull; import java.util.Collections; @@ -57,10 +56,10 @@ public class Team implements ForwardingAudience { private CollisionRule collisionRule; /** - * Used to format the name of players on the team
- * The format of a team defines how the names of the team members are visualized. + * Used to color the name of players on the team
+ * The color of a team defines how the names of the team members are visualized. */ - private TeamFormat teamFormat; + private TeamColor teamColor; /** * Shown before the names of the players who belong to this team. @@ -87,7 +86,7 @@ public class Team implements ForwardingAudience { this.nameTagVisibility = NameTagVisibility.ALWAYS; this.collisionRule = CollisionRule.ALWAYS; - this.teamFormat = TeamFormat.WHITE; + this.teamColor = TeamColor.WHITE; this.prefix = Component.empty(); this.suffix = Component.empty(); @@ -236,11 +235,13 @@ public class Team implements ForwardingAudience { * * @param color The new team color * @see #updateTeamColor(ChatColor) - * @deprecated Use {@link #setTeamFormat(TeamFormat)} + * @deprecated Use {@link #setTeamColor(TeamColor)} */ @Deprecated public void setTeamColor(@NotNull ChatColor color) { - this.setTeamFormat(color.asTeamFormat()); + TeamColor teamColor = color.asTeamColor(); + Validate.notNull(teamColor, "Cannot set team color to non-color."); + this.setTeamColor(teamColor); } /** @@ -248,31 +249,33 @@ public class Team implements ForwardingAudience { *

* Warning: This is only changed on the server side. * - * @param format The new team color - * @see #updateTeamFormat(TeamFormat) + * @param color The new team color + * @see #setTeamColor(TeamColor) */ - public void setTeamFormat(@NotNull TeamFormat format) { - this.teamFormat = format; + public void setTeamColor(@NotNull TeamColor color) { + this.teamColor = color; } /** * Changes the color of the team and sends an update packet. * - * @param teamColor The new team color - * @deprecated Use {@link #updateTeamFormat(TeamFormat)} + * @param chatColor The new team color + * @deprecated Use {@link #updateTeamColor(TeamColor)} */ @Deprecated - public void updateTeamColor(@NotNull ChatColor teamColor) { - this.updateTeamFormat(teamColor.asTeamFormat()); + public void updateTeamColor(@NotNull ChatColor chatColor) { + TeamColor teamColor = chatColor.asTeamColor(); + Validate.notNull(teamColor, "Cannot set team color to non-color."); + this.updateTeamColor(teamColor); } /** * Changes the color of the team and sends an update packet. * - * @param format The new team color + * @param color The new team color */ - public void updateTeamFormat(@NotNull TeamFormat format) { - this.setTeamFormat(format); + public void updateTeamColor(@NotNull TeamColor color) { + this.setTeamColor(color); sendUpdatePacket(); } @@ -410,7 +413,7 @@ public class Team implements ForwardingAudience { teamsCreationPacket.friendlyFlags = this.friendlyFlags; teamsCreationPacket.nameTagVisibility = this.nameTagVisibility; teamsCreationPacket.collisionRule = this.collisionRule; - teamsCreationPacket.teamFormat = this.teamFormat; + teamsCreationPacket.teamColor = this.teamColor; teamsCreationPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(this.prefix); teamsCreationPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(this.suffix); teamsCreationPacket.entities = this.members.toArray(new String[0]); @@ -494,12 +497,12 @@ public class Team implements ForwardingAudience { * Gets the color of the team. * * @return the team color - * @deprecated Use {@link #getTeamFormat()} + * @deprecated Use {@link #getTeamColor()} */ @Deprecated @NotNull - public ChatColor getTeamColor() { - return ChatColor.fromName(teamFormat.name()); + public ChatColor getTeamColorOld() { + return ChatColor.fromName(teamColor.name()); } /** @@ -508,8 +511,8 @@ public class Team implements ForwardingAudience { * @return the team color */ @NotNull - public TeamFormat getTeamFormat() { - return teamFormat; + public TeamColor getTeamColor() { + return teamColor; } /** @@ -563,7 +566,7 @@ public class Team implements ForwardingAudience { updatePacket.friendlyFlags = this.friendlyFlags; updatePacket.nameTagVisibility = this.nameTagVisibility; updatePacket.collisionRule = this.collisionRule; - updatePacket.teamFormat = this.teamFormat; + updatePacket.teamColor = this.teamColor; updatePacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(this.prefix); updatePacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(this.suffix); diff --git a/src/main/java/net/minestom/server/scoreboard/TeamBuilder.java b/src/main/java/net/minestom/server/scoreboard/TeamBuilder.java index 5ff35100a..d59bf824a 100644 --- a/src/main/java/net/minestom/server/scoreboard/TeamBuilder.java +++ b/src/main/java/net/minestom/server/scoreboard/TeamBuilder.java @@ -3,7 +3,7 @@ package net.minestom.server.scoreboard; import net.kyori.adventure.text.Component; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.JsonMessage; -import net.minestom.server.color.TeamFormat; +import net.minestom.server.color.TeamColor; import net.minestom.server.network.packet.server.play.TeamsPacket.CollisionRule; import net.minestom.server.network.packet.server.play.TeamsPacket.NameTagVisibility; @@ -87,11 +87,11 @@ public class TeamBuilder { * * @param color The new color * @return this builder, for chaining - * @deprecated Use {@link #updateTeamFormat(TeamFormat)} + * @deprecated Use {@link #updateTeamColor(TeamColor)} */ @Deprecated public TeamBuilder updateTeamColor(ChatColor color) { - return this.updateTeamFormat(color.asTeamFormat()); + return this.updateTeamColor(color.asTeamColor()); } /** @@ -100,8 +100,8 @@ public class TeamBuilder { * @param color The new color * @return this builder, for chaining */ - public TeamBuilder updateTeamFormat(TeamFormat color) { - this.team.updateTeamFormat(color); + public TeamBuilder updateTeamColor(TeamColor color) { + this.team.updateTeamColor(color); return this; } @@ -315,23 +315,23 @@ public class TeamBuilder { * * @param color The new team color * @return this builder, for chaining - * @deprecated Use {@link #teamFormat(TeamFormat)} + * @deprecated Use {@link #teamColor(TeamColor)} */ @Deprecated public TeamBuilder teamColor(ChatColor color) { - return this.teamFormat(color.asTeamFormat()); + return this.teamColor(color.asTeamColor()); } /** - * Changes the format of the {@link Team} without an update packet. + * Changes the color of the {@link Team} without an update packet. *

* Warning: If you do not call {@link #updateTeamPacket()}, this is only changed of the server side. * - * @param format The new team format + * @param color The new team color * @return this builder, for chaining */ - public TeamBuilder teamFormat(TeamFormat format) { - this.team.setTeamFormat(format); + public TeamBuilder teamColor(TeamColor color) { + this.team.setTeamColor(color); return this; } diff --git a/src/main/java/net/minestom/server/scoreboard/TeamManager.java b/src/main/java/net/minestom/server/scoreboard/TeamManager.java index c76d61423..50fe34937 100644 --- a/src/main/java/net/minestom/server/scoreboard/TeamManager.java +++ b/src/main/java/net/minestom/server/scoreboard/TeamManager.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.ChatColor; import net.minestom.server.chat.JsonMessage; -import net.minestom.server.color.TeamFormat; +import net.minestom.server.color.TeamColor; import net.minestom.server.entity.LivingEntity; import net.minestom.server.entity.Player; import net.minestom.server.network.ConnectionManager; @@ -99,11 +99,11 @@ public final class TeamManager { * @param teamColor The team color * @param suffix The team suffix * @return the created {@link Team} with a prefix, teamColor and suffix - * @deprecated Use {@link #createTeam(String, Component, TeamFormat, Component)} + * @deprecated Use {@link #createTeam(String, Component, TeamColor, Component)} */ @Deprecated public Team createTeam(String name, JsonMessage prefix, ChatColor teamColor, JsonMessage suffix) { - return this.createTeam(name, prefix.asComponent(), teamColor.asTeamFormat(), suffix.asComponent()); + return this.createTeam(name, prefix.asComponent(), teamColor.asTeamColor(), suffix.asComponent()); } /** @@ -111,12 +111,12 @@ public final class TeamManager { * * @param name The registry name * @param prefix The team prefix - * @param teamFormat The team format + * @param teamColor The team format * @param suffix The team suffix * @return the created {@link Team} with a prefix, teamColor and suffix */ - public Team createTeam(String name, Component prefix, TeamFormat teamFormat, Component suffix) { - return this.createBuilder(name).prefix(prefix).teamFormat(teamFormat).suffix(suffix).updateTeamPacket().build(); + public Team createTeam(String name, Component prefix, TeamColor teamColor, Component suffix) { + return this.createBuilder(name).prefix(prefix).teamColor(teamColor).suffix(suffix).updateTeamPacket().build(); } /** @@ -128,11 +128,11 @@ public final class TeamManager { * @param teamColor The team color * @param suffix The team suffix * @return the created {@link Team} with a prefix, teamColor, suffix and the display name - * @deprecated Use {@link #createTeam(String, Component, Component, TeamFormat, Component)} + * @deprecated Use {@link #createTeam(String, Component, Component, TeamColor, Component)} */ @Deprecated public Team createTeam(String name, JsonMessage displayName, JsonMessage prefix, ChatColor teamColor, JsonMessage suffix) { - return this.createTeam(name, displayName.asComponent(), prefix.asComponent(), teamColor.asTeamFormat(), suffix.asComponent()); + return this.createTeam(name, displayName.asComponent(), prefix.asComponent(), teamColor.asTeamColor(), suffix.asComponent()); } /** @@ -141,12 +141,12 @@ public final class TeamManager { * @param name The registry name * @param displayName The display name * @param prefix The team prefix - * @param teamFormat The team color + * @param teamColor The team color * @param suffix The team suffix * @return the created {@link Team} with a prefix, teamColor, suffix and the display name */ - public Team createTeam(String name, Component displayName, Component prefix, TeamFormat teamFormat, Component suffix) { - return this.createBuilder(name).teamDisplayName(displayName).prefix(prefix).teamFormat(teamFormat).suffix(suffix).updateTeamPacket().build(); + public Team createTeam(String name, Component displayName, Component prefix, TeamColor teamColor, Component suffix) { + return this.createBuilder(name).teamDisplayName(displayName).prefix(prefix).teamColor(teamColor).suffix(suffix).updateTeamPacket().build(); } /** From b6dd2fa8f139a7c50dd9dc80be096a40a8b19d65 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 16:03:01 +0000 Subject: [PATCH 091/364] implement Keyed in generated enums --- .../minestom/server/entity/EntityType.java | 11 ++++++- .../net/minestom/server/fluids/Fluid.java | 13 ++++++-- .../minestom/server/instance/block/Block.java | 27 +++++++++++------ .../net/minestom/server/item/Enchantment.java | 13 ++++++-- .../net/minestom/server/item/Material.java | 17 ++++++++--- .../minestom/server/particle/Particle.java | 13 ++++++-- .../minestom/server/potion/PotionEffect.java | 13 ++++++-- .../minestom/server/potion/PotionType.java | 13 ++++++-- .../java/net/minestom/server/sound/Sound.java | 13 ++++++-- .../minestom/server/stat/StatisticType.java | 13 ++++++-- .../net/minestom/codegen/AllGenerators.java | 1 + .../minestom/codegen/BasicEnumGenerator.java | 8 +++++ .../net/minestom/codegen/EnumGenerator.java | 30 +++++++++++++++++-- .../codegen/blocks/BlockEnumGenerator.java | 8 +++++ .../entitytypes/EntityTypeEnumGenerator.java | 9 ++++++ .../codegen/items/ItemEnumGenerator.java | 8 +++++ 16 files changed, 179 insertions(+), 31 deletions(-) diff --git a/src/autogenerated/java/net/minestom/server/entity/EntityType.java b/src/autogenerated/java/net/minestom/server/entity/EntityType.java index dab56474a..c5cf9ccb8 100644 --- a/src/autogenerated/java/net/minestom/server/entity/EntityType.java +++ b/src/autogenerated/java/net/minestom/server/entity/EntityType.java @@ -1,6 +1,8 @@ package net.minestom.server.entity; import java.util.function.BiFunction; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.PlayerMeta; import net.minestom.server.entity.metadata.ambient.BatMeta; @@ -120,7 +122,7 @@ import org.jetbrains.annotations.NotNull; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum EntityType { +public enum EntityType implements Keyed { AREA_EFFECT_CLOUD("minecraft:area_effect_cloud", 6.0, 0.5, AreaEffectCloudMeta::new, EntitySpawnType.BASE), ARMOR_STAND("minecraft:armor_stand", 0.5, 1.975, ArmorStandMeta::new, EntitySpawnType.LIVING), @@ -352,6 +354,8 @@ public enum EntityType { @NotNull private final EntitySpawnType spawnType; + private Key key; + EntityType(@NotNull String namespaceID, double width, double height, @NotNull BiFunction metaConstructor, @NotNull EntitySpawnType spawnType) { @@ -361,6 +365,7 @@ public enum EntityType { this.metaConstructor = metaConstructor; this.spawnType = spawnType; Registries.entityTypes.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public short getId() { @@ -393,4 +398,8 @@ public enum EntityType { } return null; } + + public Key key() { + return this.key; + } } diff --git a/src/autogenerated/java/net/minestom/server/fluids/Fluid.java b/src/autogenerated/java/net/minestom/server/fluids/Fluid.java index 2c3715c3c..73d3b565d 100644 --- a/src/autogenerated/java/net/minestom/server/fluids/Fluid.java +++ b/src/autogenerated/java/net/minestom/server/fluids/Fluid.java @@ -1,5 +1,7 @@ package net.minestom.server.fluids; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum Fluid { +public enum Fluid implements Keyed { EMPTY("minecraft:empty"), FLOWING_WATER("minecraft:flowing_water"), @@ -20,11 +22,14 @@ public enum Fluid { LAVA("minecraft:lava"); - private String namespaceID; + private final String namespaceID; + + private Key key; Fluid(String namespaceID) { this.namespaceID = namespaceID; Registries.fluids.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public int getId() { @@ -35,6 +40,10 @@ public enum Fluid { return namespaceID; } + public Key key() { + return this.key; + } + public static Fluid fromId(int id) { if (id >= 0 && id < values().length) { return values()[id]; diff --git a/src/autogenerated/java/net/minestom/server/instance/block/Block.java b/src/autogenerated/java/net/minestom/server/instance/block/Block.java index 4cca376fd..337d1d1ad 100644 --- a/src/autogenerated/java/net/minestom/server/instance/block/Block.java +++ b/src/autogenerated/java/net/minestom/server/instance/block/Block.java @@ -2,6 +2,8 @@ package net.minestom.server.instance.block; import java.util.Arrays; import java.util.List; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.instance.block.states.AcaciaButton; import net.minestom.server.instance.block.states.AcaciaDoor; import net.minestom.server.instance.block.states.AcaciaFence; @@ -481,7 +483,7 @@ import org.jetbrains.annotations.Nullable; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum Block { +public enum Block implements Keyed { AIR("minecraft:air", (short) 0, 0.0, 0.0, true, false, null, true), STONE("minecraft:stone", (short) 1, 1.5, 6.0, false, true, null, true), @@ -2480,25 +2482,27 @@ public enum Block { } @NotNull - private String namespaceID; + private final String namespaceID; - private short defaultID; + private final short defaultID; - private double hardness; + private final double hardness; - private double resistance; + private final double resistance; - private boolean isAir; + private final boolean isAir; - private boolean isSolid; + private final boolean isSolid; @Nullable - private NamespaceID blockEntity; + private final NamespaceID blockEntity; - private boolean singleState; + private final boolean singleState; private List alternatives = new java.util.ArrayList<>(); + private Key key; + Block(@NotNull String namespaceID, short defaultID, double hardness, double resistance, boolean isAir, boolean isSolid, @Nullable NamespaceID blockEntity, boolean singleState) { @@ -2514,6 +2518,7 @@ public enum Block { addBlockAlternative(new BlockAlternative(defaultID)); } Registries.blocks.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public short getBlockId() { @@ -2586,4 +2591,8 @@ public enum Block { public static Block fromStateId(short blockStateId) { return BlockArray.blocks[blockStateId]; } + + public Key key() { + return this.key; + } } diff --git a/src/autogenerated/java/net/minestom/server/item/Enchantment.java b/src/autogenerated/java/net/minestom/server/item/Enchantment.java index ff92bf36d..6b3524707 100644 --- a/src/autogenerated/java/net/minestom/server/item/Enchantment.java +++ b/src/autogenerated/java/net/minestom/server/item/Enchantment.java @@ -1,5 +1,7 @@ package net.minestom.server.item; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum Enchantment { +public enum Enchantment implements Keyed { PROTECTION("minecraft:protection"), FIRE_PROTECTION("minecraft:fire_protection"), @@ -86,11 +88,14 @@ public enum Enchantment { VANISHING_CURSE("minecraft:vanishing_curse"); - private String namespaceID; + private final String namespaceID; + + private Key key; Enchantment(String namespaceID) { this.namespaceID = namespaceID; Registries.enchantments.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public int getId() { @@ -101,6 +106,10 @@ public enum Enchantment { return namespaceID; } + public Key key() { + return this.key; + } + public static Enchantment fromId(int id) { if (id >= 0 && id < values().length) { return values()[id]; diff --git a/src/autogenerated/java/net/minestom/server/item/Material.java b/src/autogenerated/java/net/minestom/server/item/Material.java index d9a625c00..b6fdfb9a2 100644 --- a/src/autogenerated/java/net/minestom/server/item/Material.java +++ b/src/autogenerated/java/net/minestom/server/item/Material.java @@ -1,5 +1,7 @@ package net.minestom.server.item; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.instance.block.Block; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -12,7 +14,7 @@ import org.jetbrains.annotations.Nullable; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum Material { +public enum Material implements Keyed { AIR("minecraft:air", 64, Block.AIR), STONE("minecraft:stone", 64, Block.STONE), @@ -1966,12 +1968,14 @@ public enum Material { RESPAWN_ANCHOR("minecraft:respawn_anchor", 64, Block.RESPAWN_ANCHOR); @NotNull - private String namespaceID; + private final String namespaceID; - private int maxDefaultStackSize; + private final int maxDefaultStackSize; @Nullable - private Block correspondingBlock; + private final Block correspondingBlock; + + private Key key; Material(@NotNull String namespaceID, int maxDefaultStackSize, @Nullable Block correspondingBlock) { @@ -1979,6 +1983,7 @@ public enum Material { this.maxDefaultStackSize = maxDefaultStackSize; this.correspondingBlock = correspondingBlock; Registries.materials.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public short getId() { @@ -2083,4 +2088,8 @@ public enum Material { } return isFood(); } + + public Key key() { + return this.key; + } } diff --git a/src/autogenerated/java/net/minestom/server/particle/Particle.java b/src/autogenerated/java/net/minestom/server/particle/Particle.java index 4e5e15805..f62c5b600 100644 --- a/src/autogenerated/java/net/minestom/server/particle/Particle.java +++ b/src/autogenerated/java/net/minestom/server/particle/Particle.java @@ -1,5 +1,7 @@ package net.minestom.server.particle; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum Particle { +public enum Particle implements Keyed { AMBIENT_ENTITY_EFFECT("minecraft:ambient_entity_effect"), ANGRY_VILLAGER("minecraft:angry_villager"), @@ -154,11 +156,14 @@ public enum Particle { WHITE_ASH("minecraft:white_ash"); - private String namespaceID; + private final String namespaceID; + + private Key key; Particle(String namespaceID) { this.namespaceID = namespaceID; Registries.particles.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public int getId() { @@ -169,6 +174,10 @@ public enum Particle { return namespaceID; } + public Key key() { + return this.key; + } + public static Particle fromId(int id) { if (id >= 0 && id < values().length) { return values()[id]; diff --git a/src/autogenerated/java/net/minestom/server/potion/PotionEffect.java b/src/autogenerated/java/net/minestom/server/potion/PotionEffect.java index 60480a89a..28af57370 100644 --- a/src/autogenerated/java/net/minestom/server/potion/PotionEffect.java +++ b/src/autogenerated/java/net/minestom/server/potion/PotionEffect.java @@ -1,5 +1,7 @@ package net.minestom.server.potion; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum PotionEffect { +public enum PotionEffect implements Keyed { SPEED("minecraft:speed"), SLOWNESS("minecraft:slowness"), @@ -74,11 +76,14 @@ public enum PotionEffect { HERO_OF_THE_VILLAGE("minecraft:hero_of_the_village"); - private String namespaceID; + private final String namespaceID; + + private Key key; PotionEffect(String namespaceID) { this.namespaceID = namespaceID; Registries.potionEffects.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public int getId() { @@ -89,6 +94,10 @@ public enum PotionEffect { return namespaceID; } + public Key key() { + return this.key; + } + public static PotionEffect fromId(int id) { if (id >= 0 && id < values().length + 1) { return values()[id - 1]; diff --git a/src/autogenerated/java/net/minestom/server/potion/PotionType.java b/src/autogenerated/java/net/minestom/server/potion/PotionType.java index 984dbedcf..0c3a7a59f 100644 --- a/src/autogenerated/java/net/minestom/server/potion/PotionType.java +++ b/src/autogenerated/java/net/minestom/server/potion/PotionType.java @@ -1,5 +1,7 @@ package net.minestom.server.potion; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum PotionType { +public enum PotionType implements Keyed { EMPTY("minecraft:empty"), WATER("minecraft:water"), @@ -96,11 +98,14 @@ public enum PotionType { LONG_SLOW_FALLING("minecraft:long_slow_falling"); - private String namespaceID; + private final String namespaceID; + + private Key key; PotionType(String namespaceID) { this.namespaceID = namespaceID; Registries.potionTypes.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public int getId() { @@ -111,6 +116,10 @@ public enum PotionType { return namespaceID; } + public Key key() { + return this.key; + } + public static PotionType fromId(int id) { if (id >= 0 && id < values().length) { return values()[id]; diff --git a/src/autogenerated/java/net/minestom/server/sound/Sound.java b/src/autogenerated/java/net/minestom/server/sound/Sound.java index 2497d1452..af1f16e83 100644 --- a/src/autogenerated/java/net/minestom/server/sound/Sound.java +++ b/src/autogenerated/java/net/minestom/server/sound/Sound.java @@ -1,5 +1,7 @@ package net.minestom.server.sound; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum Sound { +public enum Sound implements Keyed { AMBIENT_CAVE("minecraft:ambient.cave"), AMBIENT_BASALT_DELTAS_ADDITIONS("minecraft:ambient.basalt_deltas.additions"), @@ -1994,11 +1996,14 @@ public enum Sound { ENTITY_ZOMBIE_VILLAGER_STEP("minecraft:entity.zombie_villager.step"); - private String namespaceID; + private final String namespaceID; + + private Key key; Sound(String namespaceID) { this.namespaceID = namespaceID; Registries.sounds.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public int getId() { @@ -2009,6 +2014,10 @@ public enum Sound { return namespaceID; } + public Key key() { + return this.key; + } + public static Sound fromId(int id) { if (id >= 0 && id < values().length) { return values()[id]; diff --git a/src/autogenerated/java/net/minestom/server/stat/StatisticType.java b/src/autogenerated/java/net/minestom/server/stat/StatisticType.java index 8b0f6dd0a..f7ebd8836 100644 --- a/src/autogenerated/java/net/minestom/server/stat/StatisticType.java +++ b/src/autogenerated/java/net/minestom/server/stat/StatisticType.java @@ -1,5 +1,7 @@ package net.minestom.server.stat; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -9,7 +11,7 @@ import net.minestom.server.utils.NamespaceID; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum StatisticType { +public enum StatisticType implements Keyed { LEAVE_GAME("minecraft:leave_game"), PLAY_ONE_MINUTE("minecraft:play_one_minute"), @@ -158,11 +160,14 @@ public enum StatisticType { INTERACT_WITH_SMITHING_TABLE("minecraft:interact_with_smithing_table"); - private String namespaceID; + private final String namespaceID; + + private Key key; StatisticType(String namespaceID) { this.namespaceID = namespaceID; Registries.statisticTypes.put(NamespaceID.from(namespaceID), this); + this.key = Key.key(this.namespaceID); } public int getId() { @@ -173,6 +178,10 @@ public enum StatisticType { return namespaceID; } + public Key key() { + return this.key; + } + public static StatisticType fromId(int id) { if (id >= 0 && id < values().length) { return values()[id]; diff --git a/src/generators/java/net/minestom/codegen/AllGenerators.java b/src/generators/java/net/minestom/codegen/AllGenerators.java index d63856e81..cc1ad65f1 100644 --- a/src/generators/java/net/minestom/codegen/AllGenerators.java +++ b/src/generators/java/net/minestom/codegen/AllGenerators.java @@ -16,6 +16,7 @@ import java.io.IOException; public class AllGenerators { public static void main(String[] args) throws IOException { + args = new String[]{"1.16.5", "src/autogenerated/java"}; BlockEnumGenerator.main(args); ItemEnumGenerator.main(args); // must be done after block PotionEnumGenerator.main(args); diff --git a/src/generators/java/net/minestom/codegen/BasicEnumGenerator.java b/src/generators/java/net/minestom/codegen/BasicEnumGenerator.java index 71f6b82fe..6cbb94273 100644 --- a/src/generators/java/net/minestom/codegen/BasicEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/BasicEnumGenerator.java @@ -3,6 +3,8 @@ package net.minestom.codegen; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.squareup.javapoet.*; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; @@ -116,6 +118,12 @@ public abstract class BasicEnumGenerator extends MinestomEnumGenerator { code.addStatement("$T." + CodeGenerator.decapitalize(getClassName()) + "s.put($T.from($N), this)", registriesClass, NamespaceID.class, "namespaceID"); }); + + // implement Keyed + generator.addSuperinterface(ClassName.get(Keyed.class)); + generator.addField(ClassName.get(Key.class), "key"); + generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)")); + generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key")); } @Override diff --git a/src/generators/java/net/minestom/codegen/EnumGenerator.java b/src/generators/java/net/minestom/codegen/EnumGenerator.java index 4400596f3..6fd1479cb 100644 --- a/src/generators/java/net/minestom/codegen/EnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/EnumGenerator.java @@ -20,9 +20,11 @@ public class EnumGenerator implements CodeGenerator { private final String enumName; private ParameterSpec[] parameters; + private List superinterfaces = new LinkedList<>(); private List methods = new LinkedList<>(); - private List fields = new LinkedList<>(); + private List staticFields = new LinkedList<>(); private List instances = new LinkedList<>(); + private List fields = new LinkedList<>(); private List hardcodedFields = new LinkedList<>(); private List annotations = new LinkedList<>(); private String enumPackage; @@ -35,6 +37,10 @@ public class EnumGenerator implements CodeGenerator { this.enumName = enumName; } + public void addSuperinterface(TypeName typeNames) { + superinterfaces.add(typeNames); + } + public void setParams(ParameterSpec... parameters) { this.parameters = parameters; } @@ -52,7 +58,7 @@ public class EnumGenerator implements CodeGenerator { } public void addStaticField(TypeName type, String name, String value) { - fields.add(new Field(type, name, value)); + staticFields.add(new Field(type, name, value)); } public void addInstance(String name, Object... parameters) { @@ -86,6 +92,9 @@ public class EnumGenerator implements CodeGenerator { enumClass.addEnumConstant(instance.name, arguments); } + // add superinterfaces + enumClass.addSuperinterfaces(superinterfaces); + if (staticBlock != null) { enumClass.addStaticBlock(staticBlock); } @@ -100,7 +109,7 @@ public class EnumGenerator implements CodeGenerator { .build()); } - for (Field field : fields) { + for (Field field : staticFields) { enumClass.addField(FieldSpec.builder(field.type, field.name) .initializer("$L", field.value) .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) @@ -115,6 +124,13 @@ public class EnumGenerator implements CodeGenerator { .build()); } + // normal fields + for (Field field : fields) { + enumClass.addField(FieldSpec.builder(field.type, field.name) + .addModifiers(Modifier.PRIVATE) + .build()); + } + // constructor MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder(); for (int i = 0; i < parameters.length; i++) { @@ -166,6 +182,10 @@ public class EnumGenerator implements CodeGenerator { constructorEnds.add(constructorEnding); } + public void addField(TypeName type, String name) { + fields.add(new Field(type, name)); + } + public void addHardcodedField(TypeName type, String name, String value) { hardcodedFields.add(new Field(type, name, value)); } @@ -210,6 +230,10 @@ public class EnumGenerator implements CodeGenerator { private String name; private String value; + public Field(TypeName type, String name) { + this(type, name, null); + } + public Field(TypeName type, String name, String value) { this.type = type; this.name = name; diff --git a/src/generators/java/net/minestom/codegen/blocks/BlockEnumGenerator.java b/src/generators/java/net/minestom/codegen/blocks/BlockEnumGenerator.java index 7b519b96e..89437ae5f 100644 --- a/src/generators/java/net/minestom/codegen/blocks/BlockEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/blocks/BlockEnumGenerator.java @@ -5,6 +5,8 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.squareup.javapoet.*; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.codegen.EnumGenerator; import net.minestom.codegen.MinestomEnumGenerator; import net.minestom.codegen.PrismarinePaths; @@ -298,6 +300,12 @@ public class BlockEnumGenerator extends MinestomEnumGenerator { .endControlFlow() .addStatement("$T.blocks.put($T.from(namespaceID), this)", Registries.class, NamespaceID.class); }); + + // implement Keyed + generator.addSuperinterface(ClassName.get(Keyed.class)); + generator.addField(ClassName.get(Key.class), "key"); + generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)")); + generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key")); } @Override diff --git a/src/generators/java/net/minestom/codegen/entitytypes/EntityTypeEnumGenerator.java b/src/generators/java/net/minestom/codegen/entitytypes/EntityTypeEnumGenerator.java index dc8649a30..007975c49 100644 --- a/src/generators/java/net/minestom/codegen/entitytypes/EntityTypeEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/entitytypes/EntityTypeEnumGenerator.java @@ -4,6 +4,8 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.squareup.javapoet.*; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.codegen.ConstructorLambda; import net.minestom.codegen.EnumGenerator; import net.minestom.codegen.MinestomEnumGenerator; @@ -19,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.lang.model.element.TypeElement; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -177,6 +180,12 @@ public class EntityTypeEnumGenerator extends MinestomEnumGenerator code.addStatement("this.key = Key.key(this.namespaceID)")); + generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key")); } @Override diff --git a/src/generators/java/net/minestom/codegen/items/ItemEnumGenerator.java b/src/generators/java/net/minestom/codegen/items/ItemEnumGenerator.java index 4b4f44053..fba48697b 100644 --- a/src/generators/java/net/minestom/codegen/items/ItemEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/items/ItemEnumGenerator.java @@ -4,6 +4,8 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.squareup.javapoet.*; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.Keyed; import net.minestom.codegen.EnumGenerator; import net.minestom.codegen.MinestomEnumGenerator; import net.minestom.codegen.PrismarinePaths; @@ -218,6 +220,12 @@ public class ItemEnumGenerator extends MinestomEnumGenerator { .endControlFlow() .addStatement("return isFood()"); }); + + // implement Keyed + generator.addSuperinterface(ClassName.get(Keyed.class)); + generator.addField(ClassName.get(Key.class), "key"); + generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)")); + generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key")); } @Override From 426b93db8fb68bfbea1a1d778c83676ce43052e5 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 16:44:58 +0000 Subject: [PATCH 092/364] delegate hover event for items/entities to adventure, fixes #161 --- .../minestom/server/chat/ChatHoverEvent.java | 38 +++++++++++++++---- .../net/minestom/server/chat/RichMessage.java | 2 +- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/minestom/server/chat/ChatHoverEvent.java b/src/main/java/net/minestom/server/chat/ChatHoverEvent.java index a236ff9b4..f10722f71 100644 --- a/src/main/java/net/minestom/server/chat/ChatHoverEvent.java +++ b/src/main/java/net/minestom/server/chat/ChatHoverEvent.java @@ -1,12 +1,20 @@ package net.minestom.server.chat; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minestom.server.entity.Entity; +import net.minestom.server.entity.EntityType; import net.minestom.server.item.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; +import java.util.UUID; + /** * Represents a hover event for a specific portion of the message. */ @@ -38,12 +46,12 @@ public class ChatHoverEvent { } @Nullable - protected String getValue() { + public String getValue() { return value; } @Nullable - protected JsonObject getValueObject() { + public JsonObject getValueObject() { return valueObject; } @@ -81,8 +89,17 @@ public class ChatHoverEvent { */ @NotNull public static ChatHoverEvent showItem(@NotNull ItemStack itemStack) { - final String json = itemStack.toNBT().toSNBT(); - return new ChatHoverEvent("show_item", json); + HoverEvent event = HoverEvent.showItem(itemStack.getMaterial().key(), itemStack.getAmount()); + JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject(); + obj = obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject(); + + if (itemStack.getItemMeta() != null) { + NBTCompound compound = new NBTCompound(); + itemStack.getItemMeta().write(compound); + obj.add("tag", new JsonPrimitive(compound.toSNBT())); + } + + return new ChatHoverEvent("show_item", obj); } /** @@ -93,9 +110,14 @@ public class ChatHoverEvent { */ @NotNull public static ChatHoverEvent showEntity(@NotNull Entity entity) { - NBTCompound compound = new NBTCompound() - .setString("id", entity.getUuid().toString()) - .setString("type", entity.getEntityType().getNamespaceID()); - return new ChatHoverEvent("show_entity", compound.toSNBT()); + HoverEvent event = HoverEvent.showEntity(entity.getEntityType().key(), entity.getUuid()); + JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject(); + return new ChatHoverEvent("show_entity", obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject()); + } + + public static ChatHoverEvent showEntity(UUID uuid, EntityType entityType) { + HoverEvent event = HoverEvent.showEntity(entityType.key(), uuid); + JsonObject obj = GsonComponentSerializer.gson().serializer().toJsonTree(Component.empty().hoverEvent(event)).getAsJsonObject(); + return new ChatHoverEvent("show_entity", obj.get("hoverEvent").getAsJsonObject().get("contents").getAsJsonObject()); } } diff --git a/src/main/java/net/minestom/server/chat/RichMessage.java b/src/main/java/net/minestom/server/chat/RichMessage.java index 986e1727c..52a70d0a0 100644 --- a/src/main/java/net/minestom/server/chat/RichMessage.java +++ b/src/main/java/net/minestom/server/chat/RichMessage.java @@ -160,7 +160,7 @@ public class RichMessage extends JsonMessage { // The value is a JsonObject hoverObject = new JsonObject(); hoverObject.addProperty("action", hoverEvent.getAction()); - hoverObject.add("value", hoverEvent.getValueObject()); + hoverObject.add("contents", hoverEvent.getValueObject()); } else { // The value is a raw string final String hoverValue = hoverEvent.getValue(); From d4e82cedebd91155c4ccbe513862b39210bd8ae7 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 16:54:53 +0000 Subject: [PATCH 093/364] bump adventure version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6cce96516..d0c4cda3a 100644 --- a/build.gradle +++ b/build.gradle @@ -184,7 +184,7 @@ dependencies { generatorsImplementation("com.squareup:javapoet:1.13.0") // Adventure, for text messages - api platform("net.kyori:adventure-bom:4.5.1") + api platform("net.kyori:adventure-bom:4.6.0") api "net.kyori:adventure-api" api "net.kyori:adventure-text-serializer-gson" api "net.kyori:adventure-text-serializer-plain" From f20d2ac273313706677b4a3667e44a1226107550 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 17:02:34 +0000 Subject: [PATCH 094/364] prefer iterable audiences --- src/main/java/net/minestom/server/MinecraftServer.java | 10 +++++++--- src/main/java/net/minestom/server/Viewable.java | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index 0518f5f89..45c38c133 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -59,6 +59,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; /** * The main server class used to start the server and retrieve all the managers. @@ -66,7 +68,7 @@ import java.io.IOException; * The server needs to be initialized with {@link #init()} and started with {@link #start(String, int)}. * You should register all of your dimensions, biomes, commands, events, etc... in-between. */ -public final class MinecraftServer implements ForwardingAudience.Single { +public final class MinecraftServer implements ForwardingAudience { public final static Logger LOGGER = LoggerFactory.getLogger(MinecraftServer.class); @@ -832,7 +834,9 @@ public final class MinecraftServer implements ForwardingAudience.Single { } @Override - public @NotNull Audience audience() { - return getConnectionManager(); + public @NotNull Iterable audiences() { + HashSet audiences = new HashSet<>(getConnectionManager().getOnlinePlayers()); + audiences.add(getCommandManager().getConsoleSender()); + return Collections.unmodifiableSet(audiences); } } diff --git a/src/main/java/net/minestom/server/Viewable.java b/src/main/java/net/minestom/server/Viewable.java index d7c3de2dd..683f2597e 100644 --- a/src/main/java/net/minestom/server/Viewable.java +++ b/src/main/java/net/minestom/server/Viewable.java @@ -6,6 +6,7 @@ import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.utils.PacketUtils; import org.jetbrains.annotations.NotNull; +import java.util.Collections; import java.util.Set; /** @@ -89,7 +90,7 @@ public interface Viewable { * * @return the audience */ - default @NotNull Audience asAudience() { - return Audience.audience(this.getViewers()); + default @NotNull Iterable getViewersAsAudiences() { + return Collections.unmodifiableSet(this.getViewers()); } } From 89a962de9e853b1dceb78caabf6f3c4c12b85edd Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 17:03:20 +0000 Subject: [PATCH 095/364] remove more checker annotations --- src/main/java/net/minestom/server/scoreboard/Scoreboard.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/net/minestom/server/scoreboard/Scoreboard.java b/src/main/java/net/minestom/server/scoreboard/Scoreboard.java index e75178bf6..5f3c85afc 100644 --- a/src/main/java/net/minestom/server/scoreboard/Scoreboard.java +++ b/src/main/java/net/minestom/server/scoreboard/Scoreboard.java @@ -3,14 +3,12 @@ package net.minestom.server.scoreboard; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; import net.minestom.server.MinecraftServer; import net.minestom.server.Viewable; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.DisplayScoreboardPacket; import net.minestom.server.network.packet.server.play.ScoreboardObjectivePacket; import net.minestom.server.network.packet.server.play.UpdateScorePacket; -import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; /** @@ -104,7 +102,7 @@ public interface Scoreboard extends Viewable, ForwardingAudience { String getObjectiveName(); @Override - @NonNull default Iterable audiences() { + @NotNull default Iterable audiences() { return this.getViewers(); } } From 4179e5b91e4b02e2c6fd80bc5f814ee49ccd3ba7 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 18:49:38 +0000 Subject: [PATCH 096/364] allow obtaining enum values from registries using adventure key --- .../minestom/server/registry/Registries.java | 81 +++++++++++++++++++ .../net/minestom/codegen/AllGenerators.java | 1 - .../minestom/codegen/RegistriesGenerator.java | 12 +++ .../minestom/server/utils/NamespaceID.java | 5 ++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/autogenerated/java/net/minestom/server/registry/Registries.java b/src/autogenerated/java/net/minestom/server/registry/Registries.java index b4ab492cd..b38f4c8a4 100644 --- a/src/autogenerated/java/net/minestom/server/registry/Registries.java +++ b/src/autogenerated/java/net/minestom/server/registry/Registries.java @@ -2,6 +2,7 @@ package net.minestom.server.registry; import java.util.HashMap; +import net.kyori.adventure.key.Key; import net.minestom.server.entity.EntityType; import net.minestom.server.fluids.Fluid; import net.minestom.server.instance.block.Block; @@ -96,6 +97,14 @@ public final class Registries { return blocks.getOrDefault(id, Block.AIR); } + /** + * Returns the corresponding Block matching the given key. Returns 'AIR' if none match. + */ + @NotNull + public static Block getBlock(Key key) { + return getBlock(NamespaceID.from(key)); + } + /** * Returns the corresponding Material matching the given id. Returns 'AIR' if none match. */ @@ -112,6 +121,14 @@ public final class Registries { return materials.getOrDefault(id, Material.AIR); } + /** + * Returns the corresponding Material matching the given key. Returns 'AIR' if none match. + */ + @NotNull + public static Material getMaterial(Key key) { + return getMaterial(NamespaceID.from(key)); + } + /** * Returns the corresponding Enchantment matching the given id. Returns null if none match. */ @@ -128,6 +145,14 @@ public final class Registries { return enchantments.get(id); } + /** + * Returns the corresponding Enchantment matching the given key. Returns null if none match. + */ + @Nullable + public static Enchantment getEnchantment(Key key) { + return getEnchantment(NamespaceID.from(key)); + } + /** * Returns the corresponding EntityType matching the given id. Returns null if none match. */ @@ -144,6 +169,14 @@ public final class Registries { return entityTypes.get(id); } + /** + * Returns the corresponding EntityType matching the given key. Returns null if none match. + */ + @Nullable + public static EntityType getEntityType(Key key) { + return getEntityType(NamespaceID.from(key)); + } + /** * Returns the corresponding Particle matching the given id. Returns null if none match. */ @@ -160,6 +193,14 @@ public final class Registries { return particles.get(id); } + /** + * Returns the corresponding Particle matching the given key. Returns null if none match. + */ + @Nullable + public static Particle getParticle(Key key) { + return getParticle(NamespaceID.from(key)); + } + /** * Returns the corresponding PotionType matching the given id. Returns null if none match. */ @@ -176,6 +217,14 @@ public final class Registries { return potionTypes.get(id); } + /** + * Returns the corresponding PotionType matching the given key. Returns null if none match. + */ + @Nullable + public static PotionType getPotionType(Key key) { + return getPotionType(NamespaceID.from(key)); + } + /** * Returns the corresponding PotionEffect matching the given id. Returns null if none match. */ @@ -192,6 +241,14 @@ public final class Registries { return potionEffects.get(id); } + /** + * Returns the corresponding PotionEffect matching the given key. Returns null if none match. + */ + @Nullable + public static PotionEffect getPotionEffect(Key key) { + return getPotionEffect(NamespaceID.from(key)); + } + /** * Returns the corresponding Sound matching the given id. Returns null if none match. */ @@ -208,6 +265,14 @@ public final class Registries { return sounds.get(id); } + /** + * Returns the corresponding Sound matching the given key. Returns null if none match. + */ + @Nullable + public static Sound getSound(Key key) { + return getSound(NamespaceID.from(key)); + } + /** * Returns the corresponding StatisticType matching the given id. Returns null if none match. */ @@ -224,6 +289,14 @@ public final class Registries { return statisticTypes.get(id); } + /** + * Returns the corresponding StatisticType matching the given key. Returns null if none match. + */ + @Nullable + public static StatisticType getStatisticType(Key key) { + return getStatisticType(NamespaceID.from(key)); + } + /** * Returns the corresponding Fluid matching the given id. Returns 'EMPTY' if none match. */ @@ -239,4 +312,12 @@ public final class Registries { public static Fluid getFluid(NamespaceID id) { return fluids.getOrDefault(id, Fluid.EMPTY); } + + /** + * Returns the corresponding Fluid matching the given key. Returns 'EMPTY' if none match. + */ + @NotNull + public static Fluid getFluid(Key key) { + return getFluid(NamespaceID.from(key)); + } } diff --git a/src/generators/java/net/minestom/codegen/AllGenerators.java b/src/generators/java/net/minestom/codegen/AllGenerators.java index cc1ad65f1..d63856e81 100644 --- a/src/generators/java/net/minestom/codegen/AllGenerators.java +++ b/src/generators/java/net/minestom/codegen/AllGenerators.java @@ -16,7 +16,6 @@ import java.io.IOException; public class AllGenerators { public static void main(String[] args) throws IOException { - args = new String[]{"1.16.5", "src/autogenerated/java"}; BlockEnumGenerator.main(args); ItemEnumGenerator.main(args); // must be done after block PotionEnumGenerator.main(args); diff --git a/src/generators/java/net/minestom/codegen/RegistriesGenerator.java b/src/generators/java/net/minestom/codegen/RegistriesGenerator.java index fdcbba675..b96c2eb46 100644 --- a/src/generators/java/net/minestom/codegen/RegistriesGenerator.java +++ b/src/generators/java/net/minestom/codegen/RegistriesGenerator.java @@ -1,6 +1,7 @@ package net.minestom.codegen; import com.squareup.javapoet.*; +import net.kyori.adventure.key.Key; import net.minestom.server.entity.EntityType; import net.minestom.server.fluids.Fluid; import net.minestom.server.instance.block.Block; @@ -101,6 +102,7 @@ public class RegistriesGenerator implements CodeGenerator { ParameterSpec namespaceIDParam = ParameterSpec.builder(ClassName.get(NamespaceID.class), "id") .build(); + ParameterSpec keyIDParam = ParameterSpec.builder(ClassName.get(Key.class), "key").build(); CodeBlock.Builder code = CodeBlock.builder(); Class annotation; @@ -134,6 +136,16 @@ public class RegistriesGenerator implements CodeGenerator { .addCode(code.build()) .addJavadoc(comment.toString()) .build()); + + // Key variant + registriesClass.addMethod(MethodSpec.methodBuilder("get" + simpleType) + .returns(type) + .addAnnotation(annotation) + .addModifiers(Modifier.STATIC, Modifier.PUBLIC) + .addParameter(keyIDParam) + .addStatement("return get$N(NamespaceID.from($N))", simpleType, keyIDParam) + .addJavadoc(comment.toString().replace(" id.", " key.")) + .build()); } JavaFile file = JavaFile.builder("net.minestom.server.registry", registriesClass.build()) diff --git a/src/main/java/net/minestom/server/utils/NamespaceID.java b/src/main/java/net/minestom/server/utils/NamespaceID.java index 9c22c76b2..b4706349f 100644 --- a/src/main/java/net/minestom/server/utils/NamespaceID.java +++ b/src/main/java/net/minestom/server/utils/NamespaceID.java @@ -1,6 +1,7 @@ package net.minestom.server.utils; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.kyori.adventure.key.Key; import org.jetbrains.annotations.NotNull; import java.util.Objects; @@ -62,6 +63,10 @@ public class NamespaceID implements CharSequence { return from(getDomain(id), getPath(id)); } + public static NamespaceID from(Key key) { + return from(key.asString()); + } + private NamespaceID(@NotNull String path) { final int index = path.indexOf(':'); if (index < 0) { From c25dbaa9367292181c8622c12f12b03cec544172 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 18:53:04 +0000 Subject: [PATCH 097/364] swap utils to registry --- .../minestom/server/adventure/AdventureUtils.java | 12 ------------ src/main/java/net/minestom/server/entity/Player.java | 3 ++- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/minestom/server/adventure/AdventureUtils.java b/src/main/java/net/minestom/server/adventure/AdventureUtils.java index cd23b178c..e61f9c181 100644 --- a/src/main/java/net/minestom/server/adventure/AdventureUtils.java +++ b/src/main/java/net/minestom/server/adventure/AdventureUtils.java @@ -13,17 +13,5 @@ import java.util.stream.Collectors; * Adventure related utilities. */ public class AdventureUtils { - private static final Map SOUND_MAP = - Arrays.stream(Sound.values()).collect(Collectors.toMap(Sound::getNamespaceID, sound -> sound)); - /** - * Attempts to get an NMS sound from an Adventure key. - * - * @param name the key - * - * @return the sound, if found - */ - public static @Nullable Sound asSound(@NotNull Key name) { - return SOUND_MAP.get(name.asString()); - } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index adebaab5e..22b999f45 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -53,6 +53,7 @@ import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.recipe.Recipe; import net.minestom.server.recipe.RecipeManager; +import net.minestom.server.registry.Registries; import net.minestom.server.resourcepack.ResourcePack; import net.minestom.server.scoreboard.BelowNameTag; import net.minestom.server.scoreboard.Team; @@ -917,7 +918,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { @Override public void playSound(net.kyori.adventure.sound.@NotNull Sound sound, double x, double y, double z) { - Sound minestomSound = AdventureUtils.asSound(sound.name()); + Sound minestomSound = Registries.getSound(sound.name()); if (minestomSound == null) { NamedSoundEffectPacket packet = new NamedSoundEffectPacket(); From 94b737812dd27f88ee8cc8b5b337518201536628 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 19:07:33 +0000 Subject: [PATCH 098/364] add converter from adventure enums to packet values --- .../adventure/AdventurePacketConvertor.java | 48 +++++++++++++++++++ .../server/adventure/AdventureUtils.java | 17 ------- .../server/adventure/BossBarManager.java | 22 +++------ .../packet/server/play/SoundEffectPacket.java | 3 +- 4 files changed, 57 insertions(+), 33 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java delete mode 100644 src/main/java/net/minestom/server/adventure/AdventureUtils.java diff --git a/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java b/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java new file mode 100644 index 000000000..98d4f5675 --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java @@ -0,0 +1,48 @@ +package net.minestom.server.adventure; + +import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Utility methods to convert adventure enums to their packet values. + */ +public class AdventurePacketConvertor { + + public static int getBossBarOverlayValue(@NotNull BossBar.Overlay overlay) { + return overlay.ordinal(); + } + + public static byte getBossBarFlagValue(@NotNull Collection flags) { + byte val = 0x0; + for (BossBar.Flag flag : flags) { + val |= flag.ordinal(); + } + return val; + } + + public static int getBossBarColorValue(@NotNull BossBar.Color color) { + return color.ordinal(); + } + + public static int getSoundCategoryValue(@NotNull Sound.Source source) { + return source.ordinal(); + } + + public static byte getMessageTypeValue(@NotNull MessageType messageType) { + switch (messageType) { + case CHAT: return 0x00; + case SYSTEM: return 0x01; + } + + throw new IllegalArgumentException("Cannot get message type"); + } +} diff --git a/src/main/java/net/minestom/server/adventure/AdventureUtils.java b/src/main/java/net/minestom/server/adventure/AdventureUtils.java deleted file mode 100644 index e61f9c181..000000000 --- a/src/main/java/net/minestom/server/adventure/AdventureUtils.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.minestom.server.adventure; - -import net.kyori.adventure.key.Key; -import net.minestom.server.sound.Sound; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Adventure related utilities. - */ -public class AdventureUtils { - -} diff --git a/src/main/java/net/minestom/server/adventure/BossBarManager.java b/src/main/java/net/minestom/server/adventure/BossBarManager.java index a86123fc3..8a1c780e2 100644 --- a/src/main/java/net/minestom/server/adventure/BossBarManager.java +++ b/src/main/java/net/minestom/server/adventure/BossBarManager.java @@ -166,10 +166,10 @@ public class BossBarManager implements BossBar.Listener { @NotNull BossBarPacket createAddPacket() { return this.createGenericPacket(ADD, packet -> { packet.title = MinecraftServer.getSerializationManager().serialize(bar.name()); - packet.color = bar.color().ordinal(); - packet.division = bar.overlay().ordinal(); + packet.color = AdventurePacketConvertor.getBossBarColorValue(bar.color()); + packet.division = AdventurePacketConvertor.getBossBarOverlayValue(bar.overlay()); packet.health = bar.progress(); - packet.flags = serializeFlags(bar.flags()); + packet.flags = AdventurePacketConvertor.getBossBarFlagValue(bar.flags()); }); } @@ -180,7 +180,7 @@ public class BossBarManager implements BossBar.Listener { @NotNull BossBarPacket createColorUpdate(@NotNull Color color) { return this.createGenericPacket(UPDATE_STYLE, packet -> { packet.color = color.ordinal(); - packet.division = bar.overlay().ordinal(); + packet.division = AdventurePacketConvertor.getBossBarOverlayValue(bar.overlay()); }); } @@ -193,13 +193,13 @@ public class BossBarManager implements BossBar.Listener { } @NotNull BossBarPacket createFlagsUpdate(@NotNull Set newFlags) { - return this.createGenericPacket(UPDATE_FLAGS, packet -> packet.flags = serializeFlags(bar.flags())); + return this.createGenericPacket(UPDATE_FLAGS, packet -> packet.flags = AdventurePacketConvertor.getBossBarFlagValue(bar.flags())); } @NotNull BossBarPacket createOverlayUpdate(@NotNull Overlay overlay) { return this.createGenericPacket(UPDATE_STYLE, packet -> { - packet.division = overlay.ordinal(); - packet.color = bar.color().ordinal(); + packet.division = AdventurePacketConvertor.getBossBarOverlayValue(overlay); + packet.color = AdventurePacketConvertor.getBossBarColorValue(bar.color()); }); } @@ -210,13 +210,5 @@ public class BossBarManager implements BossBar.Listener { consumer.accept(packet); return packet; } - - private static byte serializeFlags(@NotNull Set flags) { - byte val = 0x0; - for (Flag flag : flags) { - val |= flag.ordinal(); - } - return val; - } } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java index 35880697c..693399f0e 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java @@ -1,6 +1,7 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.sound.Sound.Source; +import net.minestom.server.adventure.AdventurePacketConvertor; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.sound.Sound; @@ -43,7 +44,7 @@ public class SoundEffectPacket implements ServerPacket { public static SoundEffectPacket create(Source category, Sound sound, Position position, float volume, float pitch) { SoundEffectPacket packet = new SoundEffectPacket(); packet.soundId = sound.getId(); - packet.soundCategory = category.ordinal(); + packet.soundCategory = AdventurePacketConvertor.getSoundCategoryValue(category); // *8 converts to fixed-point representation with 3 bits for fractional part packet.x = (int) position.getX(); packet.y = (int) position.getY(); From 35e057a63891821dae43c42742287deea13a6c30 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 5 Mar 2021 19:59:28 +0000 Subject: [PATCH 099/364] prefer minestom enums over raw packet values --- .../adventure/AdventurePacketConvertor.java | 2 +- .../net/minestom/server/entity/Player.java | 13 ++-- .../server/listener/ChatMessageListener.java | 2 +- .../server/network/ConnectionManager.java | 2 +- .../packet/server/play/ChatMessagePacket.java | 75 +++++++------------ .../server/play/NamedSoundEffectPacket.java | 11 ++- .../packet/server/play/SoundEffectPacket.java | 21 ++---- .../minestom/server/sound/SoundCategory.java | 18 ++++- 8 files changed, 65 insertions(+), 79 deletions(-) diff --git a/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java b/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java index 98d4f5675..8377ab9be 100644 --- a/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java +++ b/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java @@ -33,7 +33,7 @@ public class AdventurePacketConvertor { return color.ordinal(); } - public static int getSoundCategoryValue(@NotNull Sound.Source source) { + public static int getSoundSourceValue(@NotNull Sound.Source source) { return source.ordinal(); } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 22b999f45..61a17117e 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -10,6 +10,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.title.Title; import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.AdvancementTab; +import net.minestom.server.adventure.AdventurePacketConvertor; import net.minestom.server.attribute.Attribute; import net.minestom.server.adventure.AdventureUtils; import net.minestom.server.adventure.Localizable; @@ -804,7 +805,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { @Override public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { - ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().serialize(message, this), type, source.uuid()); + ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().serialize(message, this), ChatMessagePacket.Position.fromMessageType(type), source.uuid()); playerConnection.sendPacket(chatMessagePacket); } @@ -835,7 +836,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { public void playSound(@NotNull Sound sound, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) { SoundEffectPacket soundEffectPacket = new SoundEffectPacket(); soundEffectPacket.soundId = sound.getId(); - soundEffectPacket.soundCategory = soundCategory.ordinal(); + soundEffectPacket.soundSource = soundCategory.asSource(); soundEffectPacket.x = x; soundEffectPacket.y = y; soundEffectPacket.z = z; @@ -871,7 +872,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { public void playSound(@NotNull String identifier, @NotNull SoundCategory soundCategory, int x, int y, int z, float volume, float pitch) { NamedSoundEffectPacket namedSoundEffectPacket = new NamedSoundEffectPacket(); namedSoundEffectPacket.soundName = identifier; - namedSoundEffectPacket.soundCategory = soundCategory.ordinal(); + namedSoundEffectPacket.soundSource = soundCategory.asSource(); namedSoundEffectPacket.x = x; namedSoundEffectPacket.y = y; namedSoundEffectPacket.z = z; @@ -923,7 +924,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { if (minestomSound == null) { NamedSoundEffectPacket packet = new NamedSoundEffectPacket(); packet.soundName = sound.name().asString(); - packet.soundCategory = sound.source().ordinal(); + packet.soundSource = sound.source(); packet.x = (int) x; packet.y = (int) y; packet.z = (int) z; @@ -933,7 +934,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { } else { SoundEffectPacket packet = new SoundEffectPacket(); packet.soundId = minestomSound.getId(); - packet.soundCategory = sound.source().ordinal(); + packet.soundSource = sound.source(); packet.x = (int) x; packet.y = (int) y; packet.z = (int) z; @@ -950,7 +951,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { if (stop.source() != null) { packet.flags |= 0x1; - packet.source = stop.source().ordinal(); + packet.source = AdventurePacketConvertor.getSoundSourceValue(stop.source()); } if (stop.sound() != null) { diff --git a/src/main/java/net/minestom/server/listener/ChatMessageListener.java b/src/main/java/net/minestom/server/listener/ChatMessageListener.java index 41a817802..90aff7a4b 100644 --- a/src/main/java/net/minestom/server/listener/ChatMessageListener.java +++ b/src/main/java/net/minestom/server/listener/ChatMessageListener.java @@ -58,7 +58,7 @@ public class ChatMessageListener { // Send the message with the correct player UUID ChatMessagePacket chatMessagePacket = - new ChatMessagePacket(jsonMessage, MessageType.CHAT, player.getUuid()); + new ChatMessagePacket(jsonMessage, ChatMessagePacket.Position.CHAT, player.getUuid()); PacketUtils.sendGroupedPacket(recipients, chatMessagePacket); } diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 091755835..997bf173c 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -172,7 +172,7 @@ public final class ConnectionManager implements ForwardingAudience { } private void broadcastJson(@NotNull String json, @NotNull Collection recipients) { - ChatMessagePacket chatMessagePacket = new ChatMessagePacket(json, MessageType.SYSTEM); + ChatMessagePacket chatMessagePacket = new ChatMessagePacket(json, ChatMessagePacket.Position.SYSTEM_MESSAGE); PacketUtils.sendGroupedPacket(recipients, chatMessagePacket); } 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 ff0e5eb54..56f84edc9 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,8 +1,6 @@ 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.chat.JsonMessage; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; @@ -13,66 +11,34 @@ 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. + * Represents an outgoing chat message packet. */ public class ChatMessagePacket implements ServerPacket { private static final UUID NULL_UUID = new UUID(0, 0); public String message; - public MessageType messageType; + public Position position; public UUID uuid; /** * @deprecated Use {@link #message} */ public @Deprecated JsonMessage jsonMessage; - /** - * @deprecated Use {@link #messageType} - */ - public @Deprecated Position position; - @Deprecated public ChatMessagePacket(String jsonMessage, Position position, UUID uuid) { - this(jsonMessage, position.asMessageType(), uuid); + this.message = jsonMessage; + this.position = position; + this.uuid = uuid; } - @Deprecated public ChatMessagePacket(String jsonMessage, Position position) { 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 message the raw message payload - * @param messageType the message type - * @param uuid the sender of the chat message - */ - public ChatMessagePacket(String message, MessageType messageType, UUID uuid) { - this.message = message; - this.messageType = messageType; - this.uuid = uuid; - } - @Override public void write(@NotNull BinaryWriter writer) { writer.writeSizedString(jsonMessage != null ? jsonMessage.toString() : message); - writer.writeByte((byte) (position != null ? position.ordinal() : messageType == null ? 3 : messageType.ordinal())); + writer.writeByte((byte) position.ordinal()); writer.writeUuid(uuid); } @@ -81,10 +47,6 @@ public class ChatMessagePacket implements ServerPacket { return ServerPacketIdentifier.CHAT_MESSAGE; } - /** - * @deprecated Use {@link MessageType} - */ - @Deprecated public enum Position { CHAT(MessageType.CHAT), SYSTEM_MESSAGE(MessageType.SYSTEM), @@ -97,13 +59,28 @@ public class ChatMessagePacket implements ServerPacket { } /** - * 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 + * Gets the Adventure message type from this position. Note that there is no + * message type for {@link #GAME_INFO}, as Adventure uses the title methods for this. + * + * @return the message type, if any */ - public @Nullable MessageType asMessageType() { + public @Nullable MessageType getMessageType() { return this.messageType; } + + /** + * Gets a position from an Adventure message type. + * + * @param messageType the message type + * + * @return the position + */ + public static @NotNull Position fromMessageType(@NotNull MessageType messageType) { + switch (messageType) { + case CHAT: return CHAT; + case SYSTEM: return SYSTEM_MESSAGE; + } + throw new IllegalArgumentException("Cannot get position from message type!"); + } } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java index b4b1114d7..2a2230d2d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/NamedSoundEffectPacket.java @@ -1,5 +1,8 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.sound.Sound.Source; +import net.minestom.server.adventure.AdventurePacketConvertor; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.sound.SoundCategory; @@ -9,20 +12,20 @@ import org.jetbrains.annotations.NotNull; public class NamedSoundEffectPacket implements ServerPacket { public String soundName; - public int soundCategory; + public Source soundSource; public int x, y, z; public float volume; public float pitch; /** - * @deprecated Use {@link #soundCategory} + * @deprecated Use {@link #soundSource} */ - @Deprecated public SoundCategory soundCategoryOld; + @Deprecated public SoundCategory soundCategory; @Override public void write(@NotNull BinaryWriter writer) { writer.writeSizedString(soundName); - writer.writeVarInt(soundCategoryOld != null ? soundCategoryOld.ordinal() : soundCategory); + writer.writeVarInt(AdventurePacketConvertor.getSoundSourceValue(soundSource)); writer.writeInt(x * 8); writer.writeInt(y * 8); writer.writeInt(z * 8); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java index 693399f0e..130eb9b6b 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java @@ -13,38 +13,29 @@ import org.jetbrains.annotations.NotNull; public class SoundEffectPacket implements ServerPacket { public int soundId; - public int soundCategory; + public Source soundSource; public int x, y, z; public float volume; public float pitch; /** - * @deprecated Use {@link #soundCategory} + * @deprecated Use {@link #soundSource} */ - @Deprecated public SoundCategory soundCategoryOld; + @Deprecated public SoundCategory soundCategory; /** * @deprecated Use variables */ @Deprecated public static SoundEffectPacket create(SoundCategory category, Sound sound, Position position, float volume, float pitch) { - SoundEffectPacket packet = new SoundEffectPacket(); - packet.soundId = sound.getId(); - packet.soundCategory = category.ordinal(); - // *8 converts to fixed-point representation with 3 bits for fractional part - packet.x = (int) position.getX(); - packet.y = (int) position.getY(); - packet.z = (int) position.getZ(); - packet.volume = volume; - packet.pitch = pitch; - return packet; + return create(category.asSource(), sound, position, volume, pitch); } @NotNull public static SoundEffectPacket create(Source category, Sound sound, Position position, float volume, float pitch) { SoundEffectPacket packet = new SoundEffectPacket(); packet.soundId = sound.getId(); - packet.soundCategory = AdventurePacketConvertor.getSoundCategoryValue(category); + packet.soundSource = category; // *8 converts to fixed-point representation with 3 bits for fractional part packet.x = (int) position.getX(); packet.y = (int) position.getY(); @@ -56,7 +47,7 @@ public class SoundEffectPacket implements ServerPacket { @Override public void write(@NotNull BinaryWriter writer) { writer.writeVarInt(soundId); - writer.writeVarInt(soundCategoryOld != null ? soundCategoryOld.ordinal() : soundCategory); + writer.writeVarInt(AdventurePacketConvertor.getSoundSourceValue(soundSource)); writer.writeInt(x * 8); writer.writeInt(y * 8); writer.writeInt(z * 8); diff --git a/src/main/java/net/minestom/server/sound/SoundCategory.java b/src/main/java/net/minestom/server/sound/SoundCategory.java index c80f46131..15c4d62d2 100644 --- a/src/main/java/net/minestom/server/sound/SoundCategory.java +++ b/src/main/java/net/minestom/server/sound/SoundCategory.java @@ -1,7 +1,12 @@ package net.minestom.server.sound; +import net.kyori.adventure.sound.Sound; +import org.jetbrains.annotations.NotNull; + +import static net.kyori.adventure.sound.Sound.*; + /** - * @deprecated Use {@link net.kyori.adventure.sound.Sound.Source} + * @deprecated Use {@link Source} */ @Deprecated public enum SoundCategory { @@ -14,5 +19,14 @@ public enum SoundCategory { NEUTRAL, PLAYERS, AMBIENT, - VOICE + VOICE; + + /** + * Gets the Adventure source representing this sound category. + * + * @return the source + */ + public @NotNull Source asSource() { + return Source.values()[this.ordinal()]; + } } From 327558c0035139aff66f98f41b87e30fb1bd2ad2 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Wed, 10 Mar 2021 16:13:27 +0000 Subject: [PATCH 100/364] begin bringing packets back to use components --- .../server/advancements/Advancement.java | 5 +- .../notifications/NotificationCenter.java | 6 +- .../adventure/AdventurePacketConvertor.java | 5 - .../server/adventure/BossBarManager.java | 36 +++- .../server/adventure/Localizable.java | 10 ++ .../adventure/LocalizablePacketSender.java | 158 ++++++++++++++++++ .../adventure/SerializationManager.java | 132 ++++++--------- .../net/minestom/server/bossbar/BarColor.java | 6 +- .../minestom/server/bossbar/BarDivision.java | 6 +- .../net/minestom/server/bossbar/BossBar.java | 10 +- .../net/minestom/server/entity/Player.java | 30 ++-- .../server/item/metadata/WrittenBookMeta.java | 12 +- .../server/network/ConnectionManager.java | 17 +- .../client/handshake/HandshakePacket.java | 4 +- .../login/LoginPluginResponsePacket.java | 2 +- .../packet/client/login/LoginStartPacket.java | 4 +- .../server/login/LoginDisconnectPacket.java | 11 +- .../server/play/AdvancementsPacket.java | 19 +-- .../packet/server/play/BossBarPacket.java | 37 ++-- .../packet/server/play/ChatMessagePacket.java | 19 +-- .../packet/server/play/CombatEventPacket.java | 13 +- .../packet/server/play/DisconnectPacket.java | 18 +- .../packet/server/play/MapDataPacket.java | 14 +- .../packet/server/play/PlayerInfoPacket.java | 27 +-- .../play/PlayerListHeaderAndFooterPacket.java | 27 +-- .../play/ScoreboardObjectivePacket.java | 12 +- .../packet/server/play/TabCompletePacket.java | 11 +- .../packet/server/play/TeamsPacket.java | 34 +--- .../packet/server/play/TitlePacket.java | 14 +- .../server/scoreboard/Scoreboard.java | 3 +- .../net/minestom/server/scoreboard/Team.java | 12 +- .../server/utils/binary/BinaryWriter.java | 10 ++ 32 files changed, 399 insertions(+), 325 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java diff --git a/src/main/java/net/minestom/server/advancements/Advancement.java b/src/main/java/net/minestom/server/advancements/Advancement.java index 9b10a074c..d16c1a449 100644 --- a/src/main/java/net/minestom/server/advancements/Advancement.java +++ b/src/main/java/net/minestom/server/advancements/Advancement.java @@ -1,7 +1,6 @@ package net.minestom.server.advancements; import net.kyori.adventure.text.Component; -import net.minestom.server.MinecraftServer; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Player; import net.minestom.server.item.ItemStack; @@ -375,8 +374,8 @@ public class Advancement { AdvancementsPacket.DisplayData displayData = new AdvancementsPacket.DisplayData(); displayData.x = x; displayData.y = y; - displayData.title = MinecraftServer.getSerializationManager().serialize(title); - displayData.description = MinecraftServer.getSerializationManager().serialize(description); + displayData.title = title; + displayData.description = description; displayData.icon = icon; displayData.frameType = frameType; displayData.flags = getFlags(); diff --git a/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java b/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java index 836221493..57f597d3d 100644 --- a/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java +++ b/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java @@ -1,6 +1,6 @@ package net.minestom.server.advancements.notifications; -import net.minestom.server.MinecraftServer; +import net.kyori.adventure.text.Component; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.AdvancementsPacket; import net.minestom.server.network.player.PlayerConnection; @@ -82,9 +82,9 @@ public class NotificationCenter { // Setup display data for the advancement AdvancementsPacket.DisplayData displayData = new AdvancementsPacket.DisplayData(); { - displayData.title = MinecraftServer.getSerializationManager().serialize(notification.getTitle()); + displayData.title = notification.getTitle(); // Description is required, but never shown/seen so, small Easter egg. - displayData.description = "Articdive was here. #Minestom"; + displayData.description = Component.text("Articdive was here. #Minestom"); displayData.icon = notification.getIcon(); displayData.frameType = notification.getFrameType(); displayData.flags = 0x6; diff --git a/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java b/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java index 8377ab9be..24cf7c819 100644 --- a/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java +++ b/src/main/java/net/minestom/server/adventure/AdventurePacketConvertor.java @@ -2,15 +2,10 @@ package net.minestom.server.adventure; import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.bossbar.BossBar; -import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.Arrays; import java.util.Collection; -import java.util.Map; -import java.util.stream.Collectors; /** * Utility methods to convert adventure enums to their packet values. diff --git a/src/main/java/net/minestom/server/adventure/BossBarManager.java b/src/main/java/net/minestom/server/adventure/BossBarManager.java index 8a1c780e2..a1574b2bc 100644 --- a/src/main/java/net/minestom/server/adventure/BossBarManager.java +++ b/src/main/java/net/minestom/server/adventure/BossBarManager.java @@ -47,7 +47,10 @@ public class BossBarManager implements BossBar.Listener { Holder holder = this.getOrCreateHandler(bar); if (holder.players.add(player.getUuid())) { - player.getPlayerConnection().sendPacket(holder.createAddPacket()); + BossBarPacket packet = holder.createAddPacket(); + packet.title = MinecraftServer.getSerializationManager().prepare(packet.title, player); + + player.getPlayerConnection().sendPacket(packet); } } /** @@ -105,12 +108,27 @@ public class BossBarManager implements BossBar.Listener { private void updatePlayers(BossBarPacket packet, Set players) { Iterator iterator = players.iterator(); + // check if we need to translate the bossbar + Component rawTitle = packet.title; + boolean translate = false; + if (packet.action == UPDATE_TITLE || packet.action == ADD) { + Component rootTitle = MinecraftServer.getSerializationManager().prepare(rawTitle, MinecraftServer.getSerializationManager().getDefaultLocale()); + + if (!rawTitle.equals(rootTitle)) { + translate = true; + } + } + while (iterator.hasNext()) { Player player = MinecraftServer.getConnectionManager().getPlayer(iterator.next()); if (player == null) { iterator.remove(); } else { + if (translate) { + packet.title = MinecraftServer.getSerializationManager().prepare(rawTitle, player); + } + player.getPlayerConnection().sendPacket(packet); } } @@ -165,9 +183,9 @@ public class BossBarManager implements BossBar.Listener { @NotNull BossBarPacket createAddPacket() { return this.createGenericPacket(ADD, packet -> { - packet.title = MinecraftServer.getSerializationManager().serialize(bar.name()); - packet.color = AdventurePacketConvertor.getBossBarColorValue(bar.color()); - packet.division = AdventurePacketConvertor.getBossBarOverlayValue(bar.overlay()); + packet.title = bar.name(); + packet.color = bar.color(); + packet.overlay = bar.overlay(); packet.health = bar.progress(); packet.flags = AdventurePacketConvertor.getBossBarFlagValue(bar.flags()); }); @@ -179,13 +197,13 @@ public class BossBarManager implements BossBar.Listener { @NotNull BossBarPacket createColorUpdate(@NotNull Color color) { return this.createGenericPacket(UPDATE_STYLE, packet -> { - packet.color = color.ordinal(); - packet.division = AdventurePacketConvertor.getBossBarOverlayValue(bar.overlay()); + packet.color = color; + packet.overlay = bar.overlay(); }); } @NotNull BossBarPacket createTitleUpdate(@NotNull Component title) { - return this.createGenericPacket(UPDATE_TITLE, packet -> packet.title = MinecraftServer.getSerializationManager().serialize(title)); + return this.createGenericPacket(UPDATE_TITLE, packet -> packet.title = title); } @NotNull BossBarPacket createFlagsUpdate() { @@ -198,8 +216,8 @@ public class BossBarManager implements BossBar.Listener { @NotNull BossBarPacket createOverlayUpdate(@NotNull Overlay overlay) { return this.createGenericPacket(UPDATE_STYLE, packet -> { - packet.division = AdventurePacketConvertor.getBossBarOverlayValue(overlay); - packet.color = AdventurePacketConvertor.getBossBarColorValue(bar.color()); + packet.overlay = overlay; + packet.color = bar.color(); }); } diff --git a/src/main/java/net/minestom/server/adventure/Localizable.java b/src/main/java/net/minestom/server/adventure/Localizable.java index d854823ae..27612db54 100644 --- a/src/main/java/net/minestom/server/adventure/Localizable.java +++ b/src/main/java/net/minestom/server/adventure/Localizable.java @@ -1,5 +1,6 @@ package net.minestom.server.adventure; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Locale; @@ -9,6 +10,15 @@ import java.util.Locale; */ public interface Localizable { + /** + * Gets a localizable that returns {@code null} for all calls to {@link #getLocale()}. + * + * @return the empty localizable + */ + static @NotNull Localizable empty() { + return SerializationManager.NULL_LOCALIZABLE; + } + /** * Gets the locale. * diff --git a/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java b/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java new file mode 100644 index 000000000..4abf4808a --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java @@ -0,0 +1,158 @@ +package net.minestom.server.adventure; + +import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title; +import net.minestom.server.MinecraftServer; +import net.minestom.server.entity.Player; +import net.minestom.server.network.packet.server.play.ChatMessagePacket; +import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket; +import net.minestom.server.network.packet.server.play.TitlePacket; +import net.minestom.server.utils.PacketUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * Utility class for sending packets with translatable components. All functions in this + * class will send grouped packets if the components do not contain any translatable + * components. In the case that they do, the components are translated and send individually. + */ +public class LocalizablePacketSender { + + /** + * Sends a title to many players, sending it as a grouped packet if it does not + * contain translatable elements. + * + * @param players the players + * @param title the title + */ + public static void sendGroupedTitle(@NotNull Collection players, @NotNull Title title) { + Component preparedTitle = MinecraftServer.getSerializationManager().prepare(title.title(), MinecraftServer.getSerializationManager().getDefaultLocale()), + preparedSubtitle = MinecraftServer.getSerializationManager().prepare(title.subtitle(), MinecraftServer.getSerializationManager().getDefaultLocale()); + Collection rootPacket = TitlePacket.of(Title.title(preparedTitle, preparedSubtitle, title.times())); + + if (title.title().equals(preparedTitle) && title.subtitle().equals(preparedSubtitle)) { + for (TitlePacket packet : rootPacket) { + PacketUtils.sendGroupedPacket(players, packet); + } + } else { + for (Player player : players) { + Collection packets; + + if (player.getLocale() == null) { + packets = rootPacket; + } else { + packets = TitlePacket.of(Title.title(MinecraftServer.getSerializationManager().prepare(title.title(), player), + MinecraftServer.getSerializationManager().prepare(title.subtitle(), player), title.times())); + } + + for (TitlePacket packet : packets) { + player.getPlayerConnection().sendPacket(packet); + } + } + } + } + + /** + * Sends an action bar to many players, sending it as a grouped packet if it does not + * contain translatable elements. + * + * @param players the players + * @param component the component + */ + public static void sendGroupedActionBar(@NotNull Collection players, @NotNull Component component) { + Component preparedComponent = MinecraftServer.getSerializationManager().prepare(component, MinecraftServer.getSerializationManager().getDefaultLocale()); + TitlePacket rootPacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, preparedComponent); + + if (component.equals(preparedComponent)) { + PacketUtils.sendGroupedPacket(players, rootPacket); + } else { + for (Player player : players) { + TitlePacket packet; + + if (player.getLocale() == null) { + packet = rootPacket; + } else { + packet = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().prepare(component, player)); + } + + player.getPlayerConnection().sendPacket(packet); + } + } + } + + /** + * Sends a player list to many players, sending it as a grouped packet if it does not + * contain translatable elements. + * + * @param players the players + * @param header the header + * @param footer the footer + */ + public static void sendGroupedPlayerList(@NotNull Collection players, @Nullable Component header, @Nullable Component footer) { + // empty check first + if (header == null) { + header = Component.empty(); + } + + if (footer == null) { + footer = Component.empty(); + } + + // now back to the packets + Component preparedHeader = MinecraftServer.getSerializationManager().prepare(header, MinecraftServer.getSerializationManager().getDefaultLocale()), + preparedFooter = MinecraftServer.getSerializationManager().prepare(footer, MinecraftServer.getSerializationManager().getDefaultLocale()); + PlayerListHeaderAndFooterPacket rootPacket = new PlayerListHeaderAndFooterPacket(preparedHeader, preparedFooter); + + if (header.equals(preparedHeader) && footer.equals(preparedFooter)) { + PacketUtils.sendGroupedPacket(players, rootPacket); + } else { + for (Player player : players) { + PlayerListHeaderAndFooterPacket packet; + + if (player.getLocale() == null) { + packet = rootPacket; + } else { + packet = new PlayerListHeaderAndFooterPacket(MinecraftServer.getSerializationManager().prepare(header, player), + MinecraftServer.getSerializationManager().prepare(footer, player)); + } + + player.getPlayerConnection().sendPacket(packet); + } + } + } + + /** + * Sends a message to many players, sending it as a grouped packet if it does not + * contain translatable elements. + * + * @param players the players + * @param source the source of the message + * @param message the message + * @param messageType the type of the message + */ + public static void sendGroupedMessage(@NotNull Collection players, @NotNull Identity source, @NotNull Component message, @NotNull MessageType messageType) { + ChatMessagePacket.Position position = ChatMessagePacket.Position.fromMessageType(messageType); + Component preparedMessage = MinecraftServer.getSerializationManager().prepare(message, MinecraftServer.getSerializationManager().getDefaultLocale()); + ChatMessagePacket rootPacket = new ChatMessagePacket(preparedMessage, position, source.uuid()); + + if (message.equals(preparedMessage)) { + PacketUtils.sendGroupedPacket(players, rootPacket); + } else { + for (Player player : players) { + ChatMessagePacket packet; + + if (player.getLocale() == null) { + packet = rootPacket; + } else { + packet = new ChatMessagePacket(MinecraftServer.getSerializationManager().prepare(message, player), position, source.uuid()); + } + + player.getPlayerConnection().sendPacket(packet); + } + } + } +} diff --git a/src/main/java/net/minestom/server/adventure/SerializationManager.java b/src/main/java/net/minestom/server/adventure/SerializationManager.java index ecc87ff07..e171cda88 100644 --- a/src/main/java/net/minestom/server/adventure/SerializationManager.java +++ b/src/main/java/net/minestom/server/adventure/SerializationManager.java @@ -6,14 +6,10 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.translation.GlobalTranslator; import net.kyori.adventure.translation.TranslationRegistry; import net.kyori.adventure.translation.Translator; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Locale; import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Function; /** @@ -22,11 +18,7 @@ import java.util.function.Function; * class can be used to change the way text is serialized. For example, a pre-JSON * implementation of Minestom could change this to the plain component serializer. *

- * This manager also provides the ability to wrap the serializer in a renderer that - * performs operations on each component before the final serialization. This can be - * done using {@link #addRenderer(Function)} and {@link #removeRenderer(Function)}. - *

- * Finally, this manager also performs translation on all messages and the {@code serialize} + * This manager also performs translation on all messages and the {@code serialize} * method should be used when converting {@link Component}s into strings. This allows for * messages with {@link TranslatableComponent} to be automatically translated into the locale * of specific players, or other elements which implement {@link Localizable}. To add your @@ -34,12 +26,13 @@ import java.util.function.Function; * {@link TranslationRegistry} or your own implementation of {@link Translator}. */ public class SerializationManager { - private final Set> renderers = new CopyOnWriteArraySet<>(); + protected static final Localizable NULL_LOCALIZABLE = () -> null; + private Function serializer = component -> GsonComponentSerializer.gson().serialize(component); private Locale defaultLocale = Locale.US; /** - * Gets the root serializer that is used to convert Components into Strings. + * Gets the root serializer that is used to convert components into strings. * * @return the serializer */ @@ -48,8 +41,7 @@ public class SerializationManager { } /** - * Sets the root serializer that is used to convert Components into Strings. This - * method does not replace any existing renderers set with {@link #addRenderer(Function)}. + * Sets the root serializer that is used to convert components into strings. * * @param serializer the serializer */ @@ -58,9 +50,9 @@ public class SerializationManager { } /** - * Gets the default locale used to translate {@link TranslatableComponent} if - * serialized using {@link #serialize(Component)} or when {@link #serialize(Component, Localizable)} - * is used but no translation is found. Note that this is just shorthand for + * Gets the default locale used to translate {@link TranslatableComponent} if, when + * {@link #prepare(Component, Localizable)} is called with a localizable that + * does not have a locale. * * @return the default locale */ @@ -69,9 +61,9 @@ public class SerializationManager { } /** - * Sets the default locale used to translate {@link TranslatableComponent} if - * serialized using {@link #serialize(Component)} or when {@link #serialize(Component, Localizable)} - * is used but no translation is found. + * Sets the default locale used to translate {@link TranslatableComponent} if, when + * {@link #prepare(Component, Localizable)} is called with a localizable that + * does not have a locale. * * @param defaultLocale the new default locale */ @@ -79,33 +71,6 @@ public class SerializationManager { this.defaultLocale = defaultLocale; } - /** - * Adds a renderer that will be applied to each serializer. The order in which - * each renderer will be applied is arbitrary. If you want control over the order - * of renderers, create a multi-function using {@link Function#andThen(Function)}. - * - * @param renderer the renderer - */ - public void addRenderer(@NotNull Function renderer) { - this.renderers.add(renderer); - } - - /** - * Removes a renderer. - * - * @param renderer the renderer - */ - public void removeRenderer(@NotNull Function renderer) { - this.renderers.remove(renderer); - } - - /** - * Removes all current renderers. - */ - public void clearRenderers() { - this.renderers.clear(); - } - /** * Gets the global translator object used by this manager. This is just shorthand for * {@link GlobalTranslator#get()}. @@ -117,62 +82,63 @@ public class SerializationManager { } /** - * Serializes a component into a String using the current serializer. Any registered - * renderers are applied first, followed by the global translator. Finally, the - * serializer set with {@link #setSerializer(Function)} is used to convert the - * component into a String. + * Prepares a component for serialization. This runs the component through the + * translator for the localizable's locale. * * @param component the component + * @param localizable the localizable * - * @return the serialized string + * @return the prepared component */ - @Contract("null -> null; !null -> !null") - public String serialize(@Nullable Component component) { - return this.serialize(component, this.defaultLocale); + public @NotNull Component prepare(@NotNull Component component, @NotNull Localizable localizable) { + return GlobalTranslator.renderer().render(component, Objects.requireNonNullElse(localizable.getLocale(), this.getDefaultLocale())); } /** - * Serializes a component into a String using the current serializer. Any registered - * renderers are applied first, followed by the global translator. Finally, the - * serializer set with {@link #setSerializer(Function)} is used to convert the - * component into a String. + * Prepares a component for serialization. This runs the component through the + * translator for the locale. * * @param component the component - * @param localizable a localizable object used to translate components + * @param locale the locale * - * @return the serialized string + * @return the prepared component */ - @Contract("null, _ -> null; !null, _ -> !null") - public String serialize(@Nullable Component component, @NotNull Localizable localizable) { - return this.serialize(component, Objects.requireNonNullElse(localizable.getLocale(), this.defaultLocale)); + public @NotNull Component prepare(@NotNull Component component, @NotNull Locale locale) { + return GlobalTranslator.renderer().render(component, locale); } /** - * Serializes a component into a String using the current serializer. Any registered - * renderers are applied first, followed by the global translator. Finally, the - * serializer set with {@link #setSerializer(Function)} is used to convert the - * component into a String. + * Serializes a component into a string using {@link #getSerializer()}. * * @param component the component - * @param locale the locale used to translate components * * @return the serialized string */ - @Contract("null, _ -> null; !null, _ -> !null") - public String serialize(@Nullable Component component, @NotNull Locale locale) { - if (component == null) { - return null; - } - - // apply renderers - for (Function renderer : this.renderers) { - component = renderer.apply(component); - } - - // apply translation - component = GlobalTranslator.render(component, locale); - - // apply serialisation + public @NotNull String serialize(@NotNull Component component) { return this.serializer.apply(component); } + + /** + * Prepares and then serializes a component. + * + * @param component the component + * @param localizable the localisable + * + * @return the string + */ + public String prepareAndSerialize(@NotNull Component component, @NotNull Localizable localizable) { + return this.prepareAndSerialize(component, Objects.requireNonNullElse(localizable.getLocale(), this.getDefaultLocale())); + } + + /** + * Prepares and then serializes a component. + * + * @param component the component + * @param locale the locale + * + * @return the string + */ + public String prepareAndSerialize(@NotNull Component component, @NotNull Locale locale) { + return this.serialize(this.prepare(component, locale)); + } } diff --git a/src/main/java/net/minestom/server/bossbar/BarColor.java b/src/main/java/net/minestom/server/bossbar/BarColor.java index c9f5a7e08..9173782ea 100644 --- a/src/main/java/net/minestom/server/bossbar/BarColor.java +++ b/src/main/java/net/minestom/server/bossbar/BarColor.java @@ -12,5 +12,9 @@ public enum BarColor { GREEN, YELLOW, PURPLE, - WHITE + WHITE; + + public net.kyori.adventure.bossbar.BossBar.Color asAdventureColor() { + return net.kyori.adventure.bossbar.BossBar.Color.valueOf(this.name()); + } } diff --git a/src/main/java/net/minestom/server/bossbar/BarDivision.java b/src/main/java/net/minestom/server/bossbar/BarDivision.java index 9b79bbe51..54f9fe79f 100644 --- a/src/main/java/net/minestom/server/bossbar/BarDivision.java +++ b/src/main/java/net/minestom/server/bossbar/BarDivision.java @@ -11,5 +11,9 @@ public enum BarDivision { SEGMENT_6, SEGMENT_10, SEGMENT_12, - SEGMENT_20 + SEGMENT_20; + + public net.kyori.adventure.bossbar.BossBar.Overlay asAdventureOverlay() { + return net.kyori.adventure.bossbar.BossBar.Overlay.values()[this.ordinal()]; + } } diff --git a/src/main/java/net/minestom/server/bossbar/BossBar.java b/src/main/java/net/minestom/server/bossbar/BossBar.java index cd129d227..b2a043989 100644 --- a/src/main/java/net/minestom/server/bossbar/BossBar.java +++ b/src/main/java/net/minestom/server/bossbar/BossBar.java @@ -252,10 +252,10 @@ public class BossBar implements Viewable { BossBarPacket bossBarPacket = new BossBarPacket(); bossBarPacket.uuid = uuid; bossBarPacket.action = BossBarPacket.Action.ADD; - bossBarPacket.title = title.toString(); + bossBarPacket.title = title.asComponent(); bossBarPacket.health = progress; - bossBarPacket.color = color.ordinal(); - bossBarPacket.division = division.ordinal(); + bossBarPacket.color = color.asAdventureColor(); + bossBarPacket.overlay = division.asAdventureOverlay(); bossBarPacket.flags = flags; player.getPlayerConnection().sendPacket(bossBarPacket); } @@ -278,7 +278,7 @@ public class BossBar implements Viewable { BossBarPacket bossBarPacket = new BossBarPacket(); bossBarPacket.uuid = uuid; bossBarPacket.action = BossBarPacket.Action.UPDATE_TITLE; - bossBarPacket.title = title.toString(); + bossBarPacket.title = title.asComponent(); sendPacketToViewers(bossBarPacket); } @@ -294,7 +294,7 @@ public class BossBar implements Viewable { BossBarPacket bossBarPacket = new BossBarPacket(); bossBarPacket.uuid = uuid; bossBarPacket.action = BossBarPacket.Action.UPDATE_STYLE; - bossBarPacket.color = color.ordinal(); + bossBarPacket.color = color.asAdventureColor(); sendPacketToViewers(bossBarPacket); } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 61a17117e..0010b4e29 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -20,7 +20,6 @@ import net.minestom.server.chat.ChatParser; import net.minestom.server.attribute.Attributes; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; -import net.minestom.server.chat.RichMessage; import net.minestom.server.collision.BoundingBox; import net.minestom.server.command.CommandManager; import net.minestom.server.command.CommandSender; @@ -507,7 +506,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { // #buildDeathScreenText can return null, check here if (deathText != null) { - CombatEventPacket deathPacket = CombatEventPacket.death(this, null, MinecraftServer.getSerializationManager().serialize(deathText, this)); + CombatEventPacket deathPacket = CombatEventPacket.death(this, null, MinecraftServer.getSerializationManager().prepare(deathText, this)); playerConnection.sendPacket(deathPacket); } @@ -805,7 +804,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { @Override public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { - ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().serialize(message, this), ChatMessagePacket.Position.fromMessageType(type), source.uuid()); + ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().prepare(message, this), ChatMessagePacket.Position.fromMessageType(type), source.uuid()); playerConnection.sendPacket(chatMessagePacket); } @@ -1008,9 +1007,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable { @Override public void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) { - PlayerListHeaderAndFooterPacket packet = new PlayerListHeaderAndFooterPacket(); - packet.header = MinecraftServer.getSerializationManager().serialize(header, this); - packet.footer = MinecraftServer.getSerializationManager().serialize(footer, this); + PlayerListHeaderAndFooterPacket packet + = new PlayerListHeaderAndFooterPacket(MinecraftServer.getSerializationManager().prepare(header, this), MinecraftServer.getSerializationManager().prepare(footer, this)); playerConnection.sendPacket(packet); } @@ -1025,7 +1023,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { */ @Deprecated private void sendTitle(@NotNull JsonMessage text, @NotNull TitlePacket.Action action) { - TitlePacket titlePacket = new TitlePacket(action, text.toString()); + TitlePacket titlePacket = new TitlePacket(action, text.asComponent()); playerConnection.sendPacket(titlePacket); } @@ -1084,14 +1082,18 @@ public class Player extends LivingEntity implements CommandSender, Localizable { @Override public void showTitle(@NotNull Title title) { - for (TitlePacket titlePacket : TitlePacket.of(title)) { + Component preparedTitle = MinecraftServer.getSerializationManager().prepare(title.title(), this), + preparedSubtitle = MinecraftServer.getSerializationManager().prepare(title.subtitle(), this); + Collection packet = TitlePacket.of(Title.title(preparedTitle, preparedSubtitle, title.times())); + + for (TitlePacket titlePacket : packet) { playerConnection.sendPacket(titlePacket); } } @Override public void sendActionBar(@NotNull Component message) { - TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().serialize(message, this)); + TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().prepare(message, this)); playerConnection.sendPacket(titlePacket); } @@ -1175,7 +1177,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { public void openBook(@NotNull Book book) { // make the book ItemStack writtenBook = new ItemStack(Material.WRITTEN_BOOK, (byte) 1); - writtenBook.setItemMeta(WrittenBookMeta.fromAdventure(book)); + writtenBook.setItemMeta(WrittenBookMeta.fromAdventure(book, this)); // Set book in offhand SetSlotPacket setBookPacket = new SetSlotPacket(); @@ -1341,7 +1343,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { this.displayName = displayName; PlayerInfoPacket infoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.UPDATE_DISPLAY_NAME); - infoPacket.playerInfos.add(new PlayerInfoPacket.UpdateDisplayName(getUuid(), MinecraftServer.getSerializationManager().serialize(displayName))); + infoPacket.playerInfos.add(new PlayerInfoPacket.UpdateDisplayName(getUuid(), displayName)); sendPacketToViewersAndSelf(infoPacket); } @@ -1920,9 +1922,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable { // Packet type depends on the current player connection state final ServerPacket disconnectPacket; if (connectionState == ConnectionState.LOGIN) { - disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(component, this)); + disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().prepare(component, this)); } else { - disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().serialize(component, this)); + disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().prepare(component, this)); } if (playerConnection instanceof NettyPlayerConnection) { @@ -2547,7 +2549,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable { PlayerInfoPacket.AddPlayer addPlayer = new PlayerInfoPacket.AddPlayer(getUuid(), getUsername(), getGameMode(), getLatency()); - addPlayer.displayName = MinecraftServer.getSerializationManager().serialize(displayName); + addPlayer.displayName = displayName; // Skin support if (skin != null) { diff --git a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java index 2fc0b4201..b160bd59d 100644 --- a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java @@ -3,9 +3,7 @@ package net.minestom.server.item.metadata; import net.kyori.adventure.inventory.Book; import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; -import net.minestom.server.chat.ChatParser; -import net.minestom.server.chat.ColoredText; -import net.minestom.server.chat.JsonMessage; +import net.minestom.server.adventure.Localizable; import org.jetbrains.annotations.NotNull; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import org.jglrxavpok.hephaistos.nbt.NBTList; @@ -202,15 +200,17 @@ public class WrittenBookMeta extends ItemMeta { * resolved and the generation will default to {@link WrittenBookGeneration#ORIGINAL}. * * @param book the book + * @param localizable who the book is for * * @return the meta */ - public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book) { + public static @NotNull WrittenBookMeta fromAdventure(@NotNull Book book, @NotNull Localizable localizable) { + // make the book WrittenBookMeta meta = new WrittenBookMeta(); meta.resolved = false; meta.generation = WrittenBookGeneration.ORIGINAL; - meta.author = MinecraftServer.getSerializationManager().serialize(book.author()); - meta.title = MinecraftServer.getSerializationManager().serialize(book.title()); + meta.author = MinecraftServer.getSerializationManager().prepareAndSerialize(book.author(), localizable); + meta.title = MinecraftServer.getSerializationManager().prepareAndSerialize(book.title(), localizable); meta.pages = new ArrayList<>(); for (Component page : book.pages()) { diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 997bf173c..6c715fd37 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -4,9 +4,11 @@ import io.netty.channel.Channel; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.audience.MessageType; +import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; +import net.minestom.server.adventure.LocalizablePacketSender; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Player; import net.minestom.server.entity.fakeplayer.FakePlayer; @@ -17,12 +19,10 @@ import net.minestom.server.listener.manager.ClientPacketConsumer; import net.minestom.server.listener.manager.ServerPacketConsumer; import net.minestom.server.network.packet.client.login.LoginStartPacket; import net.minestom.server.network.packet.server.login.LoginSuccessPacket; -import net.minestom.server.network.packet.server.play.ChatMessagePacket; import net.minestom.server.network.packet.server.play.DisconnectPacket; import net.minestom.server.network.packet.server.play.KeepAlivePacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; -import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.async.AsyncUtils; import net.minestom.server.utils.callback.validator.PlayerValidator; import net.minestom.server.utils.validate.Check; @@ -156,7 +156,7 @@ public final class ConnectionManager implements ForwardingAudience { if (!recipients.isEmpty()) { final String jsonText = jsonMessage.toString(); - broadcastJson(jsonText, recipients); + LocalizablePacketSender.sendGroupedMessage(recipients, Identity.nil(), jsonMessage.asComponent(), MessageType.CHAT); } } @@ -168,13 +168,7 @@ public final class ConnectionManager implements ForwardingAudience { */ @Deprecated public void broadcastMessage(@NotNull JsonMessage jsonMessage) { - broadcastMessage(jsonMessage, null); - } - - private void broadcastJson(@NotNull String json, @NotNull Collection recipients) { - ChatMessagePacket chatMessagePacket = new ChatMessagePacket(json, ChatMessagePacket.Position.SYSTEM_MESSAGE); - - PacketUtils.sendGroupedPacket(recipients, chatMessagePacket); + this.sendMessage(jsonMessage); } private Collection getRecipients(@Nullable PlayerValidator condition) { @@ -491,10 +485,11 @@ public final class ConnectionManager implements ForwardingAudience { * Shutdowns the connection manager by kicking all the currently connected players. */ public void shutdown() { - DisconnectPacket disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().serialize(shutdownText)); + DisconnectPacket disconnectPacket = new DisconnectPacket(shutdownText); for (Player player : getOnlinePlayers()) { final PlayerConnection playerConnection = player.getPlayerConnection(); if (playerConnection instanceof NettyPlayerConnection) { + disconnectPacket.message = MinecraftServer.getSerializationManager().prepare(disconnectPacket.message, player); final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) playerConnection; final Channel channel = nettyPlayerConnection.getChannel(); channel.writeAndFlush(disconnectPacket); diff --git a/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java b/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java index a0441b745..f91716839 100644 --- a/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/handshake/HandshakePacket.java @@ -70,7 +70,7 @@ public class HandshakePacket implements ClientPreplayPacket { nettyPlayerConnection.UNSAFE_setBungeeSkin(playerSkin); } else { - nettyPlayerConnection.sendPacket(new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(INVALID_BUNGEE_FORWARDING))); + nettyPlayerConnection.sendPacket(new LoginDisconnectPacket(INVALID_BUNGEE_FORWARDING)); nettyPlayerConnection.disconnect(); return; } @@ -94,7 +94,7 @@ public class HandshakePacket implements ClientPreplayPacket { } } else { // Incorrect client version - connection.sendPacket(new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(INVALID_VERSION_TEXT))); + connection.sendPacket(new LoginDisconnectPacket(INVALID_VERSION_TEXT)); connection.disconnect(); } break; diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java index abe4a74e5..ab922299c 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginPluginResponsePacket.java @@ -80,7 +80,7 @@ public class LoginPluginResponsePacket implements ClientPreplayPacket { Player player = CONNECTION_MANAGER.startPlayState(connection, uuid, username, true); player.setSkin(playerSkin); } else { - LoginDisconnectPacket disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(INVALID_PROXY_RESPONSE)); + LoginDisconnectPacket disconnectPacket = new LoginDisconnectPacket(INVALID_PROXY_RESPONSE); nettyPlayerConnection.sendPacket(disconnectPacket); } diff --git a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java index 01fc88b5f..25000c288 100644 --- a/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java +++ b/src/main/java/net/minestom/server/network/packet/client/login/LoginStartPacket.java @@ -3,8 +3,6 @@ package net.minestom.server.network.packet.client.login; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; -import net.minestom.server.chat.ChatColor; -import net.minestom.server.chat.ColoredText; import net.minestom.server.entity.Player; import net.minestom.server.extras.MojangAuth; import net.minestom.server.extras.bungee.BungeeCordProxy; @@ -74,7 +72,7 @@ public class LoginStartPacket implements ClientPreplayPacket { if (MojangAuth.isEnabled() && isNettyClient) { // Mojang auth if (CONNECTION_MANAGER.getPlayer(username) != null) { - connection.sendPacket(new LoginDisconnectPacket(MinecraftServer.getSerializationManager().serialize(ALREADY_CONNECTED))); + connection.sendPacket(new LoginDisconnectPacket(ALREADY_CONNECTED)); connection.disconnect(); return; } diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java index 5c9b05b23..06e12369e 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java @@ -1,5 +1,6 @@ package net.minestom.server.network.packet.server.login; +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; @@ -8,23 +9,23 @@ import org.jetbrains.annotations.NotNull; public class LoginDisconnectPacket implements ServerPacket { - private final String kickMessage; // JSON text + private final Component kickMessage; // JSON text - public LoginDisconnectPacket(@NotNull String kickMessage) { + public LoginDisconnectPacket(@NotNull Component kickMessage) { this.kickMessage = kickMessage; } /** - * @deprecated Use {@link #LoginDisconnectPacket(String)} + * @deprecated Use {@link #LoginDisconnectPacket(Component)} */ @Deprecated public LoginDisconnectPacket(@NotNull JsonMessage jsonKickMessage) { - this(jsonKickMessage.toString()); + this(jsonKickMessage.asComponent()); } @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(kickMessage); + writer.writeComponent(kickMessage); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java index f8f950692..f6f8f963c 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java @@ -1,7 +1,7 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.text.Component; import net.minestom.server.advancements.FrameType; -import net.minestom.server.chat.JsonMessage; import net.minestom.server.item.ItemStack; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; @@ -84,8 +84,8 @@ public class AdvancementsPacket implements ServerPacket { } public static class DisplayData implements Writeable { - public String title; // Only text - public String description; // Only text + public Component title; // Only text + public Component description; // Only text public ItemStack icon; public FrameType frameType; public int flags; @@ -93,19 +93,10 @@ public class AdvancementsPacket implements ServerPacket { public float x; public float y; - /** - * @deprecated Use {@link #title} - */ - public @Deprecated JsonMessage titleJson; // Only text - /** - * @deprecated Use {@link #description} - */ - public @Deprecated JsonMessage descriptionJson; // Only text - @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(titleJson != null ? titleJson.toString() : title); - writer.writeSizedString(descriptionJson != null ? descriptionJson.toString() : description); + writer.writeComponent(title); + writer.writeComponent(description); writer.writeItemStack(icon); writer.writeVarInt(frameType.ordinal()); writer.writeInt(flags); diff --git a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java index a80b7724e..47e4d266a 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java @@ -1,8 +1,8 @@ package net.minestom.server.network.packet.server.play; -import net.minestom.server.bossbar.BarColor; -import net.minestom.server.bossbar.BarDivision; -import net.minestom.server.chat.JsonMessage; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; +import net.minestom.server.adventure.AdventurePacketConvertor; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; @@ -15,25 +15,12 @@ public class BossBarPacket implements ServerPacket { public UUID uuid; public Action action; - public String title; // Only text + public Component title; // Only text public float health; - public int color; - public int division; + public BossBar.Color color; + public BossBar.Overlay overlay; public byte flags; - /** - * @deprecated Use {@link #title} - */ - public @Deprecated JsonMessage titleJson; - /** - * @deprecated Use {@link #color} - */ - public @Deprecated BarColor colorOld; - /** - * @deprecated Use {@link #division} - */ - public @Deprecated BarDivision divisionOld; - @Override public void write(@NotNull BinaryWriter writer) { writer.writeUuid(uuid); @@ -41,10 +28,10 @@ public class BossBarPacket implements ServerPacket { switch (action) { case ADD: - writer.writeSizedString(titleJson != null ? titleJson.toString() : title); + writer.writeComponent(title); writer.writeFloat(health); - writer.writeVarInt(colorOld != null ? colorOld.ordinal() : color); - writer.writeVarInt(divisionOld != null ? divisionOld.ordinal() : division); + writer.writeVarInt(AdventurePacketConvertor.getBossBarColorValue(color)); + writer.writeVarInt(AdventurePacketConvertor.getBossBarOverlayValue(overlay)); writer.writeByte(flags); break; case REMOVE: @@ -54,11 +41,11 @@ public class BossBarPacket implements ServerPacket { writer.writeFloat(health); break; case UPDATE_TITLE: - writer.writeSizedString(titleJson != null ? titleJson.toString() : title); + writer.writeComponent(title); break; case UPDATE_STYLE: - writer.writeVarInt(colorOld != null ? colorOld.ordinal() : color); - writer.writeVarInt(divisionOld != null ? divisionOld.ordinal() : division); + writer.writeVarInt(AdventurePacketConvertor.getBossBarColorValue(color)); + writer.writeVarInt(AdventurePacketConvertor.getBossBarOverlayValue(overlay)); break; case UPDATE_FLAGS: writer.writeByte(flags); 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 56f84edc9..6eb2e559f 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,7 +1,7 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.audience.MessageType; -import net.minestom.server.chat.JsonMessage; +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; @@ -16,28 +16,23 @@ import java.util.UUID; public class ChatMessagePacket implements ServerPacket { private static final UUID NULL_UUID = new UUID(0, 0); - public String message; + public Component message; public Position position; public UUID uuid; - /** - * @deprecated Use {@link #message} - */ - public @Deprecated JsonMessage jsonMessage; - - public ChatMessagePacket(String jsonMessage, Position position, UUID uuid) { - this.message = jsonMessage; + public ChatMessagePacket(Component message, Position position, UUID uuid) { + this.message = message; this.position = position; this.uuid = uuid; } - public ChatMessagePacket(String jsonMessage, Position position) { - this(jsonMessage, position, NULL_UUID); + public ChatMessagePacket(Component message, Position position) { + this(message, position, NULL_UUID); } @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(jsonMessage != null ? jsonMessage.toString() : message); + writer.writeComponent(message); writer.writeByte((byte) position.ordinal()); writer.writeUuid(uuid); } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java index 5e39bf60f..fb54ad4da 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.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.entity.Entity; import net.minestom.server.entity.Player; @@ -8,8 +9,6 @@ import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -import java.util.Optional; - /** * Packet sent during combat to a {@link Player}. * Only death is supported for the moment (other events are ignored anyway as of 1.15.2) @@ -20,7 +19,7 @@ public class CombatEventPacket implements ServerPacket { private int duration; private int opponent; private int playerID; - private String deathMessage; + private Component deathMessage; private CombatEventPacket() { } @@ -40,14 +39,14 @@ public class CombatEventPacket implements ServerPacket { } /** - * @deprecated Use {@link #death(Player, Entity, String)} + * @deprecated Use {@link #death(Player, Entity, Component)} */ @Deprecated public static CombatEventPacket death(Player player, Entity killer, JsonMessage message) { - return death(player, killer, message.toString()); + return death(player, killer, message.asComponent()); } - public static CombatEventPacket death(Player player, Entity killer, String message) { + public static CombatEventPacket death(Player player, Entity killer, Component message) { CombatEventPacket packet = new CombatEventPacket(); packet.type = EventType.DEATH; packet.playerID = player.getEntityId(); @@ -72,7 +71,7 @@ public class CombatEventPacket implements ServerPacket { case DEATH: writer.writeVarInt(playerID); writer.writeInt(opponent); - writer.writeSizedString(deathMessage); + writer.writeComponent(deathMessage); break; } } 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 8d8293b47..4426e16ea 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,32 +8,27 @@ import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; public class DisconnectPacket implements ServerPacket { - public String message; + public Component message; /** - * @deprecated Use {@link #message} - */ - @Deprecated public JsonMessage messageJson; - - /** - * Creates a new disconnect packet with a given string. + * Creates a new disconnect packet with a given message. * @param message the message */ - public DisconnectPacket(@NotNull String message) { + public DisconnectPacket(@NotNull Component message) { this.message = message; } /** - * @deprecated Use {@link #DisconnectPacket(String)} + * @deprecated Use {@link #DisconnectPacket(Component)} */ @Deprecated public DisconnectPacket(@NotNull JsonMessage message){ - this(message.toString()); + this(message.asComponent()); } @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(messageJson != null ? messageJson.toString() : message); + writer.writeComponent(message); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java index a1ff4560d..446b96d34 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java @@ -1,6 +1,6 @@ package net.minestom.server.network.packet.server.play; -import net.minestom.server.chat.JsonMessage; +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; @@ -63,13 +63,7 @@ public class MapDataPacket implements ServerPacket { public int type; public byte x, z; public byte direction; - public String displayName; - - /** - * @deprecated Use {@link #displayName} - */ - @Deprecated - public JsonMessage displayNameJson; // Only text + public Component displayName; private void write(BinaryWriter writer) { writer.writeVarInt(type); @@ -77,10 +71,10 @@ public class MapDataPacket implements ServerPacket { writer.writeByte(z); writer.writeByte(direction); - final boolean hasDisplayName = displayName != null || displayNameJson != null; + final boolean hasDisplayName = displayName != null; writer.writeBoolean(hasDisplayName); if (hasDisplayName) { - writer.writeSizedString(displayNameJson != null ? displayNameJson.toString() : displayName); + writer.writeComponent(displayName); } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java index 0587bf88f..4d0a9b0ec 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.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.entity.GameMode; import net.minestom.server.network.packet.server.ServerPacket; @@ -75,12 +76,7 @@ public class PlayerInfoPacket implements ServerPacket { public List properties; public GameMode gameMode; public int ping; - public String displayName; - - /** - * @deprecated Use {@link #displayName} - */ - @Deprecated public JsonMessage displayNameJson; // Only text + public Component displayName; public AddPlayer(UUID uuid, String name, GameMode gameMode, int ping) { super(uuid); @@ -100,10 +96,10 @@ public class PlayerInfoPacket implements ServerPacket { writer.writeVarInt(gameMode.getId()); writer.writeVarInt(ping); - final boolean hasDisplayName = displayName != null || displayNameJson != null; + final boolean hasDisplayName = displayName != null; writer.writeBoolean(hasDisplayName); if (hasDisplayName) - writer.writeSizedString(displayNameJson != null ? displayNameJson.toString() : displayName); + writer.writeComponent(displayName); } public static class Property { @@ -166,22 +162,17 @@ public class PlayerInfoPacket implements ServerPacket { public static class UpdateDisplayName extends PlayerInfo { - public String displayName; + public Component displayName; /** - * @deprecated Use {@link #displayName} - */ - @Deprecated public JsonMessage displayNameJson; // Only text - - /** - * @deprecated Use {@link #UpdateDisplayName(UUID, String)} + * @deprecated Use {@link #UpdateDisplayName(UUID, Component)} */ @Deprecated public UpdateDisplayName(UUID uuid, JsonMessage displayName) { - this(uuid, displayName.toString()); + this(uuid, displayName.asComponent()); } - public UpdateDisplayName(UUID uuid, String displayName) { + public UpdateDisplayName(UUID uuid, Component displayName) { super(uuid); this.displayName = displayName; } @@ -191,7 +182,7 @@ public class PlayerInfoPacket implements ServerPacket { final boolean hasDisplayName = displayName != null; writer.writeBoolean(hasDisplayName); if (hasDisplayName) - writer.writeSizedString(displayNameJson != null ? displayNameJson.toString() : displayName); + writer.writeComponent(displayName); } } 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 b5889aa7f..ae86538a1 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,36 +1,27 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; -import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; -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.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Objects; public class PlayerListHeaderAndFooterPacket implements ServerPacket { - private static final String EMPTY_COMPONENT = GsonComponentSerializer.gson().serialize(Component.empty()); + public Component header; + public Component footer; - public String header; - public String footer; - - /** - * @deprecated Use {@link #header} - */ - @Deprecated public JsonMessage headerJson; - - /** - @deprecated Use {@link #footer} - */ - @Deprecated public JsonMessage footerJson; + public PlayerListHeaderAndFooterPacket(@Nullable Component header, @Nullable Component footer) { + this.header = header; + this.footer = footer; + } @Override public void write(@NotNull BinaryWriter writer) { - writer.writeSizedString(headerJson != null ? headerJson.toString() : Objects.requireNonNullElse(header, EMPTY_COMPONENT)); - writer.writeSizedString(footerJson != null ? footerJson.toString() : Objects.requireNonNullElse(footer, EMPTY_COMPONENT)); + writer.writeComponent(Objects.requireNonNullElse(header, Component.empty())); + writer.writeComponent(Objects.requireNonNullElse(footer, Component.empty())); } @Override diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java index 7866bde00..756e475ab 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java @@ -1,6 +1,6 @@ package net.minestom.server.network.packet.server.play; -import net.minestom.server.chat.JsonMessage; +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; @@ -21,25 +21,19 @@ public class ScoreboardObjectivePacket implements ServerPacket { /** * The text to be displayed for the score */ - public String objectiveValue; // Only text + public Component objectiveValue; // Only text /** * The type how the score is displayed */ public Type type; - /** - * @deprecated Use {@link #objectiveValue} - */ - @Deprecated - public JsonMessage objectiveValueJson; - @Override public void write(@NotNull BinaryWriter writer) { writer.writeSizedString(objectiveName); writer.writeByte(mode); if (mode == 0 || mode == 2) { - writer.writeSizedString(objectiveValueJson != null ? objectiveValueJson.toString() : objectiveValue); + writer.writeComponent(objectiveValue); writer.writeVarInt(type.ordinal()); } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java index 8b7a7d2d6..e78b1dadd 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java @@ -1,6 +1,6 @@ package net.minestom.server.network.packet.server.play; -import net.minestom.server.chat.JsonMessage; +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; @@ -24,7 +24,7 @@ public class TabCompletePacket implements ServerPacket { writer.writeSizedString(match.match); writer.writeBoolean(match.hasTooltip); if (match.hasTooltip) - writer.writeSizedString(match.tooltipJson != null ? match.tooltipJson.toString() : match.tooltip); + writer.writeComponent(match.tooltip); } } @@ -36,12 +36,7 @@ public class TabCompletePacket implements ServerPacket { public static class Match { public String match; public boolean hasTooltip; - public String tooltip; - - /** - * @deprecated Use {@link #tooltip} - */ - @Deprecated public String tooltipJson; + public Component tooltip; } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java index 6e861c8d1..af698abb1 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java @@ -1,6 +1,6 @@ package net.minestom.server.network.packet.server.play; -import net.minestom.server.chat.JsonMessage; +import net.kyori.adventure.text.Component; import net.minestom.server.color.TeamColor; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; @@ -24,7 +24,7 @@ public class TeamsPacket implements ServerPacket { /** * The display name for the team */ - public String teamDisplayName; + public Component teamDisplayName; /** * The friendly flags to */ @@ -44,34 +44,16 @@ public class TeamsPacket implements ServerPacket { /** * The prefix of the team */ - public String teamPrefix; + public Component teamPrefix; /** * The suffix of the team */ - public String teamSuffix; + public Component teamSuffix; /** * An array with all entities in the team */ public String[] entities; - - /** - * @deprecated Use {@link #teamColor} - */ - @Deprecated public int teamColorOld = -1; - /** - * @deprecated Use {@link #teamDisplayName} - */ - @Deprecated public JsonMessage teamDisplayNameJson; - /** - @deprecated Use {@link #teamPrefix} - */ - @Deprecated public JsonMessage teamPrefixJson; - /** - @deprecated Use {@link #teamSuffix} - */ - @Deprecated public JsonMessage teamSuffixJson; - /** * Writes data into the {@link BinaryWriter} * @@ -85,13 +67,13 @@ public class TeamsPacket implements ServerPacket { switch (action) { case CREATE_TEAM: case UPDATE_TEAM_INFO: - writer.writeSizedString(this.teamDisplayNameJson != null ? this.teamDisplayNameJson.toString() : this.teamDisplayName); + writer.writeComponent(this.teamDisplayName); writer.writeByte(this.friendlyFlags); writer.writeSizedString(this.nameTagVisibility.getIdentifier()); writer.writeSizedString(this.collisionRule.getIdentifier()); - writer.writeVarInt(this.teamColorOld != -1 ? this.teamColorOld : this.teamColor.getId()); - writer.writeSizedString(this.teamPrefixJson != null ? this.teamPrefixJson.toString() : this.teamPrefix); - writer.writeSizedString(this.teamSuffixJson != null ? this.teamSuffixJson.toString() : this.teamSuffix); + writer.writeVarInt(this.teamColor.getId()); + writer.writeComponent(this.teamPrefix); + writer.writeComponent(this.teamSuffix); break; case REMOVE_TEAM: 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 262423b8b..675ec785b 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,7 +1,7 @@ package net.minestom.server.network.packet.server.play; +import net.kyori.adventure.text.Component; import net.kyori.adventure.title.Title; -import net.minestom.server.MinecraftServer; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.TickUtils; @@ -19,21 +19,21 @@ public class TitlePacket implements ServerPacket { public Action action; - public String payload; + public Component payload; public int fadeIn; public int stay; public int fadeOut; /** - * Constructs a new title packet from an action that can take a string argument. + * Constructs a new title packet from an action that can take a component 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) { + public TitlePacket(@NotNull Action action, @NotNull Component payload) { Validate.isTrue(action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR, "Invalid action type"); this.action = action; this.payload = payload; @@ -72,7 +72,7 @@ public class TitlePacket implements ServerPacket { case SET_TITLE: case SET_SUBTITLE: case SET_ACTION_BAR: - writer.writeSizedString(payload); + writer.writeComponent(payload); break; case SET_TIMES_AND_DISPLAY: writer.writeInt(fadeIn); @@ -109,8 +109,8 @@ public class TitlePacket implements ServerPacket { List packets = new ArrayList<>(4); // base packets - packets.add(new TitlePacket(SET_TITLE, MinecraftServer.getSerializationManager().serialize(title.title()))); - packets.add(new TitlePacket(SET_SUBTITLE, MinecraftServer.getSerializationManager().serialize(title.subtitle()))); + packets.add(new TitlePacket(SET_TITLE, title.title())); + packets.add(new TitlePacket(SET_SUBTITLE, title.subtitle())); // times packet Title.Times times = title.times(); diff --git a/src/main/java/net/minestom/server/scoreboard/Scoreboard.java b/src/main/java/net/minestom/server/scoreboard/Scoreboard.java index 5f3c85afc..e16825a72 100644 --- a/src/main/java/net/minestom/server/scoreboard/Scoreboard.java +++ b/src/main/java/net/minestom/server/scoreboard/Scoreboard.java @@ -3,7 +3,6 @@ package net.minestom.server.scoreboard; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.text.Component; -import net.minestom.server.MinecraftServer; import net.minestom.server.Viewable; import net.minestom.server.entity.Player; import net.minestom.server.network.packet.server.play.DisplayScoreboardPacket; @@ -42,7 +41,7 @@ public interface Scoreboard extends Viewable, ForwardingAudience { final ScoreboardObjectivePacket packet = new ScoreboardObjectivePacket(); packet.objectiveName = this.getObjectiveName(); packet.mode = 0; // Create Scoreboard - packet.objectiveValue = MinecraftServer.getSerializationManager().serialize(value); + packet.objectiveValue = value; packet.type = type; return packet; diff --git a/src/main/java/net/minestom/server/scoreboard/Team.java b/src/main/java/net/minestom/server/scoreboard/Team.java index 18c412e2d..39c478e4f 100644 --- a/src/main/java/net/minestom/server/scoreboard/Team.java +++ b/src/main/java/net/minestom/server/scoreboard/Team.java @@ -409,13 +409,13 @@ public class Team implements ForwardingAudience { TeamsPacket teamsCreationPacket = new TeamsPacket(); teamsCreationPacket.teamName = teamName; teamsCreationPacket.action = TeamsPacket.Action.CREATE_TEAM; - teamsCreationPacket.teamDisplayName = MinecraftServer.getSerializationManager().serialize(this.teamDisplayName); + teamsCreationPacket.teamDisplayName = this.teamDisplayName; teamsCreationPacket.friendlyFlags = this.friendlyFlags; teamsCreationPacket.nameTagVisibility = this.nameTagVisibility; teamsCreationPacket.collisionRule = this.collisionRule; teamsCreationPacket.teamColor = this.teamColor; - teamsCreationPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(this.prefix); - teamsCreationPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(this.suffix); + teamsCreationPacket.teamPrefix = this.prefix; + teamsCreationPacket.teamSuffix = this.suffix; teamsCreationPacket.entities = this.members.toArray(new String[0]); return teamsCreationPacket; @@ -562,13 +562,13 @@ public class Team implements ForwardingAudience { final TeamsPacket updatePacket = new TeamsPacket(); updatePacket.teamName = this.teamName; updatePacket.action = TeamsPacket.Action.UPDATE_TEAM_INFO; - updatePacket.teamDisplayName = MinecraftServer.getSerializationManager().serialize(this.teamDisplayName); + updatePacket.teamDisplayName = this.teamDisplayName; updatePacket.friendlyFlags = this.friendlyFlags; updatePacket.nameTagVisibility = this.nameTagVisibility; updatePacket.collisionRule = this.collisionRule; updatePacket.teamColor = this.teamColor; - updatePacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(this.prefix); - updatePacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(this.suffix); + updatePacket.teamPrefix = this.prefix; + updatePacket.teamSuffix = this.suffix; PacketUtils.sendGroupedPacket(MinecraftServer.getConnectionManager().getOnlinePlayers(), updatePacket); } diff --git a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java index b7f1c430f..84a27d4ff 100644 --- a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java +++ b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java @@ -2,6 +2,7 @@ package net.minestom.server.utils.binary; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import net.kyori.adventure.text.Component; import net.minestom.server.MinecraftServer; import net.minestom.server.item.ItemStack; import net.minestom.server.utils.BlockPosition; @@ -61,6 +62,15 @@ public class BinaryWriter extends OutputStream { this.buffer = Unpooled.buffer(); } + /** + * Writes a component to the buffer as a sized string. + * + * @param component the component + */ + public void writeComponent(Component component) { + this.writeSizedString(MinecraftServer.getSerializationManager().serialize(component)); + } + /** * Writes a single boolean to the buffer. * From aea8a5f7944dc87c9bac862e5a675b26ec098642 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 11 Mar 2021 15:56:12 +0000 Subject: [PATCH 101/364] fix compilation issues --- .../java/net/minestom/server/entity/Player.java | 4 +--- .../server/listener/ChatMessageListener.java | 5 +---- .../net/minestom/server/scoreboard/Sidebar.java | 15 +++++++-------- src/test/java/demo/PlayerInit.java | 4 +--- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 0010b4e29..71de8172e 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -11,13 +11,11 @@ import net.kyori.adventure.title.Title; import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.AdvancementTab; import net.minestom.server.adventure.AdventurePacketConvertor; -import net.minestom.server.attribute.Attribute; -import net.minestom.server.adventure.AdventureUtils; import net.minestom.server.adventure.Localizable; +import net.minestom.server.attribute.Attribute; import net.minestom.server.attribute.AttributeInstance; import net.minestom.server.bossbar.BossBar; import net.minestom.server.chat.ChatParser; -import net.minestom.server.attribute.Attributes; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; import net.minestom.server.collision.BoundingBox; diff --git a/src/main/java/net/minestom/server/listener/ChatMessageListener.java b/src/main/java/net/minestom/server/listener/ChatMessageListener.java index 90aff7a4b..c5f1cff16 100644 --- a/src/main/java/net/minestom/server/listener/ChatMessageListener.java +++ b/src/main/java/net/minestom/server/listener/ChatMessageListener.java @@ -1,6 +1,5 @@ package net.minestom.server.listener; -import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.minestom.server.MinecraftServer; @@ -54,11 +53,9 @@ public class ChatMessageListener { final Collection recipients = playerChatEvent.getRecipients(); if (!recipients.isEmpty()) { - final String jsonMessage = MinecraftServer.getSerializationManager().serialize(textObject); - // Send the message with the correct player UUID ChatMessagePacket chatMessagePacket = - new ChatMessagePacket(jsonMessage, ChatMessagePacket.Position.CHAT, player.getUuid()); + new ChatMessagePacket(textObject, ChatMessagePacket.Position.CHAT, player.getUuid()); PacketUtils.sendGroupedPacket(recipients, chatMessagePacket); } diff --git a/src/main/java/net/minestom/server/scoreboard/Sidebar.java b/src/main/java/net/minestom/server/scoreboard/Sidebar.java index 7043b073d..995c51a0d 100644 --- a/src/main/java/net/minestom/server/scoreboard/Sidebar.java +++ b/src/main/java/net/minestom/server/scoreboard/Sidebar.java @@ -2,7 +2,6 @@ package net.minestom.server.scoreboard; import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet; import net.kyori.adventure.text.Component; -import net.minestom.server.MinecraftServer; import net.minestom.server.chat.JsonMessage; import net.minestom.server.color.TeamColor; import net.minestom.server.entity.Player; @@ -103,7 +102,7 @@ public class Sidebar implements Scoreboard { ScoreboardObjectivePacket scoreboardObjectivePacket = new ScoreboardObjectivePacket(); scoreboardObjectivePacket.objectiveName = objectiveName; scoreboardObjectivePacket.mode = 2; // Update display text - scoreboardObjectivePacket.objectiveValue = MinecraftServer.getSerializationManager().serialize(title); + scoreboardObjectivePacket.objectiveValue = title; scoreboardObjectivePacket.type = ScoreboardObjectivePacket.Type.INTEGER; sendPacketToViewers(scoreboardObjectivePacket); @@ -467,13 +466,13 @@ public class Sidebar implements Scoreboard { TeamsPacket teamsPacket = new TeamsPacket(); teamsPacket.teamName = teamName; teamsPacket.action = TeamsPacket.Action.CREATE_TEAM; - teamsPacket.teamDisplayName = MinecraftServer.getSerializationManager().serialize(teamDisplayName); + teamsPacket.teamDisplayName = teamDisplayName; teamsPacket.friendlyFlags = friendlyFlags; teamsPacket.nameTagVisibility = nameTagVisibility; teamsPacket.collisionRule = collisionRule; teamsPacket.teamColor = teamColor; - teamsPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(prefix); - teamsPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(suffix); + teamsPacket.teamPrefix = prefix; + teamsPacket.teamSuffix = suffix; teamsPacket.entities = new String[]{entityName}; return teamsPacket; } @@ -500,13 +499,13 @@ public class Sidebar implements Scoreboard { TeamsPacket teamsPacket = new TeamsPacket(); teamsPacket.teamName = teamName; teamsPacket.action = TeamsPacket.Action.UPDATE_TEAM_INFO; - teamsPacket.teamDisplayName = MinecraftServer.getSerializationManager().serialize(teamDisplayName); + teamsPacket.teamDisplayName = teamDisplayName; teamsPacket.friendlyFlags = friendlyFlags; teamsPacket.nameTagVisibility = nameTagVisibility; teamsPacket.collisionRule = collisionRule; teamsPacket.teamColor = teamColor; - teamsPacket.teamPrefix = MinecraftServer.getSerializationManager().serialize(prefix); - teamsPacket.teamSuffix = MinecraftServer.getSerializationManager().serialize(suffix); + teamsPacket.teamPrefix = prefix; + teamsPacket.teamSuffix = suffix; return teamsPacket; } diff --git a/src/test/java/demo/PlayerInit.java b/src/test/java/demo/PlayerInit.java index 3e277bd67..b8e1fb779 100644 --- a/src/test/java/demo/PlayerInit.java +++ b/src/test/java/demo/PlayerInit.java @@ -83,9 +83,7 @@ public class PlayerInit { final Component footer = Component.text(benchmarkManager.getCpuMonitoringMessage()); { - PlayerListHeaderAndFooterPacket playerListHeaderAndFooterPacket = new PlayerListHeaderAndFooterPacket(); - playerListHeaderAndFooterPacket.header = MinecraftServer.getSerializationManager().serialize(header); - playerListHeaderAndFooterPacket.footer = MinecraftServer.getSerializationManager().serialize(footer); + PlayerListHeaderAndFooterPacket playerListHeaderAndFooterPacket = new PlayerListHeaderAndFooterPacket(header, footer); PacketUtils.sendGroupedPacket(players, playerListHeaderAndFooterPacket); } From 4ef62a1017ed94c37890666c2b0727002d9573a9 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 11 Mar 2021 16:00:53 +0000 Subject: [PATCH 102/364] bump adventure to 4.7.0 --- build.gradle | 2 +- src/main/java/net/minestom/server/chat/JsonMessage.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d0c4cda3a..608ab545f 100644 --- a/build.gradle +++ b/build.gradle @@ -184,7 +184,7 @@ dependencies { generatorsImplementation("com.squareup:javapoet:1.13.0") // Adventure, for text messages - api platform("net.kyori:adventure-bom:4.6.0") + api platform("net.kyori:adventure-bom:4.7.0") api "net.kyori:adventure-api" api "net.kyori:adventure-text-serializer-gson" api "net.kyori:adventure-text-serializer-plain" diff --git a/src/main/java/net/minestom/server/chat/JsonMessage.java b/src/main/java/net/minestom/server/chat/JsonMessage.java index 0ccfabb66..af8f79262 100644 --- a/src/main/java/net/minestom/server/chat/JsonMessage.java +++ b/src/main/java/net/minestom/server/chat/JsonMessage.java @@ -55,7 +55,7 @@ public abstract class JsonMessage implements ComponentLike { @Override public @NotNull Component asComponent() { - return GsonComponentSerializer.gson().serializer().fromJson(this.getJsonObject(), Component.class); + return GsonComponentSerializer.gson().deserializeFromTree(this.getJsonObject()); } public static @NotNull JsonMessage fromComponent(@NotNull Component component) { From 6dd3eb804435b6ebebec66e1fd31aed07afe51a0 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 11 Mar 2021 17:07:04 +0000 Subject: [PATCH 103/364] Implement HoverSource for ItemStack, Entity and Player --- .../net/minestom/server/entity/Entity.java | 11 +++++++- .../net/minestom/server/entity/Player.java | 11 +++++++- .../net/minestom/server/item/ItemStack.java | 11 +++++++- .../net/minestom/server/utils/NBTUtils.java | 26 ++++++++++++++++--- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 1a751058d..1df638f25 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -2,6 +2,9 @@ package net.minestom.server.entity; import com.google.common.collect.Queues; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.event.HoverEvent.ShowEntity; +import net.kyori.adventure.text.event.HoverEventSource; import net.minestom.server.MinecraftServer; import net.minestom.server.Viewable; import net.minestom.server.chat.JsonMessage; @@ -47,13 +50,14 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.function.UnaryOperator; /** * Could be a player, a monster, or an object. *

* To create your own entity you probably want to extends {@link LivingEntity} or {@link EntityCreature} instead. */ -public class Entity implements Viewable, EventHandler, DataContainer, PermissionHandler { +public class Entity implements Viewable, EventHandler, DataContainer, PermissionHandler, HoverEventSource { private static final Map entityById = new ConcurrentHashMap<>(); private static final Map entityByUuid = new ConcurrentHashMap<>(); @@ -1528,6 +1532,11 @@ public class Entity implements Viewable, EventHandler, DataContainer, Permission this.customSynchronizationCooldown = cooldown; } + @Override + public @NotNull HoverEvent asHoverEvent(@NotNull UnaryOperator op) { + return HoverEvent.showEntity(ShowEntity.of(this.entityType, this.uuid)); + } + private UpdateOption getSynchronizationCooldown() { if (this.customSynchronizationCooldown != null) { return this.customSynchronizationCooldown; diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 71de8172e..09037085d 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -7,6 +7,9 @@ import net.kyori.adventure.identity.Identity; import net.kyori.adventure.inventory.Book; import net.kyori.adventure.sound.SoundStop; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.event.HoverEvent.ShowEntity; +import net.kyori.adventure.text.event.HoverEventSource; import net.kyori.adventure.title.Title; import net.minestom.server.MinecraftServer; import net.minestom.server.advancements.AdvancementTab; @@ -77,6 +80,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.UnaryOperator; /** * Those are the major actors of the server, @@ -84,7 +88,7 @@ import java.util.concurrent.atomic.AtomicInteger; *

* 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 { +public class Player extends LivingEntity implements CommandSender, Localizable, HoverEventSource { private long lastKeepAlive; private boolean answerKeepAlive; @@ -2536,6 +2540,11 @@ public class Player extends LivingEntity implements CommandSender, Localizable { return lastKeepAlive; } + @Override + public @NotNull HoverEvent asHoverEvent(@NotNull UnaryOperator op) { + return HoverEvent.showEntity(ShowEntity.of(EntityType.PLAYER, this.uuid, this.displayName)); + } + /** * Gets the packet to add the player from the tab-list. * diff --git a/src/main/java/net/minestom/server/item/ItemStack.java b/src/main/java/net/minestom/server/item/ItemStack.java index 937b571ee..7bde5b549 100644 --- a/src/main/java/net/minestom/server/item/ItemStack.java +++ b/src/main/java/net/minestom/server/item/ItemStack.java @@ -3,6 +3,9 @@ package net.minestom.server.item; import it.unimi.dsi.fastutil.objects.Object2ShortMap; import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.event.HoverEvent.ShowItem; +import net.kyori.adventure.text.event.HoverEventSource; import net.minestom.server.MinecraftServer; import net.minestom.server.chat.JsonMessage; import net.minestom.server.data.Data; @@ -27,6 +30,7 @@ import org.jetbrains.annotations.Nullable; import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.*; +import java.util.function.UnaryOperator; import java.util.stream.Collectors; // TODO should we cache a ByteBuf of this item for faster packet write @@ -40,7 +44,7 @@ import java.util.stream.Collectors; * Here a non-exhaustive list of what you can do to update the item: * {@link PlayerInventory#refreshSlot(short)}, {@link Inventory#refreshSlot(short)} or a raw {@link SetSlotPacket}. */ -public class ItemStack implements DataContainer, PublicCloneable { +public class ItemStack implements DataContainer, PublicCloneable, HoverEventSource { public static final OwnershipHandler DATA_OWNERSHIP = new OwnershipHandler<>(); public static final String OWNERSHIP_DATA_KEY = "ownership_identifier"; @@ -850,6 +854,11 @@ public class ItemStack implements DataContainer, PublicCloneable { throw new UnsupportedOperationException("Not implemented yet"); } + @Override + public @NotNull HoverEvent asHoverEvent(@NotNull UnaryOperator op) { + return HoverEvent.showItem(op.apply(ShowItem.of(this.material, this.amount, NBTUtils.asBinaryTagHolder(this.toNBT().getCompound("tag"))))); + } + // Callback events /** diff --git a/src/main/java/net/minestom/server/utils/NBTUtils.java b/src/main/java/net/minestom/server/utils/NBTUtils.java index d46e21736..af9f84657 100644 --- a/src/main/java/net/minestom/server/utils/NBTUtils.java +++ b/src/main/java/net/minestom/server/utils/NBTUtils.java @@ -1,13 +1,12 @@ package net.minestom.server.utils; +import net.kyori.adventure.nbt.api.BinaryTagHolder; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import net.kyori.adventure.util.Codec; import net.minestom.server.MinecraftServer; import net.minestom.server.attribute.Attribute; import net.minestom.server.attribute.AttributeOperation; -import net.minestom.server.chat.ChatParser; -import net.minestom.server.chat.ColoredText; -import net.minestom.server.chat.JsonMessage; import net.minestom.server.data.Data; import net.minestom.server.data.DataType; import net.minestom.server.inventory.Inventory; @@ -28,17 +27,36 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.io.StringReader; import java.util.*; // for lack of a better name public final class NBTUtils { - private final static Logger LOGGER = LoggerFactory.getLogger(NBTUtils.class); + /** + * An Adventure codec to convert between NBT and SNBT. + */ + public static final Codec SNBT_CODEC + = Codec.of(encoded -> new SNBTParser(new StringReader(encoded)).parse(), NBT::toSNBT); + private NBTUtils() { } + /** + * Turns an {@link NBTCompound} into an Adventure {@link BinaryTagHolder}. + * @param tag the tag, if any + * @return the binary tag holder, or {@code null} if the tag was null + */ + public static @Nullable BinaryTagHolder asBinaryTagHolder(@Nullable NBTCompound tag) { + if (tag == null) { + return null; + } + + return BinaryTagHolder.encode(tag, SNBT_CODEC); + } + /** * Loads all the items from the 'items' list into the given inventory * From 5ac5b0ba99d1e6d0e0ba3d81fb47463f72958848 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 12 Mar 2021 11:16:00 +0000 Subject: [PATCH 104/364] Finalise key in generated enums --- .../minestom/server/entity/EntityType.java | 96 ++----------------- .../net/minestom/server/fluids/Fluid.java | 2 +- .../minestom/server/instance/block/Block.java | 2 +- .../net/minestom/server/item/Enchantment.java | 2 +- .../net/minestom/server/item/Material.java | 2 +- .../minestom/server/particle/Particle.java | 2 +- .../minestom/server/potion/PotionEffect.java | 2 +- .../minestom/server/potion/PotionType.java | 2 +- .../java/net/minestom/server/sound/Sound.java | 2 +- .../minestom/server/stat/StatisticType.java | 2 +- .../minestom/codegen/BasicEnumGenerator.java | 2 +- .../net/minestom/codegen/EnumGenerator.java | 20 ++-- .../codegen/blocks/BlockEnumGenerator.java | 2 +- .../entitytypes/EntityTypeEnumGenerator.java | 3 +- .../codegen/items/ItemEnumGenerator.java | 2 +- 15 files changed, 36 insertions(+), 107 deletions(-) diff --git a/src/autogenerated/java/net/minestom/server/entity/EntityType.java b/src/autogenerated/java/net/minestom/server/entity/EntityType.java index c5cf9ccb8..0f7c68b77 100644 --- a/src/autogenerated/java/net/minestom/server/entity/EntityType.java +++ b/src/autogenerated/java/net/minestom/server/entity/EntityType.java @@ -1,31 +1,11 @@ package net.minestom.server.entity; -import java.util.function.BiFunction; import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Keyed; import net.minestom.server.entity.metadata.EntityMeta; import net.minestom.server.entity.metadata.PlayerMeta; import net.minestom.server.entity.metadata.ambient.BatMeta; -import net.minestom.server.entity.metadata.animal.BeeMeta; -import net.minestom.server.entity.metadata.animal.ChickenMeta; -import net.minestom.server.entity.metadata.animal.CowMeta; -import net.minestom.server.entity.metadata.animal.DonkeyMeta; -import net.minestom.server.entity.metadata.animal.FoxMeta; -import net.minestom.server.entity.metadata.animal.HoglinMeta; -import net.minestom.server.entity.metadata.animal.HorseMeta; -import net.minestom.server.entity.metadata.animal.LlamaMeta; -import net.minestom.server.entity.metadata.animal.MooshroomMeta; -import net.minestom.server.entity.metadata.animal.MuleMeta; -import net.minestom.server.entity.metadata.animal.OcelotMeta; -import net.minestom.server.entity.metadata.animal.PandaMeta; -import net.minestom.server.entity.metadata.animal.PigMeta; -import net.minestom.server.entity.metadata.animal.PolarBearMeta; -import net.minestom.server.entity.metadata.animal.RabbitMeta; -import net.minestom.server.entity.metadata.animal.SheepMeta; -import net.minestom.server.entity.metadata.animal.SkeletonHorseMeta; -import net.minestom.server.entity.metadata.animal.StriderMeta; -import net.minestom.server.entity.metadata.animal.TurtleMeta; -import net.minestom.server.entity.metadata.animal.ZombieHorseMeta; +import net.minestom.server.entity.metadata.animal.*; import net.minestom.server.entity.metadata.animal.tameable.CatMeta; import net.minestom.server.entity.metadata.animal.tameable.ParrotMeta; import net.minestom.server.entity.metadata.animal.tameable.WolfMeta; @@ -37,73 +17,15 @@ import net.minestom.server.entity.metadata.flying.PhantomMeta; import net.minestom.server.entity.metadata.golem.IronGolemMeta; import net.minestom.server.entity.metadata.golem.ShulkerMeta; import net.minestom.server.entity.metadata.golem.SnowGolemMeta; -import net.minestom.server.entity.metadata.item.EyeOfEnderMeta; -import net.minestom.server.entity.metadata.item.FireballMeta; -import net.minestom.server.entity.metadata.item.ItemEntityMeta; -import net.minestom.server.entity.metadata.item.SmallFireballMeta; -import net.minestom.server.entity.metadata.item.SnowballMeta; -import net.minestom.server.entity.metadata.item.ThrownEggMeta; -import net.minestom.server.entity.metadata.item.ThrownEnderPearlMeta; -import net.minestom.server.entity.metadata.item.ThrownExperienceBottleMeta; -import net.minestom.server.entity.metadata.item.ThrownPotionMeta; -import net.minestom.server.entity.metadata.minecart.ChestMinecartMeta; -import net.minestom.server.entity.metadata.minecart.CommandBlockMinecartMeta; -import net.minestom.server.entity.metadata.minecart.FurnaceMinecartMeta; -import net.minestom.server.entity.metadata.minecart.HopperMinecartMeta; -import net.minestom.server.entity.metadata.minecart.MinecartMeta; -import net.minestom.server.entity.metadata.minecart.SpawnerMinecartMeta; -import net.minestom.server.entity.metadata.minecart.TntMinecartMeta; -import net.minestom.server.entity.metadata.monster.BlazeMeta; -import net.minestom.server.entity.metadata.monster.CaveSpiderMeta; -import net.minestom.server.entity.metadata.monster.CreeperMeta; -import net.minestom.server.entity.metadata.monster.ElderGuardianMeta; -import net.minestom.server.entity.metadata.monster.EndermanMeta; -import net.minestom.server.entity.metadata.monster.EndermiteMeta; -import net.minestom.server.entity.metadata.monster.GiantMeta; -import net.minestom.server.entity.metadata.monster.GuardianMeta; -import net.minestom.server.entity.metadata.monster.PiglinBruteMeta; -import net.minestom.server.entity.metadata.monster.PiglinMeta; -import net.minestom.server.entity.metadata.monster.SilverfishMeta; -import net.minestom.server.entity.metadata.monster.SpiderMeta; -import net.minestom.server.entity.metadata.monster.VexMeta; -import net.minestom.server.entity.metadata.monster.WitherMeta; -import net.minestom.server.entity.metadata.monster.ZoglinMeta; -import net.minestom.server.entity.metadata.monster.raider.EvokerMeta; -import net.minestom.server.entity.metadata.monster.raider.IllusionerMeta; -import net.minestom.server.entity.metadata.monster.raider.PillagerMeta; -import net.minestom.server.entity.metadata.monster.raider.RavagerMeta; -import net.minestom.server.entity.metadata.monster.raider.VindicatorMeta; -import net.minestom.server.entity.metadata.monster.raider.WitchMeta; +import net.minestom.server.entity.metadata.item.*; +import net.minestom.server.entity.metadata.minecart.*; +import net.minestom.server.entity.metadata.monster.*; +import net.minestom.server.entity.metadata.monster.raider.*; import net.minestom.server.entity.metadata.monster.skeleton.SkeletonMeta; import net.minestom.server.entity.metadata.monster.skeleton.StrayMeta; import net.minestom.server.entity.metadata.monster.skeleton.WitherSkeletonMeta; -import net.minestom.server.entity.metadata.monster.zombie.DrownedMeta; -import net.minestom.server.entity.metadata.monster.zombie.HuskMeta; -import net.minestom.server.entity.metadata.monster.zombie.ZombieMeta; -import net.minestom.server.entity.metadata.monster.zombie.ZombieVillagerMeta; -import net.minestom.server.entity.metadata.monster.zombie.ZombifiedPiglinMeta; -import net.minestom.server.entity.metadata.other.AreaEffectCloudMeta; -import net.minestom.server.entity.metadata.other.ArmorStandMeta; -import net.minestom.server.entity.metadata.other.BoatMeta; -import net.minestom.server.entity.metadata.other.DragonFireballMeta; -import net.minestom.server.entity.metadata.other.EndCrystalMeta; -import net.minestom.server.entity.metadata.other.EnderDragonMeta; -import net.minestom.server.entity.metadata.other.EvokerFangsMeta; -import net.minestom.server.entity.metadata.other.ExperienceOrbMeta; -import net.minestom.server.entity.metadata.other.FallingBlockMeta; -import net.minestom.server.entity.metadata.other.FireworkRocketMeta; -import net.minestom.server.entity.metadata.other.FishingHookMeta; -import net.minestom.server.entity.metadata.other.ItemFrameMeta; -import net.minestom.server.entity.metadata.other.LeashKnotMeta; -import net.minestom.server.entity.metadata.other.LightningBoltMeta; -import net.minestom.server.entity.metadata.other.LlamaSpitMeta; -import net.minestom.server.entity.metadata.other.MagmaCubeMeta; -import net.minestom.server.entity.metadata.other.PaintingMeta; -import net.minestom.server.entity.metadata.other.PrimedTntMeta; -import net.minestom.server.entity.metadata.other.ShulkerBulletMeta; -import net.minestom.server.entity.metadata.other.SlimeMeta; -import net.minestom.server.entity.metadata.other.TraderLlamaMeta; -import net.minestom.server.entity.metadata.other.WitherSkullMeta; +import net.minestom.server.entity.metadata.monster.zombie.*; +import net.minestom.server.entity.metadata.other.*; import net.minestom.server.entity.metadata.villager.VillagerMeta; import net.minestom.server.entity.metadata.villager.WanderingTraderMeta; import net.minestom.server.entity.metadata.water.DolphinMeta; @@ -116,6 +38,8 @@ import net.minestom.server.registry.Registries; import net.minestom.server.utils.NamespaceID; import org.jetbrains.annotations.NotNull; +import java.util.function.BiFunction; + /** * //============================== * // AUTOGENERATED BY EnumGenerator @@ -354,7 +278,7 @@ public enum EntityType implements Keyed { @NotNull private final EntitySpawnType spawnType; - private Key key; + private final Key key; EntityType(@NotNull String namespaceID, double width, double height, @NotNull BiFunction metaConstructor, diff --git a/src/autogenerated/java/net/minestom/server/fluids/Fluid.java b/src/autogenerated/java/net/minestom/server/fluids/Fluid.java index 73d3b565d..7d73de35a 100644 --- a/src/autogenerated/java/net/minestom/server/fluids/Fluid.java +++ b/src/autogenerated/java/net/minestom/server/fluids/Fluid.java @@ -24,7 +24,7 @@ public enum Fluid implements Keyed { private final String namespaceID; - private Key key; + private final Key key; Fluid(String namespaceID) { this.namespaceID = namespaceID; diff --git a/src/autogenerated/java/net/minestom/server/instance/block/Block.java b/src/autogenerated/java/net/minestom/server/instance/block/Block.java index 337d1d1ad..c4788bac6 100644 --- a/src/autogenerated/java/net/minestom/server/instance/block/Block.java +++ b/src/autogenerated/java/net/minestom/server/instance/block/Block.java @@ -2501,7 +2501,7 @@ public enum Block implements Keyed { private List alternatives = new java.util.ArrayList<>(); - private Key key; + private final Key key; Block(@NotNull String namespaceID, short defaultID, double hardness, double resistance, boolean isAir, boolean isSolid, @Nullable NamespaceID blockEntity, diff --git a/src/autogenerated/java/net/minestom/server/item/Enchantment.java b/src/autogenerated/java/net/minestom/server/item/Enchantment.java index 6b3524707..faba57e8b 100644 --- a/src/autogenerated/java/net/minestom/server/item/Enchantment.java +++ b/src/autogenerated/java/net/minestom/server/item/Enchantment.java @@ -90,7 +90,7 @@ public enum Enchantment implements Keyed { private final String namespaceID; - private Key key; + private final Key key; Enchantment(String namespaceID) { this.namespaceID = namespaceID; diff --git a/src/autogenerated/java/net/minestom/server/item/Material.java b/src/autogenerated/java/net/minestom/server/item/Material.java index b6fdfb9a2..0a6f526ac 100644 --- a/src/autogenerated/java/net/minestom/server/item/Material.java +++ b/src/autogenerated/java/net/minestom/server/item/Material.java @@ -1975,7 +1975,7 @@ public enum Material implements Keyed { @Nullable private final Block correspondingBlock; - private Key key; + private final Key key; Material(@NotNull String namespaceID, int maxDefaultStackSize, @Nullable Block correspondingBlock) { diff --git a/src/autogenerated/java/net/minestom/server/particle/Particle.java b/src/autogenerated/java/net/minestom/server/particle/Particle.java index f62c5b600..378db4edc 100644 --- a/src/autogenerated/java/net/minestom/server/particle/Particle.java +++ b/src/autogenerated/java/net/minestom/server/particle/Particle.java @@ -158,7 +158,7 @@ public enum Particle implements Keyed { private final String namespaceID; - private Key key; + private final Key key; Particle(String namespaceID) { this.namespaceID = namespaceID; diff --git a/src/autogenerated/java/net/minestom/server/potion/PotionEffect.java b/src/autogenerated/java/net/minestom/server/potion/PotionEffect.java index 28af57370..826394ee1 100644 --- a/src/autogenerated/java/net/minestom/server/potion/PotionEffect.java +++ b/src/autogenerated/java/net/minestom/server/potion/PotionEffect.java @@ -78,7 +78,7 @@ public enum PotionEffect implements Keyed { private final String namespaceID; - private Key key; + private final Key key; PotionEffect(String namespaceID) { this.namespaceID = namespaceID; diff --git a/src/autogenerated/java/net/minestom/server/potion/PotionType.java b/src/autogenerated/java/net/minestom/server/potion/PotionType.java index 0c3a7a59f..375314472 100644 --- a/src/autogenerated/java/net/minestom/server/potion/PotionType.java +++ b/src/autogenerated/java/net/minestom/server/potion/PotionType.java @@ -100,7 +100,7 @@ public enum PotionType implements Keyed { private final String namespaceID; - private Key key; + private final Key key; PotionType(String namespaceID) { this.namespaceID = namespaceID; diff --git a/src/autogenerated/java/net/minestom/server/sound/Sound.java b/src/autogenerated/java/net/minestom/server/sound/Sound.java index af1f16e83..2394aff1a 100644 --- a/src/autogenerated/java/net/minestom/server/sound/Sound.java +++ b/src/autogenerated/java/net/minestom/server/sound/Sound.java @@ -1998,7 +1998,7 @@ public enum Sound implements Keyed { private final String namespaceID; - private Key key; + private final Key key; Sound(String namespaceID) { this.namespaceID = namespaceID; diff --git a/src/autogenerated/java/net/minestom/server/stat/StatisticType.java b/src/autogenerated/java/net/minestom/server/stat/StatisticType.java index f7ebd8836..fea4762ff 100644 --- a/src/autogenerated/java/net/minestom/server/stat/StatisticType.java +++ b/src/autogenerated/java/net/minestom/server/stat/StatisticType.java @@ -162,7 +162,7 @@ public enum StatisticType implements Keyed { private final String namespaceID; - private Key key; + private final Key key; StatisticType(String namespaceID) { this.namespaceID = namespaceID; diff --git a/src/generators/java/net/minestom/codegen/BasicEnumGenerator.java b/src/generators/java/net/minestom/codegen/BasicEnumGenerator.java index 6cbb94273..b51ae322a 100644 --- a/src/generators/java/net/minestom/codegen/BasicEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/BasicEnumGenerator.java @@ -121,7 +121,7 @@ public abstract class BasicEnumGenerator extends MinestomEnumGenerator code.addStatement("this.key = Key.key(this.namespaceID)")); generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key")); } diff --git a/src/generators/java/net/minestom/codegen/EnumGenerator.java b/src/generators/java/net/minestom/codegen/EnumGenerator.java index 6fd1479cb..c61f4d21d 100644 --- a/src/generators/java/net/minestom/codegen/EnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/EnumGenerator.java @@ -1,6 +1,7 @@ package net.minestom.codegen; import com.squareup.javapoet.*; +import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,7 +25,7 @@ public class EnumGenerator implements CodeGenerator { private List methods = new LinkedList<>(); private List staticFields = new LinkedList<>(); private List instances = new LinkedList<>(); - private List fields = new LinkedList<>(); + private List> fields = new LinkedList<>(); private List hardcodedFields = new LinkedList<>(); private List annotations = new LinkedList<>(); private String enumPackage; @@ -125,10 +126,15 @@ public class EnumGenerator implements CodeGenerator { } // normal fields - for (Field field : fields) { - enumClass.addField(FieldSpec.builder(field.type, field.name) - .addModifiers(Modifier.PRIVATE) - .build()); + for (Pair field : fields) { + FieldSpec.Builder builder = FieldSpec.builder(field.getLeft().type, field.getLeft().name) + .addModifiers(Modifier.PRIVATE); + + if (field.getRight()) { + builder.addModifiers(Modifier.FINAL); + } + + enumClass.addField(builder.build()); } // constructor @@ -182,8 +188,8 @@ public class EnumGenerator implements CodeGenerator { constructorEnds.add(constructorEnding); } - public void addField(TypeName type, String name) { - fields.add(new Field(type, name)); + public void addField(TypeName type, String name, boolean isFinal) { + fields.add(Pair.of(new Field(type, name), isFinal)); } public void addHardcodedField(TypeName type, String name, String value) { diff --git a/src/generators/java/net/minestom/codegen/blocks/BlockEnumGenerator.java b/src/generators/java/net/minestom/codegen/blocks/BlockEnumGenerator.java index 89437ae5f..168ea6611 100644 --- a/src/generators/java/net/minestom/codegen/blocks/BlockEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/blocks/BlockEnumGenerator.java @@ -303,7 +303,7 @@ public class BlockEnumGenerator extends MinestomEnumGenerator { // implement Keyed generator.addSuperinterface(ClassName.get(Keyed.class)); - generator.addField(ClassName.get(Key.class), "key"); + generator.addField(ClassName.get(Key.class), "key", true); generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)")); generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key")); } diff --git a/src/generators/java/net/minestom/codegen/entitytypes/EntityTypeEnumGenerator.java b/src/generators/java/net/minestom/codegen/entitytypes/EntityTypeEnumGenerator.java index 007975c49..741c2c7e6 100644 --- a/src/generators/java/net/minestom/codegen/entitytypes/EntityTypeEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/entitytypes/EntityTypeEnumGenerator.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.lang.model.element.TypeElement; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -183,7 +182,7 @@ public class EntityTypeEnumGenerator extends MinestomEnumGenerator code.addStatement("this.key = Key.key(this.namespaceID)")); generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key")); } diff --git a/src/generators/java/net/minestom/codegen/items/ItemEnumGenerator.java b/src/generators/java/net/minestom/codegen/items/ItemEnumGenerator.java index fba48697b..0c18693b2 100644 --- a/src/generators/java/net/minestom/codegen/items/ItemEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/items/ItemEnumGenerator.java @@ -223,7 +223,7 @@ public class ItemEnumGenerator extends MinestomEnumGenerator { // implement Keyed generator.addSuperinterface(ClassName.get(Keyed.class)); - generator.addField(ClassName.get(Key.class), "key"); + generator.addField(ClassName.get(Key.class), "key", true); generator.appendToConstructor(code -> code.addStatement("this.key = Key.key(this.namespaceID)")); generator.addMethod("key", new ParameterSpec[0], ClassName.get(Key.class), code -> code.addStatement("return this.key")); } From c40139349b7a1d182184d23ef4c25ce13bbfa2b0 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 12 Mar 2021 15:18:06 +0000 Subject: [PATCH 105/364] Add null check for Component in BinaryWriter --- .../java/net/minestom/server/utils/binary/BinaryWriter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java index 84a27d4ff..59c39d34f 100644 --- a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java +++ b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java @@ -16,6 +16,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTWriter; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.Objects; import java.util.UUID; import java.util.function.Consumer; @@ -68,7 +69,7 @@ public class BinaryWriter extends OutputStream { * @param component the component */ public void writeComponent(Component component) { - this.writeSizedString(MinecraftServer.getSerializationManager().serialize(component)); + this.writeSizedString(MinecraftServer.getSerializationManager().serialize(Objects.requireNonNullElseGet(component, Component::empty))); } /** From 52ce8027f249f521d53ad9cdafa89819440481d0 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 12 Mar 2021 15:33:19 +0000 Subject: [PATCH 106/364] Simplify component translation --- .../server/adventure/BossBarManager.java | 20 +-- .../server/adventure/ComponentHolder.java | 39 +++++ .../adventure/LocalizablePacketSender.java | 158 ------------------ .../adventure/SerializationManager.java | 45 ++++- .../net/minestom/server/entity/Player.java | 17 +- .../server/item/metadata/WrittenBookMeta.java | 4 +- .../server/network/ConnectionManager.java | 9 +- .../server/ComponentHoldingServerPacket.java | 8 + .../server/login/LoginDisconnectPacket.java | 18 +- .../server/play/AdvancementsPacket.java | 35 +++- .../packet/server/play/BossBarPacket.java | 40 ++++- .../packet/server/play/ChatMessagePacket.java | 16 +- .../packet/server/play/CombatEventPacket.java | 30 +++- .../packet/server/play/DisconnectPacket.java | 17 +- .../packet/server/play/MapDataPacket.java | 44 ++++- .../packet/server/play/PlayerInfoPacket.java | 85 +++++++++- .../play/PlayerListHeaderAndFooterPacket.java | 24 ++- .../play/ScoreboardObjectivePacket.java | 30 +++- .../packet/server/play/TabCompletePacket.java | 68 +++++++- .../packet/server/play/TeamsPacket.java | 37 +++- .../packet/server/play/TitlePacket.java | 23 ++- .../network/player/NettyPlayerConnection.java | 7 + .../minestom/server/utils/PacketUtils.java | 10 +- 23 files changed, 559 insertions(+), 225 deletions(-) create mode 100644 src/main/java/net/minestom/server/adventure/ComponentHolder.java delete mode 100644 src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java create mode 100644 src/main/java/net/minestom/server/network/packet/server/ComponentHoldingServerPacket.java diff --git a/src/main/java/net/minestom/server/adventure/BossBarManager.java b/src/main/java/net/minestom/server/adventure/BossBarManager.java index a1574b2bc..67e6db2a1 100644 --- a/src/main/java/net/minestom/server/adventure/BossBarManager.java +++ b/src/main/java/net/minestom/server/adventure/BossBarManager.java @@ -47,10 +47,7 @@ public class BossBarManager implements BossBar.Listener { Holder holder = this.getOrCreateHandler(bar); if (holder.players.add(player.getUuid())) { - BossBarPacket packet = holder.createAddPacket(); - packet.title = MinecraftServer.getSerializationManager().prepare(packet.title, player); - - player.getPlayerConnection().sendPacket(packet); + player.getPlayerConnection().sendPacket(holder.createAddPacket()); } } /** @@ -108,27 +105,12 @@ public class BossBarManager implements BossBar.Listener { private void updatePlayers(BossBarPacket packet, Set players) { Iterator iterator = players.iterator(); - // check if we need to translate the bossbar - Component rawTitle = packet.title; - boolean translate = false; - if (packet.action == UPDATE_TITLE || packet.action == ADD) { - Component rootTitle = MinecraftServer.getSerializationManager().prepare(rawTitle, MinecraftServer.getSerializationManager().getDefaultLocale()); - - if (!rawTitle.equals(rootTitle)) { - translate = true; - } - } - while (iterator.hasNext()) { Player player = MinecraftServer.getConnectionManager().getPlayer(iterator.next()); if (player == null) { iterator.remove(); } else { - if (translate) { - packet.title = MinecraftServer.getSerializationManager().prepare(rawTitle, player); - } - player.getPlayerConnection().sendPacket(packet); } } diff --git a/src/main/java/net/minestom/server/adventure/ComponentHolder.java b/src/main/java/net/minestom/server/adventure/ComponentHolder.java new file mode 100644 index 000000000..baa08da60 --- /dev/null +++ b/src/main/java/net/minestom/server/adventure/ComponentHolder.java @@ -0,0 +1,39 @@ +package net.minestom.server.adventure; + +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +/** + * Represents an object that holds some amount of components. + * @param the holding class + */ +public interface ComponentHolder { + + /** + * Gets the components held by this object. + * @return the components + */ + @NotNull Collection components(); + + /** + * Returns a copy of this object. For each component this object holds, the operator + * is applied to the copy before returning. + * @param operator the operator + * @return the copy + */ + @NotNull T copyWithOperator(@NotNull UnaryOperator operator); + + /** + * Visits each component held by this object. + * @param visitor the visitor + */ + default void visitComponents(@NotNull Consumer visitor) { + for (Component component : this.components()) { + visitor.accept(component); + } + } +} diff --git a/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java b/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java deleted file mode 100644 index 4abf4808a..000000000 --- a/src/main/java/net/minestom/server/adventure/LocalizablePacketSender.java +++ /dev/null @@ -1,158 +0,0 @@ -package net.minestom.server.adventure; - -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identity; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.title.Title; -import net.minestom.server.MinecraftServer; -import net.minestom.server.entity.Player; -import net.minestom.server.network.packet.server.play.ChatMessagePacket; -import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket; -import net.minestom.server.network.packet.server.play.TitlePacket; -import net.minestom.server.utils.PacketUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -/** - * Utility class for sending packets with translatable components. All functions in this - * class will send grouped packets if the components do not contain any translatable - * components. In the case that they do, the components are translated and send individually. - */ -public class LocalizablePacketSender { - - /** - * Sends a title to many players, sending it as a grouped packet if it does not - * contain translatable elements. - * - * @param players the players - * @param title the title - */ - public static void sendGroupedTitle(@NotNull Collection players, @NotNull Title title) { - Component preparedTitle = MinecraftServer.getSerializationManager().prepare(title.title(), MinecraftServer.getSerializationManager().getDefaultLocale()), - preparedSubtitle = MinecraftServer.getSerializationManager().prepare(title.subtitle(), MinecraftServer.getSerializationManager().getDefaultLocale()); - Collection rootPacket = TitlePacket.of(Title.title(preparedTitle, preparedSubtitle, title.times())); - - if (title.title().equals(preparedTitle) && title.subtitle().equals(preparedSubtitle)) { - for (TitlePacket packet : rootPacket) { - PacketUtils.sendGroupedPacket(players, packet); - } - } else { - for (Player player : players) { - Collection packets; - - if (player.getLocale() == null) { - packets = rootPacket; - } else { - packets = TitlePacket.of(Title.title(MinecraftServer.getSerializationManager().prepare(title.title(), player), - MinecraftServer.getSerializationManager().prepare(title.subtitle(), player), title.times())); - } - - for (TitlePacket packet : packets) { - player.getPlayerConnection().sendPacket(packet); - } - } - } - } - - /** - * Sends an action bar to many players, sending it as a grouped packet if it does not - * contain translatable elements. - * - * @param players the players - * @param component the component - */ - public static void sendGroupedActionBar(@NotNull Collection players, @NotNull Component component) { - Component preparedComponent = MinecraftServer.getSerializationManager().prepare(component, MinecraftServer.getSerializationManager().getDefaultLocale()); - TitlePacket rootPacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, preparedComponent); - - if (component.equals(preparedComponent)) { - PacketUtils.sendGroupedPacket(players, rootPacket); - } else { - for (Player player : players) { - TitlePacket packet; - - if (player.getLocale() == null) { - packet = rootPacket; - } else { - packet = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().prepare(component, player)); - } - - player.getPlayerConnection().sendPacket(packet); - } - } - } - - /** - * Sends a player list to many players, sending it as a grouped packet if it does not - * contain translatable elements. - * - * @param players the players - * @param header the header - * @param footer the footer - */ - public static void sendGroupedPlayerList(@NotNull Collection players, @Nullable Component header, @Nullable Component footer) { - // empty check first - if (header == null) { - header = Component.empty(); - } - - if (footer == null) { - footer = Component.empty(); - } - - // now back to the packets - Component preparedHeader = MinecraftServer.getSerializationManager().prepare(header, MinecraftServer.getSerializationManager().getDefaultLocale()), - preparedFooter = MinecraftServer.getSerializationManager().prepare(footer, MinecraftServer.getSerializationManager().getDefaultLocale()); - PlayerListHeaderAndFooterPacket rootPacket = new PlayerListHeaderAndFooterPacket(preparedHeader, preparedFooter); - - if (header.equals(preparedHeader) && footer.equals(preparedFooter)) { - PacketUtils.sendGroupedPacket(players, rootPacket); - } else { - for (Player player : players) { - PlayerListHeaderAndFooterPacket packet; - - if (player.getLocale() == null) { - packet = rootPacket; - } else { - packet = new PlayerListHeaderAndFooterPacket(MinecraftServer.getSerializationManager().prepare(header, player), - MinecraftServer.getSerializationManager().prepare(footer, player)); - } - - player.getPlayerConnection().sendPacket(packet); - } - } - } - - /** - * Sends a message to many players, sending it as a grouped packet if it does not - * contain translatable elements. - * - * @param players the players - * @param source the source of the message - * @param message the message - * @param messageType the type of the message - */ - public static void sendGroupedMessage(@NotNull Collection players, @NotNull Identity source, @NotNull Component message, @NotNull MessageType messageType) { - ChatMessagePacket.Position position = ChatMessagePacket.Position.fromMessageType(messageType); - Component preparedMessage = MinecraftServer.getSerializationManager().prepare(message, MinecraftServer.getSerializationManager().getDefaultLocale()); - ChatMessagePacket rootPacket = new ChatMessagePacket(preparedMessage, position, source.uuid()); - - if (message.equals(preparedMessage)) { - PacketUtils.sendGroupedPacket(players, rootPacket); - } else { - for (Player player : players) { - ChatMessagePacket packet; - - if (player.getLocale() == null) { - packet = rootPacket; - } else { - packet = new ChatMessagePacket(MinecraftServer.getSerializationManager().prepare(message, player), position, source.uuid()); - } - - player.getPlayerConnection().sendPacket(packet); - } - } - } -} diff --git a/src/main/java/net/minestom/server/adventure/SerializationManager.java b/src/main/java/net/minestom/server/adventure/SerializationManager.java index e171cda88..ad2f71f6f 100644 --- a/src/main/java/net/minestom/server/adventure/SerializationManager.java +++ b/src/main/java/net/minestom/server/adventure/SerializationManager.java @@ -8,6 +8,7 @@ import net.kyori.adventure.translation.TranslationRegistry; import net.kyori.adventure.translation.Translator; import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.Locale; import java.util.Objects; import java.util.function.Function; @@ -51,7 +52,7 @@ public class SerializationManager { /** * Gets the default locale used to translate {@link TranslatableComponent} if, when - * {@link #prepare(Component, Localizable)} is called with a localizable that + * {@link #translate(Component, Localizable)} is called with a localizable that * does not have a locale. * * @return the default locale @@ -62,7 +63,7 @@ public class SerializationManager { /** * Sets the default locale used to translate {@link TranslatableComponent} if, when - * {@link #prepare(Component, Localizable)} is called with a localizable that + * {@link #translate(Component, Localizable)} is called with a localizable that * does not have a locale. * * @param defaultLocale the new default locale @@ -90,7 +91,7 @@ public class SerializationManager { * * @return the prepared component */ - public @NotNull Component prepare(@NotNull Component component, @NotNull Localizable localizable) { + public @NotNull Component translate(@NotNull Component component, @NotNull Localizable localizable) { return GlobalTranslator.renderer().render(component, Objects.requireNonNullElse(localizable.getLocale(), this.getDefaultLocale())); } @@ -103,7 +104,7 @@ public class SerializationManager { * * @return the prepared component */ - public @NotNull Component prepare(@NotNull Component component, @NotNull Locale locale) { + public @NotNull Component translate(@NotNull Component component, @NotNull Locale locale) { return GlobalTranslator.renderer().render(component, locale); } @@ -126,8 +127,8 @@ public class SerializationManager { * * @return the string */ - public String prepareAndSerialize(@NotNull Component component, @NotNull Localizable localizable) { - return this.prepareAndSerialize(component, Objects.requireNonNullElse(localizable.getLocale(), this.getDefaultLocale())); + public String translateAndSerialize(@NotNull Component component, @NotNull Localizable localizable) { + return this.translateAndSerialize(component, Objects.requireNonNullElse(localizable.getLocale(), this.getDefaultLocale())); } /** @@ -138,7 +139,35 @@ public class SerializationManager { * * @return the string */ - public String prepareAndSerialize(@NotNull Component component, @NotNull Locale locale) { - return this.serialize(this.prepare(component, locale)); + public String translateAndSerialize(@NotNull Component component, @NotNull Locale locale) { + return this.serialize(this.translate(component, locale)); + } + + /** + * Checks if a component can be translated server-side. This is done by running the + * component through the translator and seeing if the translated component is equal + * to the non translated component. + * @param component the component + * @return {@code true} if the component can be translated server-side, + * {@code false} otherwise + */ + public boolean isTranslatable(@NotNull Component component) { + return !component.equals(this.translate(component, this.getDefaultLocale())); + } + + /** + * Checks if any of a series of components are translatable server-side. + * @param components the components + * @return {@code true} if any of the components can be translated server-side, + * {@code false} otherwise + */ + public boolean areAnyTranslatable(@NotNull Collection components) { + for (Component component : components) { + if (this.isTranslatable(component)) { + return true; + } + } + + return false; } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 09037085d..1340c6fb0 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -508,7 +508,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // #buildDeathScreenText can return null, check here if (deathText != null) { - CombatEventPacket deathPacket = CombatEventPacket.death(this, null, MinecraftServer.getSerializationManager().prepare(deathText, this)); + CombatEventPacket deathPacket = CombatEventPacket.death(this, null, deathText); playerConnection.sendPacket(deathPacket); } @@ -806,7 +806,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public void sendMessage(@NotNull Identity source, @NotNull Component message, @NotNull MessageType type) { - ChatMessagePacket chatMessagePacket = new ChatMessagePacket(MinecraftServer.getSerializationManager().prepare(message, this), ChatMessagePacket.Position.fromMessageType(type), source.uuid()); + ChatMessagePacket chatMessagePacket = new ChatMessagePacket(message, ChatMessagePacket.Position.fromMessageType(type), source.uuid()); playerConnection.sendPacket(chatMessagePacket); } @@ -1009,8 +1009,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public void sendPlayerListHeaderAndFooter(@NotNull Component header, @NotNull Component footer) { - PlayerListHeaderAndFooterPacket packet - = new PlayerListHeaderAndFooterPacket(MinecraftServer.getSerializationManager().prepare(header, this), MinecraftServer.getSerializationManager().prepare(footer, this)); + PlayerListHeaderAndFooterPacket packet = new PlayerListHeaderAndFooterPacket(header, footer); playerConnection.sendPacket(packet); } @@ -1084,9 +1083,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public void showTitle(@NotNull Title title) { - Component preparedTitle = MinecraftServer.getSerializationManager().prepare(title.title(), this), - preparedSubtitle = MinecraftServer.getSerializationManager().prepare(title.subtitle(), this); - Collection packet = TitlePacket.of(Title.title(preparedTitle, preparedSubtitle, title.times())); + Collection packet = TitlePacket.of(Title.title(title.title(), title.subtitle(), title.times())); for (TitlePacket titlePacket : packet) { playerConnection.sendPacket(titlePacket); @@ -1095,7 +1092,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public void sendActionBar(@NotNull Component message) { - TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, MinecraftServer.getSerializationManager().prepare(message, this)); + TitlePacket titlePacket = new TitlePacket(TitlePacket.Action.SET_ACTION_BAR, message); playerConnection.sendPacket(titlePacket); } @@ -1924,9 +1921,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // Packet type depends on the current player connection state final ServerPacket disconnectPacket; if (connectionState == ConnectionState.LOGIN) { - disconnectPacket = new LoginDisconnectPacket(MinecraftServer.getSerializationManager().prepare(component, this)); + disconnectPacket = new LoginDisconnectPacket(component); } else { - disconnectPacket = new DisconnectPacket(MinecraftServer.getSerializationManager().prepare(component, this)); + disconnectPacket = new DisconnectPacket(component); } if (playerConnection instanceof NettyPlayerConnection) { diff --git a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java index b160bd59d..1d339fc76 100644 --- a/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java +++ b/src/main/java/net/minestom/server/item/metadata/WrittenBookMeta.java @@ -209,8 +209,8 @@ public class WrittenBookMeta extends ItemMeta { WrittenBookMeta meta = new WrittenBookMeta(); meta.resolved = false; meta.generation = WrittenBookGeneration.ORIGINAL; - meta.author = MinecraftServer.getSerializationManager().prepareAndSerialize(book.author(), localizable); - meta.title = MinecraftServer.getSerializationManager().prepareAndSerialize(book.title(), localizable); + meta.author = MinecraftServer.getSerializationManager().translateAndSerialize(book.author(), localizable); + meta.title = MinecraftServer.getSerializationManager().translateAndSerialize(book.title(), localizable); meta.pages = new ArrayList<>(); for (Component page : book.pages()) { diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java index 6c715fd37..1201b885a 100644 --- a/src/main/java/net/minestom/server/network/ConnectionManager.java +++ b/src/main/java/net/minestom/server/network/ConnectionManager.java @@ -3,12 +3,9 @@ package net.minestom.server.network; import io.netty.channel.Channel; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.ForwardingAudience; -import net.kyori.adventure.audience.MessageType; -import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.MinecraftServer; -import net.minestom.server.adventure.LocalizablePacketSender; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Player; import net.minestom.server.entity.fakeplayer.FakePlayer; @@ -155,8 +152,9 @@ public final class ConnectionManager implements ForwardingAudience { final Collection recipients = getRecipients(condition); if (!recipients.isEmpty()) { - final String jsonText = jsonMessage.toString(); - LocalizablePacketSender.sendGroupedMessage(recipients, Identity.nil(), jsonMessage.asComponent(), MessageType.CHAT); + for (Player recipient : recipients) { + recipient.sendMessage(jsonMessage); + } } } @@ -489,7 +487,6 @@ public final class ConnectionManager implements ForwardingAudience { for (Player player : getOnlinePlayers()) { final PlayerConnection playerConnection = player.getPlayerConnection(); if (playerConnection instanceof NettyPlayerConnection) { - disconnectPacket.message = MinecraftServer.getSerializationManager().prepare(disconnectPacket.message, player); final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) playerConnection; final Channel channel = nettyPlayerConnection.getChannel(); channel.writeAndFlush(disconnectPacket); diff --git a/src/main/java/net/minestom/server/network/packet/server/ComponentHoldingServerPacket.java b/src/main/java/net/minestom/server/network/packet/server/ComponentHoldingServerPacket.java new file mode 100644 index 000000000..7c2d96ff0 --- /dev/null +++ b/src/main/java/net/minestom/server/network/packet/server/ComponentHoldingServerPacket.java @@ -0,0 +1,8 @@ +package net.minestom.server.network.packet.server; + +import net.minestom.server.adventure.ComponentHolder; + +/** + * A server packet that can hold components. + */ +public interface ComponentHoldingServerPacket extends ServerPacket, ComponentHolder { } diff --git a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java index 06e12369e..ff30b48e1 100644 --- a/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/login/LoginDisconnectPacket.java @@ -2,14 +2,17 @@ package net.minestom.server.network.packet.server.login; 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.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; -public class LoginDisconnectPacket implements ServerPacket { +import java.util.Collection; +import java.util.List; +import java.util.function.UnaryOperator; - private final Component kickMessage; // JSON text +public class LoginDisconnectPacket implements ComponentHoldingServerPacket { + public Component kickMessage; public LoginDisconnectPacket(@NotNull Component kickMessage) { this.kickMessage = kickMessage; @@ -33,4 +36,13 @@ public class LoginDisconnectPacket implements ServerPacket { return ServerPacketIdentifier.LOGIN_DISCONNECT; } + @Override + public @NotNull Collection components() { + return List.of(this.kickMessage); + } + + @Override + public @NotNull LoginDisconnectPacket copyWithOperator(@NotNull UnaryOperator operator) { + return new LoginDisconnectPacket(operator.apply(this.kickMessage)); + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java index f6f8f963c..eebc1d9c8 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/AdvancementsPacket.java @@ -3,13 +3,20 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; import net.minestom.server.advancements.FrameType; import net.minestom.server.item.ItemStack; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.binary.Writeable; import org.jetbrains.annotations.NotNull; -public class AdvancementsPacket implements ServerPacket { +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.UnaryOperator; + +public class AdvancementsPacket implements ComponentHoldingServerPacket { public boolean resetAdvancements; public AdvancementMapping[] advancementMappings; @@ -37,6 +44,32 @@ public class AdvancementsPacket implements ServerPacket { return ServerPacketIdentifier.ADVANCEMENTS; } + @Override + public @NotNull Collection components() { + List components = new ArrayList<>(); + for (AdvancementMapping advancementMapping : advancementMappings) { + components.add(advancementMapping.value.displayData.title); + components.add(advancementMapping.value.displayData.description); + } + return components; + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + AdvancementsPacket packet = new AdvancementsPacket(); + packet.resetAdvancements = this.resetAdvancements; + packet.advancementMappings = Arrays.copyOf(this.advancementMappings, this.advancementMappings.length); + packet.identifiersToRemove = Arrays.copyOf(this.identifiersToRemove, this.identifiersToRemove.length); + packet.progressMappings = Arrays.copyOf(this.progressMappings, this.progressMappings.length); + + for (AdvancementMapping advancementMapping : packet.advancementMappings) { + advancementMapping.value.displayData.title = operator.apply(advancementMapping.value.displayData.title); + advancementMapping.value.displayData.description = operator.apply(advancementMapping.value.displayData.title); + } + + return packet; + } + /** * AdvancementMapping maps the namespaced ID to the Advancement. */ diff --git a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java index 47e4d266a..12699e8a2 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/BossBarPacket.java @@ -3,14 +3,18 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.text.Component; import net.minestom.server.adventure.AdventurePacketConvertor; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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 java.util.Collection; +import java.util.Collections; import java.util.UUID; +import java.util.function.UnaryOperator; -public class BossBarPacket implements ServerPacket { +public class BossBarPacket implements ComponentHoldingServerPacket { public UUID uuid; public Action action; @@ -58,6 +62,40 @@ public class BossBarPacket implements ServerPacket { return ServerPacketIdentifier.BOSS_BAR; } + @Override + public @NotNull Collection components() { + if (title != null) { + return Collections.singleton(title); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + switch (action) { + case UPDATE_TITLE: { + BossBarPacket packet = new BossBarPacket(); + packet.action = action; + packet.uuid = uuid; + packet.title = operator.apply(title); + return packet; + } + case ADD: { + BossBarPacket packet = new BossBarPacket(); + packet.action = action; + packet.uuid = uuid; + packet.title = operator.apply(title); + packet.health = health; + packet.overlay = overlay; + packet.color = color; + packet.flags = flags; + return packet; + } + default: return this; + } + } + public enum Action { ADD, REMOVE, 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 6eb2e559f..8e96bccb7 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 @@ -2,18 +2,22 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.text.Component; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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.Collection; +import java.util.Collections; import java.util.UUID; +import java.util.function.UnaryOperator; /** * Represents an outgoing chat message packet. */ -public class ChatMessagePacket implements ServerPacket { +public class ChatMessagePacket implements ComponentHoldingServerPacket { private static final UUID NULL_UUID = new UUID(0, 0); public Component message; @@ -42,6 +46,16 @@ public class ChatMessagePacket implements ServerPacket { return ServerPacketIdentifier.CHAT_MESSAGE; } + @Override + public @NotNull Collection components() { + return Collections.singleton(message); + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + return new ChatMessagePacket(operator.apply(message), position, uuid); + } + public enum Position { CHAT(MessageType.CHAT), SYSTEM_MESSAGE(MessageType.SYSTEM), diff --git a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java index fb54ad4da..01734bb68 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/CombatEventPacket.java @@ -4,16 +4,21 @@ import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.Entity; import net.minestom.server.entity.Player; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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 java.util.Collection; +import java.util.Collections; +import java.util.function.UnaryOperator; + /** * Packet sent during combat to a {@link Player}. * Only death is supported for the moment (other events are ignored anyway as of 1.15.2) */ -public class CombatEventPacket implements ServerPacket { +public class CombatEventPacket implements ComponentHoldingServerPacket { private EventType type; private int duration; @@ -81,6 +86,29 @@ public class CombatEventPacket implements ServerPacket { return ServerPacketIdentifier.COMBAT_EVENT; } + @Override + public @NotNull Collection components() { + if (this.type == EventType.DEATH) { + return Collections.singleton(deathMessage); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (this.type == EventType.DEATH) { + CombatEventPacket packet = new CombatEventPacket(); + packet.type = type; + packet.playerID = playerID; + packet.opponent = opponent; + packet.deathMessage = deathMessage; + return packet; + } else { + return this; + } + } + public enum EventType { ENTER_COMBAT, END_COMBAT, // both ignored by Notchian client DEATH, 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 4426e16ea..d321a879d 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 @@ -2,12 +2,17 @@ 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.ComponentHoldingServerPacket; 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; -public class DisconnectPacket implements ServerPacket { +import java.util.Collection; +import java.util.Collections; +import java.util.function.UnaryOperator; + +public class DisconnectPacket implements ComponentHoldingServerPacket { public Component message; /** @@ -35,4 +40,14 @@ public class DisconnectPacket implements ServerPacket { public int getId() { return ServerPacketIdentifier.DISCONNECT; } + + @Override + public @NotNull Collection components() { + return Collections.singleton(message); + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + return new DisconnectPacket(operator.apply(message)); + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java index 446b96d34..2a7ad74ae 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/MapDataPacket.java @@ -1,12 +1,16 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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; -public class MapDataPacket implements ServerPacket { +import java.util.*; +import java.util.function.UnaryOperator; + +public class MapDataPacket implements ComponentHoldingServerPacket { public int mapId; public byte scale; @@ -59,6 +63,44 @@ public class MapDataPacket implements ServerPacket { return ServerPacketIdentifier.MAP_DATA; } + @Override + public @NotNull Collection components() { + if (icons == null || icons.length == 0) { + return Collections.emptyList(); + } else { + List components = new ArrayList<>(); + for (Icon icon : icons) { + components.add(icon.displayName); + } + return components; + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (this.icons == null || this.icons.length == 0) { + return this; + } else { + MapDataPacket packet = new MapDataPacket(); + packet.mapId = this.mapId; + packet.scale = this.scale; + packet.trackingPosition = this.trackingPosition; + packet.locked = this.locked; + packet.columns = this.columns; + packet.rows = this.rows; + packet.x = this.x; + packet.z = this.z; + packet.data = this.data; + + packet.icons = Arrays.copyOf(this.icons, this.icons.length); + for (Icon icon : packet.icons) { + icon.displayName = operator.apply(icon.displayName); + } + + return packet; + } + } + public static class Icon { public int type; public byte x, z; diff --git a/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java index 4d0a9b0ec..a28500fe3 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/PlayerInfoPacket.java @@ -1,18 +1,19 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.adventure.ComponentHolder; import net.minestom.server.chat.JsonMessage; import net.minestom.server.entity.GameMode; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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 java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.function.UnaryOperator; -public class PlayerInfoPacket implements ServerPacket { +public class PlayerInfoPacket implements ComponentHoldingServerPacket { public Action action; public List playerInfos; @@ -39,6 +40,40 @@ public class PlayerInfoPacket implements ServerPacket { return ServerPacketIdentifier.PLAYER_INFO; } + @Override + public @NotNull Collection components() { + switch (this.action) { + case ADD_PLAYER: + case UPDATE_DISPLAY_NAME: + List components = new ArrayList<>(); + for (PlayerInfo playerInfo : playerInfos) { + if (playerInfo instanceof ComponentHolder) { + components.addAll(((ComponentHolder) playerInfo).components()); + } + } + return components; + default: return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + switch (this.action) { + case ADD_PLAYER: + case UPDATE_DISPLAY_NAME: + PlayerInfoPacket packet = new PlayerInfoPacket(action); + packet.playerInfos = new ArrayList<>(playerInfos.size()); + for (PlayerInfo playerInfo : playerInfos) { + if (playerInfo instanceof ComponentHolder) { + playerInfos.add(((ComponentHolder) playerInfo).copyWithOperator(operator)); + } else { + playerInfos.add(playerInfo); + } + } + default: return this; + } + } + public enum Action { ADD_PLAYER(AddPlayer.class), @@ -70,7 +105,7 @@ public class PlayerInfoPacket implements ServerPacket { public abstract void write(BinaryWriter writer); } - public static class AddPlayer extends PlayerInfo { + public static class AddPlayer extends PlayerInfo implements ComponentHolder { public String name; public List properties; @@ -102,6 +137,26 @@ public class PlayerInfoPacket implements ServerPacket { writer.writeComponent(displayName); } + @Override + public @NotNull Collection components() { + if (displayName == null) { + return Collections.emptyList(); + } else { + return Collections.singleton(displayName); + } + } + + @Override + public @NotNull AddPlayer copyWithOperator(@NotNull UnaryOperator operator) { + if (displayName == null) { + return this; + } else { + AddPlayer addPlayer = new AddPlayer(uuid, name, gameMode, ping); + addPlayer.displayName = operator.apply(displayName); + return addPlayer; + } + } + public static class Property { public String name; @@ -160,7 +215,7 @@ public class PlayerInfoPacket implements ServerPacket { } } - public static class UpdateDisplayName extends PlayerInfo { + public static class UpdateDisplayName extends PlayerInfo implements ComponentHolder { public Component displayName; @@ -184,6 +239,24 @@ public class PlayerInfoPacket implements ServerPacket { if (hasDisplayName) writer.writeComponent(displayName); } + + @Override + public @NotNull Collection components() { + if (displayName == null) { + return Collections.emptyList(); + } else { + return Collections.singleton(displayName); + } + } + + @Override + public @NotNull UpdateDisplayName copyWithOperator(@NotNull UnaryOperator operator) { + if (displayName == null) { + return this; + } else { + return new UpdateDisplayName(uuid, operator.apply(displayName)); + } + } } public static class RemovePlayer extends PlayerInfo { 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 ae86538a1..6f6ec312b 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,15 +1,20 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Objects; +import java.util.function.UnaryOperator; -public class PlayerListHeaderAndFooterPacket implements ServerPacket { +public class PlayerListHeaderAndFooterPacket implements ComponentHoldingServerPacket { public Component header; public Component footer; @@ -28,4 +33,21 @@ public class PlayerListHeaderAndFooterPacket implements ServerPacket { public int getId() { return ServerPacketIdentifier.PLAYER_LIST_HEADER_AND_FOOTER; } + + @Override + public @NotNull Collection components() { + List components = new ArrayList<>(); + if (header != null) { + components.add(header); + } + if (footer != null) { + components.add(footer); + } + return components; + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + return new PlayerListHeaderAndFooterPacket(header == null ? null : operator.apply(header), footer == null ? null : operator.apply(footer)); + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java index 756e475ab..ac8bd077d 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/ScoreboardObjectivePacket.java @@ -1,12 +1,17 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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; -public class ScoreboardObjectivePacket implements ServerPacket { +import java.util.Collection; +import java.util.Collections; +import java.util.function.UnaryOperator; + +public class ScoreboardObjectivePacket implements ComponentHoldingServerPacket { /** * An unique name for the objective @@ -43,6 +48,29 @@ public class ScoreboardObjectivePacket implements ServerPacket { return ServerPacketIdentifier.SCOREBOARD_OBJECTIVE; } + @Override + public @NotNull Collection components() { + if (mode == 0 || mode == 2) { + return Collections.singleton(objectiveValue); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (mode == 0 || mode == 2) { + ScoreboardObjectivePacket packet = new ScoreboardObjectivePacket(); + packet.objectiveName = objectiveName; + packet.mode = mode; + packet.objectiveValue = operator.apply(objectiveValue); + packet.type = type; + return packet; + } else { + return this; + } + } + /** * This enumeration represents all available types for the scoreboard objective */ diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java index e78b1dadd..3d4df77b9 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java @@ -1,12 +1,20 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; +import net.minestom.server.adventure.ComponentHolder; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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; -public class TabCompletePacket implements ServerPacket { +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.UnaryOperator; + +public class TabCompletePacket implements ComponentHoldingServerPacket { public int transactionId; public int start; @@ -33,10 +41,66 @@ public class TabCompletePacket implements ServerPacket { return ServerPacketIdentifier.TAB_COMPLETE; } - public static class Match { + @Override + public @NotNull Collection components() { + if (matches == null || matches.length == 0) { + return Collections.emptyList(); + } else { + List components = new ArrayList<>(matches.length); + for (Match match : matches) { + if (match.hasTooltip) { + components.add(match.tooltip); + } + } + return components; + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (matches == null || matches.length == 0) { + return this; + } else { + TabCompletePacket packet = new TabCompletePacket(); + packet.transactionId = transactionId; + packet.start = start; + packet.length = length; + packet.matches = new Match[matches.length]; + + for (int i = 0; i < matches.length; i++) { + packet.matches[i] = matches[i].copyWithOperator(operator); + } + + return packet; + } + } + + public static class Match implements ComponentHolder { public String match; public boolean hasTooltip; public Component tooltip; + + @Override + public @NotNull Collection components() { + if (hasTooltip) { + return Collections.singleton(tooltip); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull Match copyWithOperator(@NotNull UnaryOperator operator) { + if (hasTooltip) { + Match newMatch = new Match(); + newMatch.match = match; + newMatch.hasTooltip = hasTooltip; + newMatch.tooltip = tooltip; + return newMatch; + } else { + return this; + } + } } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java index af698abb1..793a93372 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/TeamsPacket.java @@ -2,15 +2,21 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; import net.minestom.server.color.TeamColor; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; 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 java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.UnaryOperator; + /** * The packet creates or updates teams */ -public class TeamsPacket implements ServerPacket { +public class TeamsPacket implements ComponentHoldingServerPacket { /** * The registry name of the team @@ -100,6 +106,35 @@ public class TeamsPacket implements ServerPacket { return ServerPacketIdentifier.TEAMS; } + @Override + public @NotNull Collection components() { + if (this.action == Action.UPDATE_TEAM_INFO || this.action == Action.CREATE_TEAM) { + return List.of(teamDisplayName, teamPrefix, teamSuffix); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (this.action == Action.UPDATE_TEAM_INFO || this.action == Action.CREATE_TEAM) { + TeamsPacket packet = new TeamsPacket(); + packet.teamName = teamName; + packet.action = action; + packet.teamDisplayName = teamDisplayName == null ? null : operator.apply(teamDisplayName); + packet.friendlyFlags = friendlyFlags; + packet.nameTagVisibility = nameTagVisibility; + packet.collisionRule = collisionRule; + packet.teamColor = teamColor; + packet.teamPrefix = teamPrefix == null ? null : operator.apply(teamPrefix); + packet.teamSuffix = teamSuffix == null ? null : operator.apply(teamSuffix); + packet.entities = entities; + return packet; + } else { + return this; + } + } + /** * An enumeration which representing all actions for the packet */ 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 675ec785b..7820e4adb 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 @@ -2,6 +2,7 @@ package net.minestom.server.network.packet.server.play; import net.kyori.adventure.text.Component; import net.kyori.adventure.title.Title; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.utils.TickUtils; @@ -11,11 +12,13 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.function.UnaryOperator; import static net.minestom.server.network.packet.server.play.TitlePacket.Action.*; -public class TitlePacket implements ServerPacket { +public class TitlePacket implements ComponentHoldingServerPacket { public Action action; @@ -90,6 +93,24 @@ public class TitlePacket implements ServerPacket { return ServerPacketIdentifier.TITLE; } + @Override + public @NotNull Collection components() { + if (action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR) { + return Collections.singleton(payload); + } else { + return Collections.emptyList(); + } + } + + @Override + public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator operator) { + if (action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR) { + return new TitlePacket(action, operator.apply(payload)); + } else { + return this; + } + } + public enum Action { SET_TITLE, SET_SUBTITLE, diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 2e62cc817..c5b76d49c 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -13,6 +13,7 @@ import net.minestom.server.network.ConnectionState; import net.minestom.server.network.netty.NettyServer; import net.minestom.server.network.netty.codec.PacketCompressor; import net.minestom.server.network.netty.packet.FramedPacket; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.login.SetCompressionPacket; import net.minestom.server.utils.BufUtils; @@ -25,6 +26,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.crypto.SecretKey; +import java.awt.Component; import java.net.SocketAddress; import java.util.Map; import java.util.UUID; @@ -180,6 +182,11 @@ public class NettyPlayerConnection extends PlayerConnection { return; } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; + + if (getPlayer() != null && serverPacket instanceof ComponentHoldingServerPacket) { + serverPacket = ((ComponentHoldingServerPacket) serverPacket).copyWithOperator(component -> MinecraftServer.getSerializationManager().translate(component, getPlayer())); + } + synchronized (tickBuffer) { PacketUtils.writeFramedPacket(tickBuffer, serverPacket, false); } diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index bdf4a9612..0108ecbd7 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -6,6 +6,7 @@ import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.network.netty.packet.FramedPacket; +import net.minestom.server.network.packet.server.ComponentHoldingServerPacket; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.player.NettyPlayerConnection; import net.minestom.server.network.player.PlayerConnection; @@ -45,7 +46,14 @@ public final class PacketUtils { if (players.isEmpty()) return; - if (MinecraftServer.hasGroupedPacket()) { + // work out if the packet needs to be sent individually due to server-side translating + boolean needsTranslating = false; + + if (packet instanceof ComponentHoldingServerPacket) { + needsTranslating = MinecraftServer.getSerializationManager().areAnyTranslatable(((ComponentHoldingServerPacket) packet).components()); + } + + if (MinecraftServer.hasGroupedPacket() && !needsTranslating) { // Send grouped packet... final boolean success = PACKET_LISTENER_MANAGER.processServerPacket(packet, players); if (success) { From 931d790702700262ea2ee4d314c2bff6ce4f7b82 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 12 Mar 2021 15:36:22 +0000 Subject: [PATCH 107/364] Make player implement Identified --- src/main/java/net/minestom/server/entity/Player.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 1340c6fb0..f36d207d5 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -3,6 +3,7 @@ package net.minestom.server.entity; import com.google.common.collect.Queues; import net.kyori.adventure.audience.MessageType; import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.identity.Identified; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.inventory.Book; import net.kyori.adventure.sound.SoundStop; @@ -72,6 +73,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; @@ -88,7 +90,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 { +public class Player extends LivingEntity implements CommandSender, Localizable, HoverEventSource, Identified { private long lastKeepAlive; private boolean answerKeepAlive; @@ -2697,6 +2699,11 @@ public class Player extends LivingEntity implements CommandSender, Localizable, settings.locale = locale == null ? null : locale.toLanguageTag(); } + @Override + public @NonNull Identity identity() { + return Identity.identity(this.uuid); + } + /** * Represents the main or off hand of the player. */ From f7b683c343e656aa852054a98b9ac943ddb0c2b7 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 12 Mar 2021 15:51:48 +0000 Subject: [PATCH 108/364] Add ability to skip packet translating --- .../network/player/FakePlayerConnection.java | 2 +- .../network/player/NettyPlayerConnection.java | 12 ++++++++---- .../server/network/player/PlayerConnection.java | 14 +++++++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/minestom/server/network/player/FakePlayerConnection.java b/src/main/java/net/minestom/server/network/player/FakePlayerConnection.java index 74a4a4a55..9cb1b4637 100644 --- a/src/main/java/net/minestom/server/network/player/FakePlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/FakePlayerConnection.java @@ -13,7 +13,7 @@ import java.net.SocketAddress; public class FakePlayerConnection extends PlayerConnection { @Override - public void sendPacket(@NotNull ServerPacket serverPacket) { + public void sendPacket(@NotNull ServerPacket serverPacket, boolean skipTranslating) { if (shouldSendPacket(serverPacket)) { getFakePlayer().getController().consumePacket(serverPacket); } diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index c5b76d49c..91042081d 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -125,7 +125,7 @@ public class NettyPlayerConnection extends PlayerConnection { * @param serverPacket the packet to write */ @Override - public void sendPacket(@NotNull ServerPacket serverPacket) { + public void sendPacket(@NotNull ServerPacket serverPacket, boolean skipTranslating) { if (!channel.isActive()) return; @@ -138,7 +138,7 @@ public class NettyPlayerConnection extends PlayerConnection { if (identifier == null) { // This packet explicitly asks to do not retrieve the cache - write(serverPacket); + write(serverPacket, skipTranslating); } else { final long timestamp = cacheablePacket.getTimestamp(); // Try to retrieve the cached buffer @@ -163,7 +163,7 @@ public class NettyPlayerConnection extends PlayerConnection { } } else { - write(serverPacket); + write(serverPacket, skipTranslating); } } else { // Player is probably not logged yet @@ -173,6 +173,10 @@ public class NettyPlayerConnection extends PlayerConnection { } public void write(@NotNull Object message) { + this.write(message, false); + } + + public void write(@NotNull Object message, boolean skipTranslating) { if (message instanceof FramedPacket) { final FramedPacket framedPacket = (FramedPacket) message; synchronized (tickBuffer) { @@ -183,7 +187,7 @@ public class NettyPlayerConnection extends PlayerConnection { } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; - if (getPlayer() != null && serverPacket instanceof ComponentHoldingServerPacket) { + if (!skipTranslating && getPlayer() != null && serverPacket instanceof ComponentHoldingServerPacket) { serverPacket = ((ComponentHoldingServerPacket) serverPacket).copyWithOperator(component -> MinecraftServer.getSerializationManager().translate(component, getPlayer())); } diff --git a/src/main/java/net/minestom/server/network/player/PlayerConnection.java b/src/main/java/net/minestom/server/network/player/PlayerConnection.java index 77a63c697..e183c772a 100644 --- a/src/main/java/net/minestom/server/network/player/PlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/PlayerConnection.java @@ -90,7 +90,19 @@ public abstract class PlayerConnection { * @param serverPacket the packet to send * @see #shouldSendPacket(ServerPacket) */ - public abstract void sendPacket(@NotNull ServerPacket serverPacket); + public void sendPacket(@NotNull ServerPacket serverPacket) { + this.sendPacket(serverPacket, false); + } + + /** + * Serializes the packet and send it to the client, skipping the translation phase. + *

+ * Also responsible for executing {@link ConnectionManager#onPacketSend(ServerPacketConsumer)} consumers. + * + * @param serverPacket the packet to send + * @see #shouldSendPacket(ServerPacket) + */ + public abstract void sendPacket(@NotNull ServerPacket serverPacket, boolean skipTranslating); protected boolean shouldSendPacket(@NotNull ServerPacket serverPacket) { return player == null || From 1cc7867f9bab205d66a89c79c01682cedb42cd71 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Fri, 12 Mar 2021 17:26:57 +0000 Subject: [PATCH 109/364] Exclude more checker-qual --- src/main/java/net/minestom/server/entity/Player.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index f36d207d5..f4aedbd00 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -73,7 +73,6 @@ 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; @@ -2700,7 +2699,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, } @Override - public @NonNull Identity identity() { + public @NotNull Identity identity() { return Identity.identity(this.uuid); } From 96826e03d1b4f5e9e6af687f8d13d4b42fa8dc82 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 15 Mar 2021 13:53:06 +0000 Subject: [PATCH 110/364] Cache player Identity --- .../java/net/minestom/server/entity/Player.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index f4aedbd00..8220fa46f 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -178,6 +178,9 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // Tick related private final PlayerTickEvent playerTickEvent = new PlayerTickEvent(this); + // Adventure + private Identity identity; + public Player(@NotNull UUID uuid, @NotNull String username, @NotNull PlayerConnection playerConnection) { super(EntityType.PLAYER, uuid); this.username = username; @@ -202,6 +205,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // FakePlayer init its connection there playerConnectionInit(); + + this.identity = Identity.identity(uuid); } /** @@ -2700,7 +2705,14 @@ public class Player extends LivingEntity implements CommandSender, Localizable, @Override public @NotNull Identity identity() { - return Identity.identity(this.uuid); + return this.identity; + } + + @Override + public void setUuid(@NotNull UUID uuid) { + super.setUuid(uuid); + // update identity + this.identity = Identity.identity(uuid); } /** From 8dc7328c76d3121e158ba4e7284547a329baeb74 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 15 Mar 2021 14:19:43 +0000 Subject: [PATCH 111/364] Off-by-default component translation --- .../minestom/server/adventure/SerializationManager.java | 5 +++++ .../server/network/player/NettyPlayerConnection.java | 3 ++- src/main/java/net/minestom/server/utils/PacketUtils.java | 7 ++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/minestom/server/adventure/SerializationManager.java b/src/main/java/net/minestom/server/adventure/SerializationManager.java index ad2f71f6f..939afdccc 100644 --- a/src/main/java/net/minestom/server/adventure/SerializationManager.java +++ b/src/main/java/net/minestom/server/adventure/SerializationManager.java @@ -27,6 +27,11 @@ import java.util.function.Function; * {@link TranslationRegistry} or your own implementation of {@link Translator}. */ public class SerializationManager { + /** + * If components should be automatically translated in outgoing packets. + */ + public static final boolean AUTOMATIC_COMPONENT_TRANSLATION = false; + protected static final Localizable NULL_LOCALIZABLE = () -> null; private Function serializer = component -> GsonComponentSerializer.gson().serialize(component); diff --git a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java index 91042081d..3cfe40052 100644 --- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java +++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java @@ -5,6 +5,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.socket.SocketChannel; import net.minestom.server.MinecraftServer; +import net.minestom.server.adventure.SerializationManager; import net.minestom.server.entity.PlayerSkin; import net.minestom.server.extras.mojangAuth.Decrypter; import net.minestom.server.extras.mojangAuth.Encrypter; @@ -187,7 +188,7 @@ public class NettyPlayerConnection extends PlayerConnection { } else if (message instanceof ServerPacket) { final ServerPacket serverPacket = (ServerPacket) message; - if (!skipTranslating && getPlayer() != null && serverPacket instanceof ComponentHoldingServerPacket) { + if ((SerializationManager.AUTOMATIC_COMPONENT_TRANSLATION && !skipTranslating) && getPlayer() != null && serverPacket instanceof ComponentHoldingServerPacket) { serverPacket = ((ComponentHoldingServerPacket) serverPacket).copyWithOperator(component -> MinecraftServer.getSerializationManager().translate(component, getPlayer())); } diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 0108ecbd7..5264fad8f 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -3,6 +3,7 @@ package net.minestom.server.utils; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.minestom.server.MinecraftServer; +import net.minestom.server.adventure.SerializationManager; import net.minestom.server.entity.Player; import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.network.netty.packet.FramedPacket; @@ -49,7 +50,7 @@ public final class PacketUtils { // work out if the packet needs to be sent individually due to server-side translating boolean needsTranslating = false; - if (packet instanceof ComponentHoldingServerPacket) { + if (SerializationManager.AUTOMATIC_COMPONENT_TRANSLATION && packet instanceof ComponentHoldingServerPacket) { needsTranslating = MinecraftServer.getSerializationManager().areAnyTranslatable(((ComponentHoldingServerPacket) packet).components()); } @@ -75,7 +76,7 @@ public final class PacketUtils { final PlayerConnection playerConnection = player.getPlayerConnection(); if (playerConnection instanceof NettyPlayerConnection) { final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) playerConnection; - nettyPlayerConnection.write(framedPacket); + nettyPlayerConnection.write(framedPacket, true); } else { playerConnection.sendPacket(packet); } @@ -93,7 +94,7 @@ public final class PacketUtils { continue; final PlayerConnection playerConnection = player.getPlayerConnection(); - playerConnection.sendPacket(packet); + playerConnection.sendPacket(packet, false); } } } From 63aaee698ece15c109146597f5c02bb3706544d6 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Mon, 15 Mar 2021 14:56:37 +0000 Subject: [PATCH 112/364] Rebase to master and fix complication issues --- .../builder/suggestion/SuggestionEntry.java | 28 ++++++++++++++++--- .../net/minestom/server/entity/Player.java | 2 -- .../java/demo/commands/HealthCommand.java | 2 +- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/command/builder/suggestion/SuggestionEntry.java b/src/main/java/net/minestom/server/command/builder/suggestion/SuggestionEntry.java index 9e8a2b96c..d05e4dd64 100644 --- a/src/main/java/net/minestom/server/command/builder/suggestion/SuggestionEntry.java +++ b/src/main/java/net/minestom/server/command/builder/suggestion/SuggestionEntry.java @@ -1,5 +1,6 @@ package net.minestom.server.command.builder.suggestion; +import net.kyori.adventure.text.Component; import net.minestom.server.chat.JsonMessage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -7,15 +8,25 @@ import org.jetbrains.annotations.Nullable; public class SuggestionEntry { private final String entry; - private final JsonMessage tooltip; + private final Component tooltip; + /** + * @deprecated Use {{@link #SuggestionEntry(String, JsonMessage)}} + */ + @Deprecated public SuggestionEntry(@NotNull String entry, @Nullable JsonMessage tooltip) { this.entry = entry; - this.tooltip = tooltip; + this.tooltip = tooltip.asComponent(); + } + + public SuggestionEntry(@NotNull String entry, @Nullable Component tooltip) { + this.entry = entry; + this.tooltip = tooltip.asComponent(); } public SuggestionEntry(@NotNull String entry) { - this(entry, null); + this.entry = entry; + this.tooltip = null; } @NotNull @@ -24,7 +35,16 @@ public class SuggestionEntry { } @Nullable - public JsonMessage getTooltip() { + public Component getTooltip() { return tooltip; } + + /** + * @deprecated Use {@link #getTooltip()} + */ + @Deprecated + @Nullable + public JsonMessage getTooltipJson() { + return JsonMessage.fromComponent(tooltip); + } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 8220fa46f..29483c0a7 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -17,8 +17,6 @@ import net.minestom.server.advancements.AdvancementTab; import net.minestom.server.adventure.AdventurePacketConvertor; import net.minestom.server.adventure.Localizable; import net.minestom.server.attribute.Attribute; -import net.minestom.server.attribute.AttributeInstance; -import net.minestom.server.bossbar.BossBar; import net.minestom.server.chat.ChatParser; import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.JsonMessage; diff --git a/src/test/java/demo/commands/HealthCommand.java b/src/test/java/demo/commands/HealthCommand.java index d8279bb82..171e5dac6 100644 --- a/src/test/java/demo/commands/HealthCommand.java +++ b/src/test/java/demo/commands/HealthCommand.java @@ -59,7 +59,7 @@ public class HealthCommand extends Command { } private void sendSuggestionMessage(CommandSender sender, CommandContext context) { - sender.sendMessage(Component.text("/health " + args.get("mode") + " [Integer]")); + sender.sendMessage(Component.text("/health " + context.get("mode") + " [Integer]")); } private void onHealthCommand(CommandSender sender, CommandContext context) { From 0082f5776b6badb94d108d1bb78b3ea7df6cac7f Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Tue, 16 Mar 2021 15:45:01 +0000 Subject: [PATCH 113/364] switch to properties-based dep alignment for adventure --- build.gradle | 9 ++++----- gradle.properties | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 608ab545f..a4d7fcd0e 100644 --- a/build.gradle +++ b/build.gradle @@ -184,11 +184,10 @@ dependencies { generatorsImplementation("com.squareup:javapoet:1.13.0") // Adventure, for text messages - api platform("net.kyori:adventure-bom:4.7.0") - api "net.kyori:adventure-api" - api "net.kyori:adventure-text-serializer-gson" - api "net.kyori:adventure-text-serializer-plain" - api "net.kyori:adventure-text-serializer-legacy" + api "net.kyori:adventure-api:$adventureVersion" + api "net.kyori:adventure-text-serializer-gson:$adventureVersion" + api "net.kyori:adventure-text-serializer-plain:$adventureVersion" + api "net.kyori:adventure-text-serializer-legacy:$adventureVersion" } publishing { diff --git a/gradle.properties b/gradle.properties index 14a2ffece..c74ba052d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,5 @@ asmVersion=9.0 mixinVersion=0.8.1 hephaistosVersion=v1.1.8 -kotlinVersion=1.4.21 \ No newline at end of file +kotlinVersion=1.4.21 +adventureVersion=4.7.0 \ No newline at end of file From 8ee624f02d52ae8103bfa50e23e95c528635c356 Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Wed, 17 Mar 2021 12:19:22 +0000 Subject: [PATCH 114/364] make Sound enum implement Adventure's Sound.Type --- .../java/net/minestom/server/sound/Sound.java | 2 +- .../minestom/codegen/sounds/SoundEnumGenerator.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/autogenerated/java/net/minestom/server/sound/Sound.java b/src/autogenerated/java/net/minestom/server/sound/Sound.java index 2394aff1a..d5b8058d0 100644 --- a/src/autogenerated/java/net/minestom/server/sound/Sound.java +++ b/src/autogenerated/java/net/minestom/server/sound/Sound.java @@ -11,7 +11,7 @@ import net.minestom.server.utils.NamespaceID; * //============================== */ @SuppressWarnings({"deprecation"}) -public enum Sound implements Keyed { +public enum Sound implements Keyed, net.kyori.adventure.sound.Sound.Type { AMBIENT_CAVE("minecraft:ambient.cave"), AMBIENT_BASALT_DELTAS_ADDITIONS("minecraft:ambient.basalt_deltas.additions"), diff --git a/src/generators/java/net/minestom/codegen/sounds/SoundEnumGenerator.java b/src/generators/java/net/minestom/codegen/sounds/SoundEnumGenerator.java index 8ca5f8fd2..0d67b1ea5 100644 --- a/src/generators/java/net/minestom/codegen/sounds/SoundEnumGenerator.java +++ b/src/generators/java/net/minestom/codegen/sounds/SoundEnumGenerator.java @@ -1,7 +1,9 @@ package net.minestom.codegen.sounds; +import com.squareup.javapoet.ClassName; +import net.kyori.adventure.sound.Sound; import net.minestom.codegen.BasicEnumGenerator; -import net.minestom.codegen.stats.StatsEnumGenerator; +import net.minestom.codegen.EnumGenerator; import net.minestom.server.registry.ResourceGatherer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,6 +47,14 @@ public class SoundEnumGenerator extends BasicEnumGenerator { super(targetFolder); } + @Override + protected void prepare(EnumGenerator generator) { + super.prepare(generator); + + // implement type as well + generator.addSuperinterface(ClassName.get(Sound.Type.class)); + } + @Override protected String getCategoryID() { return "minecraft:sound_event"; From b4b2cf70c547464f5f6c24cb12c6f0159e50417d Mon Sep 17 00:00:00 2001 From: Kieran Wallbanks Date: Thu, 18 Mar 2021 15:28:41 +0000 Subject: [PATCH 115/364] Revert argument changes to single file with adventure style result --- .../builder/arguments/ArgumentType.java | 24 +------ .../minecraft/ArgumentChatColor.java | 43 ------------ .../arguments/minecraft/ArgumentColor.java | 39 +++++++---- .../arguments/minecraft/ArgumentDyeColor.java | 32 --------- .../minecraft/ArgumentTeamColor.java | 40 ------------ .../minecraft/ArgumentTextColor.java | 65 ------------------- .../minecraft/ArgumentTextDecoration.java | 40 ------------ 7 files changed, 26 insertions(+), 257 deletions(-) delete mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java delete mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentDyeColor.java delete mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTeamColor.java delete mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextColor.java delete mode 100644 src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentTextDecoration.java diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java index 4dc0e1841..bf244ba8d 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentType.java @@ -107,34 +107,12 @@ public class ArgumentType { // Minecraft specific arguments /** - * @deprecated Use {@link ArgumentTextColor} for colors, {@link ArgumentTextDecoration} for styles, {@link ArgumentColor} for raw colors, - * {@link ArgumentDyeColor} for dye colors and {@link ArgumentTeamColor} for team formats + * @see ArgumentColor */ - @Deprecated - public static ArgumentChatColor ChatColor(@NotNull String id) { - return new ArgumentChatColor(id); - } - - public static ArgumentTextColor TextColor(@NotNull String id) { - return new ArgumentTextColor(id); - } - - public static ArgumentTextDecoration TextDecoration(@NotNull String id) { - return new ArgumentTextDecoration(id); - } - public static ArgumentColor Color(@NotNull String id) { return new ArgumentColor(id); } - public static ArgumentDyeColor DyeColor(@NotNull String id) { - return new ArgumentDyeColor(id); - } - - public static ArgumentTeamColor TeamColor(@NotNull String id) { - return new ArgumentTeamColor(id); - } - /** * @see ArgumentTime */ diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java deleted file mode 100644 index f34a36205..000000000 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentChatColor.java +++ /dev/null @@ -1,43 +0,0 @@ -package net.minestom.server.command.builder.arguments.minecraft; - -import net.minestom.server.chat.ChatColor; -import net.minestom.server.command.builder.NodeMaker; -import net.minestom.server.command.builder.arguments.Argument; -import net.minestom.server.command.builder.exception.ArgumentSyntaxException; -import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; -import org.jetbrains.annotations.NotNull; - -/** - * Represents an argument which will give you a {@link ChatColor}. - *

- * Example: red, white, reset - * @deprecated Use {@link ArgumentTextColor} for colors, {@link ArgumentTextDecoration} for styles, {@link ArgumentColor} for raw colors, - * {@link ArgumentDyeColor} for dye colors and {@link ArgumentTeamColor} for team formats - */ -@Deprecated -public class ArgumentChatColor extends Argument { - - public static final int UNDEFINED_COLOR = -2; - - public ArgumentChatColor(String id) { - super(id); - } - - @NotNull - @Override - public ChatColor parse(@NotNull String input) throws ArgumentSyntaxException { - final ChatColor color = ChatColor.fromName(input); - if (color == ChatColor.NO_COLOR) - throw new ArgumentSyntaxException("Undefined color", input, UNDEFINED_COLOR); - - return color; - } - - @Override - public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { - DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); - argumentNode.parser = "minecraft:chat_color"; - - nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode}); - } -} diff --git a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java index b8701d9e0..0fc2363bb 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/minecraft/ArgumentColor.java @@ -1,7 +1,8 @@ package net.minestom.server.command.builder.arguments.minecraft; import net.kyori.adventure.text.format.NamedTextColor; -import net.minestom.server.color.Color; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextDecoration; import net.minestom.server.command.builder.NodeMaker; import net.minestom.server.command.builder.arguments.Argument; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; @@ -9,26 +10,36 @@ import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; import org.jetbrains.annotations.NotNull; /** - * Represents an argument that will give you a {@link Color}. Input is parsed - * first as a hex string ({@code #int}), then as a CSS hex string ({@code #rrggbb} or - * {@code #rgb}), then as an integer and finally as a named text colour. The values for - * the named text colours can be found in {@link NamedTextColor}. - *

- * This class is essentially a wrapper around {@link ArgumentTextColor}. + * Represents an argument which will give you a {@link Style} containing the colour or no + * colour if the argument was {@code reset}. + *

+ * Example: red, white, reset */ -public class ArgumentColor extends Argument { - private final ArgumentTextColor argumentTextColor; +public class ArgumentColor extends Argument