diff --git a/build.gradle b/build.gradle
index 986c6a9c4..1a019f754 100644
--- a/build.gradle
+++ b/build.gradle
@@ -107,12 +107,6 @@ dependencies {
// Only here to ensure J9 module support for extensions and our classloaders
testCompileOnly 'org.mockito:mockito-core:3.11.1'
- // Netty
- api 'io.netty:netty-handler:4.1.65.Final'
- api 'io.netty:netty-codec:4.1.65.Final'
- api 'io.netty:netty-transport-native-epoll:4.1.65.Final:linux-x86_64'
- api 'io.netty:netty-transport-native-kqueue:4.1.65.Final:osx-x86_64'
-
// https://mvnrepository.com/artifact/it.unimi.dsi/fastutil
api 'it.unimi.dsi:fastutil:8.5.4'
diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java
index 4d1b2a742..84605aa0e 100644
--- a/src/main/java/net/minestom/server/MinecraftServer.java
+++ b/src/main/java/net/minestom/server/MinecraftServer.java
@@ -21,10 +21,10 @@ import net.minestom.server.listener.manager.PacketListenerManager;
import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.PacketProcessor;
-import net.minestom.server.network.netty.NettyServer;
import net.minestom.server.network.packet.server.play.PluginMessagePacket;
import net.minestom.server.network.packet.server.play.ServerDifficultyPacket;
import net.minestom.server.network.packet.server.play.UpdateViewDistancePacket;
+import net.minestom.server.network.socket.Server;
import net.minestom.server.ping.ResponseDataConsumer;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.scoreboard.TeamManager;
@@ -44,6 +44,9 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
/**
* The main server class used to start the server and retrieve all the managers.
*
@@ -85,9 +88,7 @@ public final class MinecraftServer {
// Network
private static PacketListenerManager packetListenerManager;
private static PacketProcessor packetProcessor;
- private static NettyServer nettyServer;
- private static int nettyThreadCount = Runtime.getRuntime().availableProcessors();
- private static boolean processNettyErrors = true;
+ private static Server server;
private static ExceptionManager exceptionManager;
@@ -122,7 +123,6 @@ public final class MinecraftServer {
private static int chunkViewDistance = 8;
private static int entityViewDistance = 5;
private static int compressionThreshold = 256;
- private static boolean packetCaching = true;
private static boolean groupedPacket = true;
private static boolean terminalEnabled = System.getProperty("minestom.terminal.disabled") == null;
private static ResponseDataConsumer responseDataConsumer;
@@ -168,7 +168,11 @@ public final class MinecraftServer {
tagManager = new TagManager();
- nettyServer = new NettyServer(packetProcessor);
+ try {
+ server = new Server(packetProcessor);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
initialized = true;
@@ -277,16 +281,6 @@ public final class MinecraftServer {
return packetListenerManager;
}
- /**
- * Gets the netty server.
- *
- * @return the netty server
- */
- public static NettyServer getNettyServer() {
- checkInitStatus(nettyServer);
- return nettyServer;
- }
-
/**
* Gets the manager handling all registered instances.
*
@@ -519,34 +513,6 @@ public final class MinecraftServer {
MinecraftServer.compressionThreshold = compressionThreshold;
}
- /**
- * Gets if the packet caching feature is enabled.
- *
- * This feature allows some packets (implementing the {@link net.minestom.server.utils.cache.CacheablePacket} to be cached
- * in order to do not have to be written and compressed over and over again), this is especially useful for chunk and light packets.
- *
- * It is enabled by default and it is our recommendation,
- * you should only disable it if you want to focus on low memory usage
- * at the cost of many packet writing and compression.
- *
- * @return true if the packet caching feature is enabled, false otherwise
- */
- public static boolean hasPacketCaching() {
- return packetCaching;
- }
-
- /**
- * Enables or disable packet caching.
- *
- * @param packetCaching true to enable packet caching
- * @throws IllegalStateException if this is called after the server started
- * @see #hasPacketCaching()
- */
- public static void setPacketCaching(boolean packetCaching) {
- Check.stateCondition(started, "You cannot change the packet caching value after the server has been started.");
- MinecraftServer.packetCaching = packetCaching;
- }
-
/**
* Gets if the packet caching feature is enabled.
*
@@ -666,45 +632,9 @@ public final class MinecraftServer {
return updateManager;
}
- /**
- * Gets the number of threads used by Netty.
- *
- * Is the number of vCPU by default.
- *
- * @return the number of netty threads
- */
- public static int getNettyThreadCount() {
- return nettyThreadCount;
- }
-
- /**
- * Changes the number of threads used by Netty.
- *
- * @param nettyThreadCount the number of threads
- * @throws IllegalStateException if the server is already started
- */
- public static void setNettyThreadCount(int nettyThreadCount) {
- Check.stateCondition(started, "Netty thread count can only be changed before the server starts!");
- MinecraftServer.nettyThreadCount = nettyThreadCount;
- }
-
- /**
- * Gets if the server should process netty errors and other unnecessary netty events.
- *
- * @return should process netty errors
- */
- public static boolean shouldProcessNettyErrors() {
- return processNettyErrors;
- }
-
- /**
- * Sets if the server should process netty errors and other unnecessary netty events.
- * false is faster
- *
- * @param processNettyErrors should process netty errors
- */
- public static void setShouldProcessNettyErrors(boolean processNettyErrors) {
- MinecraftServer.processNettyErrors = processNettyErrors;
+ public static Server getServer() {
+ checkInitStatus(server);
+ return server;
}
/**
@@ -744,8 +674,11 @@ public final class MinecraftServer {
updateManager.start();
// Init & start the TCP server
- nettyServer.init();
- nettyServer.start(address, port);
+ try {
+ server.start(new InetSocketAddress(address, port));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
if (extensionManager.shouldLoadOnStartup()) {
final long loadStartTime = System.nanoTime();
@@ -779,7 +712,7 @@ public final class MinecraftServer {
updateManager.stop();
schedulerManager.shutdown();
connectionManager.shutdown();
- nettyServer.stop();
+ server.stop();
storageManager.getLoadedLocations().forEach(StorageLocation::close);
LOGGER.info("Unloading all extensions.");
extensionManager.shutdown();
diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentString.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentString.java
index 696760381..f8aaed23d 100644
--- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentString.java
+++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentString.java
@@ -1,6 +1,5 @@
package net.minestom.server.command.builder.arguments;
-import io.netty.util.internal.StringUtil;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
@@ -48,11 +47,10 @@ public class ArgumentString extends Argument {
*/
@Deprecated
public static String staticParse(@NotNull String input) throws ArgumentSyntaxException {
-
// Return if not quoted
if (!input.contains(String.valueOf(DOUBLE_QUOTE)) &&
!input.contains(String.valueOf(QUOTE)) &&
- !input.contains(String.valueOf(StringUtil.SPACE))) {
+ !input.contains(StringUtils.SPACE)) {
return input;
}
diff --git a/src/main/java/net/minestom/server/extras/lan/OpenToLAN.java b/src/main/java/net/minestom/server/extras/lan/OpenToLAN.java
index 350b05b83..a4777587a 100644
--- a/src/main/java/net/minestom/server/extras/lan/OpenToLAN.java
+++ b/src/main/java/net/minestom/server/extras/lan/OpenToLAN.java
@@ -121,7 +121,7 @@ public class OpenToLAN {
* Performs the ping.
*/
private static void ping() {
- if (MinecraftServer.getNettyServer().getPort() != 0) {
+ if (MinecraftServer.getServer().getPort() != 0) {
if (packet == null || eventCooldown.isReady(System.currentTimeMillis())) {
final ServerListPingEvent event = new ServerListPingEvent(OPEN_TO_LAN);
EventDispatcher.call(event);
diff --git a/src/main/java/net/minestom/server/extras/mojangAuth/CipherBase.java b/src/main/java/net/minestom/server/extras/mojangAuth/CipherBase.java
deleted file mode 100644
index 9c77a7e52..000000000
--- a/src/main/java/net/minestom/server/extras/mojangAuth/CipherBase.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package net.minestom.server.extras.mojangAuth;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import org.jetbrains.annotations.NotNull;
-
-import javax.crypto.Cipher;
-import javax.crypto.ShortBufferException;
-
-public class CipherBase {
-
- private final Cipher cipher;
- private byte[] inTempArray = new byte[0];
- private byte[] outTempArray = new byte[0];
-
- protected CipherBase(@NotNull Cipher cipher) {
- this.cipher = cipher;
- }
-
- private byte[] bufToByte(ByteBuf buffer) {
- int remainingBytes = buffer.readableBytes();
-
- // Need to resize temp array
- if (inTempArray.length < remainingBytes) {
- inTempArray = new byte[remainingBytes];
- }
-
- buffer.readBytes(inTempArray, 0, remainingBytes);
- return inTempArray;
- }
-
- protected ByteBuf decrypt(ChannelHandlerContext channelHandlerContext, ByteBuf byteBufIn) throws ShortBufferException {
- int remainingBytes = byteBufIn.readableBytes();
- byte[] bytes = bufToByte(byteBufIn);
-
- ByteBuf outputBuffer = channelHandlerContext.alloc().heapBuffer(cipher.getOutputSize(remainingBytes));
- outputBuffer.writerIndex(cipher.update(bytes, 0, remainingBytes, outputBuffer.array(), outputBuffer.arrayOffset()));
-
- return outputBuffer;
- }
-
- protected void encrypt(ByteBuf byteBufIn, ByteBuf byteBufOut) throws ShortBufferException {
- int remainingBytes = byteBufIn.readableBytes();
- byte[] bytes = bufToByte(byteBufIn);
- int newSize = cipher.getOutputSize(remainingBytes);
-
- // Need to resize temp array
- if (outTempArray.length < newSize) {
- outTempArray = new byte[newSize];
- }
-
- byteBufOut.writeBytes(outTempArray, 0, cipher.update(bytes, 0, remainingBytes, outTempArray));
- }
-}
diff --git a/src/main/java/net/minestom/server/extras/mojangAuth/Decrypter.java b/src/main/java/net/minestom/server/extras/mojangAuth/Decrypter.java
deleted file mode 100644
index 69c89ad6f..000000000
--- a/src/main/java/net/minestom/server/extras/mojangAuth/Decrypter.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package net.minestom.server.extras.mojangAuth;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToMessageDecoder;
-
-import javax.crypto.Cipher;
-import java.util.List;
-
-public class Decrypter extends MessageToMessageDecoder {
- private final CipherBase cipher;
-
- public Decrypter(Cipher cipher) {
- this.cipher = new CipherBase(cipher);
- }
-
- protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List list) throws Exception {
- list.add(this.cipher.decrypt(channelHandlerContext, byteBuf));
- }
-}
diff --git a/src/main/java/net/minestom/server/extras/mojangAuth/Encrypter.java b/src/main/java/net/minestom/server/extras/mojangAuth/Encrypter.java
deleted file mode 100644
index 053ca36cb..000000000
--- a/src/main/java/net/minestom/server/extras/mojangAuth/Encrypter.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package net.minestom.server.extras.mojangAuth;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToByteEncoder;
-
-import javax.crypto.Cipher;
-
-public class Encrypter extends MessageToByteEncoder {
- private final CipherBase cipher;
-
- public Encrypter(Cipher cipher) {
- this.cipher = new CipherBase(cipher);
- }
-
- protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBufIn, ByteBuf byteBufOut) throws Exception {
- this.cipher.encrypt(byteBufIn, byteBufOut);
- }
-}
diff --git a/src/main/java/net/minestom/server/extras/query/Query.java b/src/main/java/net/minestom/server/extras/query/Query.java
index a99225607..f80e88fc5 100644
--- a/src/main/java/net/minestom/server/extras/query/Query.java
+++ b/src/main/java/net/minestom/server/extras/query/Query.java
@@ -1,7 +1,5 @@
package net.minestom.server.extras.query;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@@ -23,6 +21,7 @@ import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Random;
@@ -151,18 +150,18 @@ public class Query {
}
// get the contents
- ByteBuf data = Unpooled.wrappedBuffer(packet.getData());
+ ByteBuffer data = ByteBuffer.wrap(packet.getData());
// check the magic field
- if (data.readUnsignedShort() != 0xFEFD) {
+ if ((data.getShort() & 0xFFFF) != 0xFEFD) {
continue;
}
// now check the query type
- byte type = data.readByte();
+ byte type = data.get();
if (type == 9) { // handshake
- int sessionID = data.readInt();
+ int sessionID = data.getInt();
int challengeToken = RANDOM.nextInt();
CHALLENGE_TOKENS.put(challengeToken, packet.getSocketAddress());
@@ -184,12 +183,12 @@ public class Query {
}
}
} else if (type == 0) { // stat
- int sessionID = data.readInt();
- int challengeToken = data.readInt();
+ int sessionID = data.getInt();
+ int challengeToken = data.getInt();
SocketAddress sender = packet.getSocketAddress();
if (CHALLENGE_TOKENS.containsKey(challengeToken) && CHALLENGE_TOKENS.get(challengeToken).equals(sender)) {
- int remaining = data.readableBytes();
+ int remaining = data.remaining();
if (remaining == 0) { // basic
BasicQueryEvent event = new BasicQueryEvent(sender, sessionID);
diff --git a/src/main/java/net/minestom/server/extras/query/response/BasicQueryResponse.java b/src/main/java/net/minestom/server/extras/query/response/BasicQueryResponse.java
index da502f2a0..144c30064 100644
--- a/src/main/java/net/minestom/server/extras/query/response/BasicQueryResponse.java
+++ b/src/main/java/net/minestom/server/extras/query/response/BasicQueryResponse.java
@@ -142,7 +142,7 @@ public class BasicQueryResponse implements Writeable {
writer.writeNullTerminatedString(this.map, Query.CHARSET);
writer.writeNullTerminatedString(this.numPlayers, Query.CHARSET);
writer.writeNullTerminatedString(this.maxPlayers, Query.CHARSET);
- writer.getBuffer().writeShortLE(MinecraftServer.getNettyServer().getPort());
- writer.writeNullTerminatedString(Objects.requireNonNullElse(MinecraftServer.getNettyServer().getAddress(), ""), Query.CHARSET);
+ writer.getBuffer().putShort((short) MinecraftServer.getServer().getPort()); // TODO little endian?
+ writer.writeNullTerminatedString(Objects.requireNonNullElse(MinecraftServer.getServer().getAddress(), ""), Query.CHARSET);
}
}
diff --git a/src/main/java/net/minestom/server/extras/query/response/QueryKey.java b/src/main/java/net/minestom/server/extras/query/response/QueryKey.java
index 066fb3f8a..0b5762e3b 100644
--- a/src/main/java/net/minestom/server/extras/query/response/QueryKey.java
+++ b/src/main/java/net/minestom/server/extras/query/response/QueryKey.java
@@ -20,8 +20,8 @@ public enum QueryKey {
MAP(() -> "world"),
NUM_PLAYERS("numplayers", () -> String.valueOf(MinecraftServer.getConnectionManager().getOnlinePlayers().size())),
MAX_PLAYERS("maxplayers", () -> String.valueOf(MinecraftServer.getConnectionManager().getOnlinePlayers().size() + 1)),
- HOST_PORT("hostport", () -> String.valueOf(MinecraftServer.getNettyServer().getPort())),
- HOST_IP("hostip", () -> Objects.requireNonNullElse(MinecraftServer.getNettyServer().getAddress(), "localhost"));
+ HOST_PORT("hostport", () -> String.valueOf(MinecraftServer.getServer().getPort())),
+ HOST_IP("hostip", () -> Objects.requireNonNullElse(MinecraftServer.getServer().getAddress(), "localhost"));
static QueryKey[] VALUES = QueryKey.values();
diff --git a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java
index f4a133243..97e653109 100644
--- a/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java
+++ b/src/main/java/net/minestom/server/extras/velocity/VelocityProxy.java
@@ -1,6 +1,5 @@
package net.minestom.server.extras.velocity;
-import io.netty.buffer.ByteBuf;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.PlayerSkin;
import net.minestom.server.utils.binary.BinaryReader;
@@ -10,6 +9,7 @@ import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -54,9 +54,11 @@ public final class VelocityProxy {
final byte[] signature = reader.readBytes(32);
- ByteBuf buf = reader.getBuffer();
- final byte[] data = new byte[buf.readableBytes()];
- buf.getBytes(buf.readerIndex(), data);
+ ByteBuffer buf = reader.getBuffer();
+ buf.mark();
+ final byte[] data = new byte[buf.remaining()];
+ buf.get(data);
+ buf.reset();
try {
final Mac mac = Mac.getInstance("HmacSHA256");
diff --git a/src/main/java/net/minestom/server/instance/Chunk.java b/src/main/java/net/minestom/server/instance/Chunk.java
index f82bbff35..dec32ac4d 100644
--- a/src/main/java/net/minestom/server/instance/Chunk.java
+++ b/src/main/java/net/minestom/server/instance/Chunk.java
@@ -256,7 +256,7 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
List skyLights = new ArrayList<>();
List blockLights = new ArrayList<>();
- UpdateLightPacket updateLightPacket = new UpdateLightPacket(getIdentifier(), getLastChangeTime());
+ UpdateLightPacket updateLightPacket = new UpdateLightPacket();
updateLightPacket.chunkX = getChunkX();
updateLightPacket.chunkZ = getChunkZ();
@@ -392,7 +392,5 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
*/
protected void unload() {
this.loaded = false;
- ChunkDataPacket.CACHE.invalidate(getIdentifier());
- UpdateLightPacket.CACHE.invalidate(getIdentifier());
}
}
\ No newline at end of file
diff --git a/src/main/java/net/minestom/server/instance/DynamicChunk.java b/src/main/java/net/minestom/server/instance/DynamicChunk.java
index cdcd32edb..d44fe308a 100644
--- a/src/main/java/net/minestom/server/instance/DynamicChunk.java
+++ b/src/main/java/net/minestom/server/instance/DynamicChunk.java
@@ -124,7 +124,7 @@ public class DynamicChunk extends Chunk {
if (packet != null && cachedPacketTime == getLastChangeTime()) {
return packet;
}
- packet = new ChunkDataPacket(getIdentifier(), getLastChangeTime());
+ packet = new ChunkDataPacket();
packet.biomes = biomes;
packet.chunkX = chunkX;
packet.chunkZ = chunkZ;
diff --git a/src/main/java/net/minestom/server/item/ItemMeta.java b/src/main/java/net/minestom/server/item/ItemMeta.java
index 9b6c5b211..a31518f88 100644
--- a/src/main/java/net/minestom/server/item/ItemMeta.java
+++ b/src/main/java/net/minestom/server/item/ItemMeta.java
@@ -1,6 +1,5 @@
package net.minestom.server.item;
-import io.netty.buffer.ByteBuf;
import net.kyori.adventure.text.Component;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.attribute.ItemAttribute;
@@ -13,6 +12,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
+import java.nio.ByteBuffer;
import java.util.*;
import java.util.function.Consumer;
@@ -36,7 +36,7 @@ public class ItemMeta implements TagReadable, Writeable {
private final NBTCompound nbt;
private String cachedSNBT;
- private ByteBuf cachedBuffer;
+ private ByteBuffer cachedBuffer;
protected ItemMeta(@NotNull ItemMetaBuilder metaBuilder) {
this.damage = metaBuilder.damage;
@@ -154,6 +154,6 @@ public class ItemMeta implements TagReadable, Writeable {
this.cachedBuffer = w.getBuffer();
}
writer.write(cachedBuffer);
- this.cachedBuffer.resetReaderIndex();
+ this.cachedBuffer.position(0);
}
}
diff --git a/src/main/java/net/minestom/server/network/ConnectionManager.java b/src/main/java/net/minestom/server/network/ConnectionManager.java
index cd364866e..d6b8d1fef 100644
--- a/src/main/java/net/minestom/server/network/ConnectionManager.java
+++ b/src/main/java/net/minestom/server/network/ConnectionManager.java
@@ -1,6 +1,5 @@
package net.minestom.server.network;
-import io.netty.channel.Channel;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
@@ -24,6 +23,7 @@ import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -294,7 +294,7 @@ public final class ConnectionManager {
// Close the player channel if he has been disconnected (kick)
if (!player.isOnline()) {
if (playerConnection instanceof NettyPlayerConnection) {
- ((NettyPlayerConnection) playerConnection).getChannel().flush();
+ ((NettyPlayerConnection) playerConnection).flush();
}
//playerConnection.disconnect();
return;
@@ -350,9 +350,12 @@ public final class ConnectionManager {
final PlayerConnection playerConnection = player.getPlayerConnection();
if (playerConnection instanceof NettyPlayerConnection) {
final NettyPlayerConnection nettyPlayerConnection = (NettyPlayerConnection) playerConnection;
- final Channel channel = nettyPlayerConnection.getChannel();
- channel.writeAndFlush(disconnectPacket);
- channel.close();
+ nettyPlayerConnection.writeAndFlush(disconnectPacket);
+ try {
+ nettyPlayerConnection.getChannel().close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
}
this.players.clear();
diff --git a/src/main/java/net/minestom/server/network/PacketProcessor.java b/src/main/java/net/minestom/server/network/PacketProcessor.java
index c33f256b0..f89799602 100644
--- a/src/main/java/net/minestom/server/network/PacketProcessor.java
+++ b/src/main/java/net/minestom/server/network/PacketProcessor.java
@@ -1,7 +1,5 @@
package net.minestom.server.network;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.socket.SocketChannel;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.network.netty.packet.InboundPacket;
@@ -16,13 +14,9 @@ import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.Readable;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
/**
* Responsible for processing client packets.
*
@@ -34,11 +28,8 @@ import java.util.concurrent.ConcurrentHashMap;
* the same meaning as it is a login or play packet).
*/
public final class PacketProcessor {
-
private final static Logger LOGGER = LoggerFactory.getLogger(PacketProcessor.class);
- private final Map connectionPlayerConnectionMap = new ConcurrentHashMap<>();
-
// Protocols state
private final ClientStatusPacketsHandler statusPacketsHandler;
private final ClientLoginPacketsHandler loginPacketsHandler;
@@ -50,33 +41,15 @@ public final class PacketProcessor {
this.playPacketsHandler = new ClientPlayPacketsHandler();
}
- public void process(@NotNull ChannelHandlerContext context, @NotNull InboundPacket packet) {
- final SocketChannel socketChannel = (SocketChannel) context.channel();
-
- // Create the netty player connection object if not existing
- PlayerConnection playerConnection = connectionPlayerConnectionMap.get(context);
- if (playerConnection == null) {
- // Should never happen
- context.close();
- return;
- }
-
- // Prevent the client from sending packets when disconnected (kick)
- if (!playerConnection.isOnline() || !socketChannel.isActive()) {
- playerConnection.disconnect();
- return;
- }
-
+ public void process(@NotNull NettyPlayerConnection playerConnection, @NotNull InboundPacket packet) {
// Increment packet count (checked in PlayerConnection#update)
if (MinecraftServer.getRateLimit() > 0) {
playerConnection.getPacketCounter().incrementAndGet();
}
-
- final ConnectionState connectionState = playerConnection.getConnectionState();
-
final int packetId = packet.getPacketId();
BinaryReader binaryReader = new BinaryReader(packet.getBody());
+ final ConnectionState connectionState = playerConnection.getConnectionState();
if (connectionState == ConnectionState.UNKNOWN) {
// Should be handshake packet
if (packetId == 0) {
@@ -86,7 +59,6 @@ public final class PacketProcessor {
}
return;
}
-
switch (connectionState) {
case PLAY:
final Player player = playerConnection.getPlayer();
@@ -108,34 +80,13 @@ public final class PacketProcessor {
}
}
- /**
- * Retrieves a player connection from its channel.
- *
- * @param context the connection context
- * @return the connection of this channel, null if not found
- */
- @Nullable
- public PlayerConnection getPlayerConnection(ChannelHandlerContext context) {
- return connectionPlayerConnectionMap.get(context);
- }
-
- public void createPlayerConnection(@NotNull ChannelHandlerContext context) {
- final PlayerConnection playerConnection = new NettyPlayerConnection((SocketChannel) context.channel());
- connectionPlayerConnectionMap.put(context, playerConnection);
- }
-
- public PlayerConnection removePlayerConnection(@NotNull ChannelHandlerContext context) {
- return connectionPlayerConnectionMap.remove(context);
- }
-
/**
* Gets the handler for client status packets.
*
* @return the status packets handler
* @see Status packets
*/
- @NotNull
- public ClientStatusPacketsHandler getStatusPacketsHandler() {
+ public @NotNull ClientStatusPacketsHandler getStatusPacketsHandler() {
return statusPacketsHandler;
}
@@ -145,8 +96,7 @@ public final class PacketProcessor {
* @return the status login handler
* @see Login packets
*/
- @NotNull
- public ClientLoginPacketsHandler getLoginPacketsHandler() {
+ public @NotNull ClientLoginPacketsHandler getLoginPacketsHandler() {
return loginPacketsHandler;
}
@@ -156,8 +106,7 @@ public final class PacketProcessor {
* @return the play packets handler
* @see Play packets
*/
- @NotNull
- public ClientPlayPacketsHandler getPlayPacketsHandler() {
+ public @NotNull ClientPlayPacketsHandler getPlayPacketsHandler() {
return playPacketsHandler;
}
@@ -170,12 +119,10 @@ public final class PacketProcessor {
*/
private void safeRead(@NotNull PlayerConnection connection, @NotNull Readable readable, @NotNull BinaryReader reader) {
final int readableBytes = reader.available();
-
// Check if there is anything to read
if (readableBytes == 0) {
return;
}
-
try {
readable.read(reader);
} catch (Exception e) {
diff --git a/src/main/java/net/minestom/server/network/netty/NettyServer.java b/src/main/java/net/minestom/server/network/netty/NettyServer.java
deleted file mode 100644
index c9adc82b9..000000000
--- a/src/main/java/net/minestom/server/network/netty/NettyServer.java
+++ /dev/null
@@ -1,202 +0,0 @@
-package net.minestom.server.network.netty;
-
-import io.netty.bootstrap.ServerBootstrap;
-import io.netty.buffer.ByteBufAllocator;
-import io.netty.channel.*;
-import io.netty.channel.epoll.Epoll;
-import io.netty.channel.epoll.EpollEventLoopGroup;
-import io.netty.channel.epoll.EpollServerSocketChannel;
-import io.netty.channel.kqueue.KQueue;
-import io.netty.channel.kqueue.KQueueEventLoopGroup;
-import io.netty.channel.kqueue.KQueueServerSocketChannel;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.ServerSocketChannel;
-import io.netty.channel.socket.SocketChannel;
-import io.netty.channel.socket.nio.NioServerSocketChannel;
-import net.minestom.server.MinecraftServer;
-import net.minestom.server.network.PacketProcessor;
-import net.minestom.server.network.netty.channel.ClientChannel;
-import net.minestom.server.network.netty.codec.*;
-import net.minestom.server.utils.validate.Check;
-import org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.InetSocketAddress;
-
-@ApiStatus.Internal
-public final class NettyServer {
-
- public static final Logger LOGGER = LoggerFactory.getLogger(NettyServer.class);
- public static final int BUFFER_SIZE = Integer.getInteger("minestom.channel-buffer-size", 65535);
-
- private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
- 1 << 21);
-
- public static final String LEGACY_PING_HANDLER_NAME = "legacy-ping"; // Read
-
- public static final String ENCRYPT_HANDLER_NAME = "encrypt"; // Write
- public static final String DECRYPT_HANDLER_NAME = "decrypt"; // Read
-
- public static final String GROUPED_PACKET_HANDLER_NAME = "grouped-packet"; // Write
- public static final String FRAMER_HANDLER_NAME = "framer"; // Read/write
-
- public static final String COMPRESSOR_HANDLER_NAME = "compressor"; // Read/write
-
- public static final String DECODER_HANDLER_NAME = "decoder"; // Read
- public static final String ENCODER_HANDLER_NAME = "encoder"; // Write
- public static final String CLIENT_CHANNEL_NAME = "handler"; // Read
-
- private boolean initialized = false;
-
- private final PacketProcessor packetProcessor;
-
- private EventLoopGroup boss, worker;
- private ServerBootstrap bootstrap;
-
- private ServerSocketChannel serverChannel;
-
- private String address;
- private int port;
-
- public NettyServer(@NotNull PacketProcessor packetProcessor) {
- this.packetProcessor = packetProcessor;
- }
-
- /**
- * Inits the server by choosing which transport layer to use, number of threads, pipeline order, etc...
- *
- * Called just before {@link #start(String, int)}.
- */
- public void init() {
- Check.stateCondition(initialized, "Netty server has already been initialized!");
- this.initialized = true;
-
- Class extends ServerChannel> channel;
- final int workerThreadCount = MinecraftServer.getNettyThreadCount();
-
- // Find boss/worker event group
- {
- if (Epoll.isAvailable()) {
- boss = new EpollEventLoopGroup(2);
- worker = new EpollEventLoopGroup(workerThreadCount);
-
- channel = EpollServerSocketChannel.class;
-
- LOGGER.info("Using epoll");
- } else if (KQueue.isAvailable()) {
- boss = new KQueueEventLoopGroup(2);
- worker = new KQueueEventLoopGroup(workerThreadCount);
-
- channel = KQueueServerSocketChannel.class;
-
- LOGGER.info("Using kqueue");
- } else {
- boss = new NioEventLoopGroup(2);
- worker = new NioEventLoopGroup(workerThreadCount);
-
- channel = NioServerSocketChannel.class;
-
- LOGGER.info("Using NIO");
- }
- }
-
- bootstrap = new ServerBootstrap()
- .group(boss, worker)
- .childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
- .channel(channel);
-
-
- bootstrap.childHandler(new ChannelInitializer() {
- protected void initChannel(@NotNull SocketChannel ch) {
- ChannelConfig config = ch.config();
- config.setOption(ChannelOption.TCP_NODELAY, true);
- config.setOption(ChannelOption.SO_KEEPALIVE, true);
- config.setOption(ChannelOption.SO_SNDBUF, BUFFER_SIZE);
- config.setAllocator(ByteBufAllocator.DEFAULT);
-
- ChannelPipeline pipeline = ch.pipeline();
-
- // 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)
- pipeline.addLast(LEGACY_PING_HANDLER_NAME, new LegacyPingHandler());
-
- // Used to bypass all the previous handlers by directly sending a framed buffer
- pipeline.addLast(GROUPED_PACKET_HANDLER_NAME, new GroupedPacketHandler());
-
- // Adds packetLength at start | Reads framed buffer
- pipeline.addLast(FRAMER_HANDLER_NAME, new PacketFramer(packetProcessor));
-
- // Reads buffer and create inbound packet
- pipeline.addLast(DECODER_HANDLER_NAME, new PacketDecoder());
-
- // Writes packet to buffer
- pipeline.addLast(ENCODER_HANDLER_NAME, new PacketEncoder());
-
- pipeline.addLast(CLIENT_CHANNEL_NAME, new ClientChannel(packetProcessor));
- }
- });
- }
-
- /**
- * Binds the address to start the server.
- *
- * @param address the server address
- * @param port the server port
- */
- public void start(@NotNull String address, int port) {
- this.address = address;
- this.port = port;
-
- // Bind address
- try {
- ChannelFuture cf = bootstrap.bind(new InetSocketAddress(address, port)).sync();
-
- if (!cf.isSuccess()) {
- throw new IllegalStateException("Unable to bind server at " + address + ":" + port);
- }
-
- this.serverChannel = (ServerSocketChannel) cf.channel();
- } catch (InterruptedException ex) {
- MinecraftServer.getExceptionManager().handleException(ex);
- }
- }
-
- public ServerSocketChannel getServerChannel() {
- return serverChannel;
- }
-
- /**
- * Gets the address of the server.
- *
- * @return the server address, null if the address isn't bound yet
- */
- @Nullable
- public String getAddress() {
- return address;
- }
-
- /**
- * Gets the port used by the server.
- *
- * @return the server port, 0 if the address isn't bound yet
- */
- public int getPort() {
- return port;
- }
-
- /**
- * Stops the server.
- */
- public void stop() {
- try {
- this.boss.shutdownGracefully().sync();
- this.worker.shutdownGracefully().sync();
- this.serverChannel.closeFuture().sync();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-}
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
deleted file mode 100644
index 48bc2e0e1..000000000
--- a/src/main/java/net/minestom/server/network/netty/channel/ClientChannel.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package net.minestom.server.network.netty.channel;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.SimpleChannelInboundHandler;
-import net.minestom.server.MinecraftServer;
-import net.minestom.server.entity.Player;
-import net.minestom.server.network.ConnectionManager;
-import net.minestom.server.network.PacketProcessor;
-import net.minestom.server.network.netty.packet.InboundPacket;
-import net.minestom.server.network.player.NettyPlayerConnection;
-import net.minestom.server.network.player.PlayerConnection;
-import org.jetbrains.annotations.NotNull;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ClientChannel extends SimpleChannelInboundHandler {
-
- private final static Logger LOGGER = LoggerFactory.getLogger(ClientChannel.class);
-
- private final static ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
- private final PacketProcessor packetProcessor;
-
- public ClientChannel(@NotNull PacketProcessor packetProcessor) {
- this.packetProcessor = packetProcessor;
- }
-
- @Override
- public void channelActive(@NotNull ChannelHandlerContext ctx) {
- //System.out.println("CONNECTION");
- packetProcessor.createPlayerConnection(ctx);
- }
-
- @Override
- public void channelRead0(ChannelHandlerContext ctx, InboundPacket packet) {
- try {
- packetProcessor.process(ctx, packet);
- } catch (Exception e) {
- MinecraftServer.getExceptionManager().handleException(e);
- } finally {
- // Check remaining
- final ByteBuf body = packet.getBody();
- final int packetId = packet.getPacketId();
-
- final int availableBytes = body.readableBytes();
-
- if (availableBytes > 0) {
- final PlayerConnection playerConnection = packetProcessor.getPlayerConnection(ctx);
-
- LOGGER.warn("WARNING: Packet 0x{} not fully read ({} bytes left), {}",
- Integer.toHexString(packetId),
- availableBytes,
- playerConnection);
-
- body.skipBytes(availableBytes);
- }
- }
- }
-
- @Override
- public void channelInactive(@NotNull ChannelHandlerContext ctx) {
- PlayerConnection playerConnection = packetProcessor.removePlayerConnection(ctx);
- if (playerConnection != null) {
- // Remove the connection
- playerConnection.refreshOnline(false);
- Player player = playerConnection.getPlayer();
- if (player != null) {
- player.remove();
- CONNECTION_MANAGER.removePlayer(playerConnection);
- }
-
- // Release tick buffer
- if (playerConnection instanceof NettyPlayerConnection) {
- ((NettyPlayerConnection) playerConnection).releaseTickBuffer();
- }
- }
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
- if (!ctx.channel().isActive()) {
- return;
- }
-
- if (MinecraftServer.shouldProcessNettyErrors()) {
- MinecraftServer.getExceptionManager().handleException(cause);
- }
- ctx.close();
- }
-}
diff --git a/src/main/java/net/minestom/server/network/netty/codec/GroupedPacketHandler.java b/src/main/java/net/minestom/server/network/netty/codec/GroupedPacketHandler.java
deleted file mode 100644
index b11cfa118..000000000
--- a/src/main/java/net/minestom/server/network/netty/codec/GroupedPacketHandler.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package net.minestom.server.network.netty.codec;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToByteEncoder;
-import net.minestom.server.network.netty.packet.FramedPacket;
-
-public class GroupedPacketHandler extends MessageToByteEncoder {
-
- @Override
- protected void encode(ChannelHandlerContext ctx, FramedPacket msg, ByteBuf out) {
- }
-
- @Override
- protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, FramedPacket msg, boolean preferDirect) {
- return msg.getBody().retainedSlice();
- }
-
-}
diff --git a/src/main/java/net/minestom/server/network/netty/codec/LegacyPingHandler.java b/src/main/java/net/minestom/server/network/netty/codec/LegacyPingHandler.java
deleted file mode 100644
index 9631d5487..000000000
--- a/src/main/java/net/minestom/server/network/netty/codec/LegacyPingHandler.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package net.minestom.server.network.netty.codec;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import net.minestom.server.event.EventDispatcher;
-import net.minestom.server.event.server.ServerListPingEvent;
-import net.minestom.server.ping.ServerListPingType;
-import org.jetbrains.annotations.NotNull;
-
-import java.nio.charset.StandardCharsets;
-
-public class LegacyPingHandler extends ChannelInboundHandlerAdapter {
-
- private ByteBuf buf;
-
- @Override
- public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object object) {
- final ByteBuf buf = (ByteBuf) object;
-
- if (this.buf != null) {
- try {
- handle1_6(ctx, buf);
- } finally {
- buf.release();
- }
- return;
- }
-
- buf.markReaderIndex();
-
- boolean flag = true;
-
- try {
- if (buf.readUnsignedByte() == 0xFE) {
- int length = buf.readableBytes();
-
- switch (length) {
- case 0:
- if (trySendResponse(ServerListPingType.LEGACY_UNVERSIONED, ctx)) return;
- break;
- case 1:
- if (buf.readUnsignedByte() != 1) return;
-
- if (trySendResponse(ServerListPingType.LEGACY_VERSIONED, ctx)) return;
- break;
- default:
- if (buf.readUnsignedByte() != 0x01 || buf.readUnsignedByte() != 0xFA) return;
-
- handle1_6(ctx, buf);
- break;
- }
-
- buf.release();
- flag = false;
- }
- } finally {
- if (flag) {
- buf.resetReaderIndex();
- ctx.channel().pipeline().remove("legacy-ping");
- ctx.fireChannelRead(object);
- }
- }
- }
-
- private void handle1_6(ChannelHandlerContext ctx, ByteBuf part) {
- ByteBuf buf = this.buf;
-
- if (buf == null) {
- this.buf = buf = ctx.alloc().buffer();
- buf.markReaderIndex();
- } else {
- buf.resetReaderIndex();
- }
-
- buf.writeBytes(part);
-
- if (!buf.isReadable(Short.BYTES + Short.BYTES + Byte.BYTES + Short.BYTES + Integer.BYTES)) {
- return;
- }
-
- final String s = readLegacyString(buf);
-
- if (s == null) {
- return;
- }
-
- if (!s.equals("MC|PingHost")) {
- removeHandler(ctx);
- return;
- }
-
- if (!buf.isReadable(Short.BYTES) || !buf.isReadable(buf.readShort())) {
- return;
- }
-
- int protocolVersion = buf.readByte();
-
- if (readLegacyString(buf) == null) {
- removeHandler(ctx);
- return;
- }
-
- buf.skipBytes(4); // port
-
- if (buf.isReadable()) {
- removeHandler(ctx);
- return;
- }
-
- buf.release();
-
- this.buf = null;
-
- trySendResponse(ServerListPingType.LEGACY_VERSIONED, ctx);
- }
-
- private void removeHandler(ChannelHandlerContext ctx) {
- ByteBuf buf = this.buf;
- this.buf = null;
-
- buf.resetReaderIndex();
- ctx.pipeline().remove(this);
- ctx.fireChannelRead(buf);
- }
-
- @Override
- public void handlerRemoved(ChannelHandlerContext ctx) {
- if (this.buf != null) {
- this.buf.release();
- this.buf = null;
- }
- }
-
- /**
- * Calls a {@link ServerListPingEvent} and sends the response, if the event was not cancelled.
- *
- * @param version the version
- * @param ctx the context
- * @return {@code true} if the response was cancelled, {@code false} otherwise
- */
- private static boolean trySendResponse(@NotNull ServerListPingType version, @NotNull ChannelHandlerContext ctx) {
- final ServerListPingEvent event = new ServerListPingEvent(version);
- EventDispatcher.call(event);
-
- if (event.isCancelled()) {
- return true;
- } else {
- // get the response string
- String s = version.getPingResponse(event.getResponseData());
-
- // create the buffer
- ByteBuf response = Unpooled.buffer();
- response.writeByte(255);
-
- final char[] chars = s.toCharArray();
-
- response.writeShort(chars.length);
-
- for (char c : chars) {
- response.writeChar(c);
- }
-
- // write the buffer
- ctx.pipeline().firstContext().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
-
- return false;
- }
- }
-
- private static String readLegacyString(ByteBuf buf) {
- int size = buf.readShort() * Character.BYTES;
- if (!buf.isReadable(size)) {
- return null;
- }
-
- final String result = buf.toString(buf.readerIndex(), size, StandardCharsets.UTF_16BE);
- buf.skipBytes(size);
-
- return result;
- }
-}
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
deleted file mode 100644
index 4ba5b0ef0..000000000
--- a/src/main/java/net/minestom/server/network/netty/codec/PacketCompressor.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (2020) [artem]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.minestom.server.network.netty.codec;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageCodec;
-import io.netty.handler.codec.DecoderException;
-import net.minestom.server.utils.PacketUtils;
-import net.minestom.server.utils.Utils;
-
-import java.util.List;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
-
-public class PacketCompressor extends ByteToMessageCodec {
-
- private final static int MAX_SIZE = 2097152;
-
- private final int threshold;
-
- private final Deflater deflater = new Deflater();
- private final Inflater inflater = new Inflater();
-
- public PacketCompressor(int threshold) {
- this.threshold = threshold;
- }
-
- @Override
- protected void encode(ChannelHandlerContext ctx, ByteBuf from, ByteBuf to) {
- PacketUtils.compressBuffer(deflater, from, to);
- }
-
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
- if (in.readableBytes() != 0) {
- final int claimedUncompressedSize = Utils.readVarInt(in);
-
- if (claimedUncompressedSize == 0) {
- out.add(in.readRetainedSlice(in.readableBytes()));
- } else {
- if (claimedUncompressedSize < this.threshold) {
- throw new DecoderException("Badly compressed packet - size of " + claimedUncompressedSize + " is below server threshold of " + this.threshold);
- }
-
- if (claimedUncompressedSize > MAX_SIZE) {
- throw new DecoderException("Badly compressed packet - size of " + claimedUncompressedSize + " is larger than protocol maximum of " + MAX_SIZE);
- }
-
- // TODO optimize to do not initialize arrays each time
-
- byte[] input = new byte[in.readableBytes()];
- in.readBytes(input);
-
- inflater.setInput(input);
- byte[] output = new byte[claimedUncompressedSize];
- inflater.inflate(output);
- inflater.reset();
-
- out.add(Unpooled.wrappedBuffer(output));
- }
- }
- }
-}
diff --git a/src/main/java/net/minestom/server/network/netty/codec/PacketDecoder.java b/src/main/java/net/minestom/server/network/netty/codec/PacketDecoder.java
deleted file mode 100644
index 3f5f669bc..000000000
--- a/src/main/java/net/minestom/server/network/netty/codec/PacketDecoder.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package net.minestom.server.network.netty.codec;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageDecoder;
-import net.minestom.server.network.netty.packet.InboundPacket;
-import net.minestom.server.utils.Utils;
-
-import java.util.List;
-
-public class PacketDecoder extends ByteToMessageDecoder {
-
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List list) {
- if (buf.readableBytes() > 0) {
- final int packetId = Utils.readVarInt(buf);
- list.add(new InboundPacket(packetId, buf));
- }
- }
-}
diff --git a/src/main/java/net/minestom/server/network/netty/codec/PacketEncoder.java b/src/main/java/net/minestom/server/network/netty/codec/PacketEncoder.java
deleted file mode 100644
index 85781bc4d..000000000
--- a/src/main/java/net/minestom/server/network/netty/codec/PacketEncoder.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.minestom.server.network.netty.codec;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.MessageToByteEncoder;
-import net.minestom.server.network.packet.server.ServerPacket;
-import net.minestom.server.utils.PacketUtils;
-
-public class PacketEncoder extends MessageToByteEncoder {
-
- @Override
- protected void encode(ChannelHandlerContext ctx, ServerPacket packet, ByteBuf buf) {
- PacketUtils.writePacket(buf, packet);
- }
-
-}
diff --git a/src/main/java/net/minestom/server/network/netty/codec/PacketFramer.java b/src/main/java/net/minestom/server/network/netty/codec/PacketFramer.java
deleted file mode 100644
index d0470e7cf..000000000
--- a/src/main/java/net/minestom/server/network/netty/codec/PacketFramer.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package net.minestom.server.network.netty.codec;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageCodec;
-import io.netty.handler.codec.CorruptedFrameException;
-import net.minestom.server.MinecraftServer;
-import net.minestom.server.network.PacketProcessor;
-import net.minestom.server.network.player.PlayerConnection;
-import net.minestom.server.utils.PacketUtils;
-import net.minestom.server.utils.Utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-
-public class PacketFramer extends ByteToMessageCodec {
-
- public final static Logger LOGGER = LoggerFactory.getLogger(PacketFramer.class);
-
- private final PacketProcessor packetProcessor;
-
- public PacketFramer(PacketProcessor packetProcessor) {
- this.packetProcessor = packetProcessor;
- }
-
- @Override
- protected void encode(ChannelHandlerContext ctx, ByteBuf from, ByteBuf to) {
- PacketUtils.frameBuffer(from, to);
- }
-
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List out) {
- buf.markReaderIndex();
-
- for (int i = 0; i < 3; ++i) {
- if (!buf.isReadable()) {
- buf.resetReaderIndex();
- return;
- }
-
- final byte b = buf.readByte();
-
- if (b >= 0) {
- buf.resetReaderIndex();
-
- final int packetSize = Utils.readVarInt(buf);
-
- // Max packet size check
- if (packetSize >= MinecraftServer.getMaxPacketSize()) {
- final PlayerConnection playerConnection = packetProcessor.getPlayerConnection(ctx);
- if (playerConnection != null) {
- final String identifier = playerConnection.getIdentifier();
- LOGGER.warn("An user ({}) sent a packet over the maximum size ({})",
- identifier, packetSize);
- } else {
- LOGGER.warn("An unregistered user sent a packet over the maximum size ({})", packetSize);
- }
- ctx.close();
- }
-
- if (buf.readableBytes() < packetSize) {
- buf.resetReaderIndex();
- return;
- }
-
- out.add(buf.readRetainedSlice(packetSize));
- return;
- }
- }
-
- throw new CorruptedFrameException("length wider than 21-bit");
- }
-}
diff --git a/src/main/java/net/minestom/server/network/netty/packet/FramedPacket.java b/src/main/java/net/minestom/server/network/netty/packet/FramedPacket.java
index 8197301ff..17fe194a7 100644
--- a/src/main/java/net/minestom/server/network/netty/packet/FramedPacket.java
+++ b/src/main/java/net/minestom/server/network/netty/packet/FramedPacket.java
@@ -1,22 +1,22 @@
package net.minestom.server.network.netty.packet;
-import io.netty.buffer.ByteBuf;
import org.jetbrains.annotations.NotNull;
+import java.nio.ByteBuffer;
+
/**
* Represents a packet which is already framed. (packet id+payload) + optional compression
* Can be used if you want to send the exact same buffer to multiple clients without processing it more than once.
*/
public class FramedPacket {
- private final ByteBuf body;
+ private final ByteBuffer body;
- public FramedPacket(@NotNull ByteBuf body) {
+ public FramedPacket(@NotNull ByteBuffer body) {
this.body = body;
}
- @NotNull
- public ByteBuf getBody() {
+ public @NotNull ByteBuffer getBody() {
return body;
}
}
diff --git a/src/main/java/net/minestom/server/network/netty/packet/InboundPacket.java b/src/main/java/net/minestom/server/network/netty/packet/InboundPacket.java
index 782c6731d..51ae25550 100644
--- a/src/main/java/net/minestom/server/network/netty/packet/InboundPacket.java
+++ b/src/main/java/net/minestom/server/network/netty/packet/InboundPacket.java
@@ -1,14 +1,14 @@
package net.minestom.server.network.netty.packet;
-import io.netty.buffer.ByteBuf;
import org.jetbrains.annotations.NotNull;
+import java.nio.ByteBuffer;
+
public class InboundPacket {
-
private final int packetId;
- private final ByteBuf body;
+ private final ByteBuffer body;
- public InboundPacket(int id, @NotNull ByteBuf body) {
+ public InboundPacket(int id, @NotNull ByteBuffer body) {
this.packetId = id;
this.body = body;
}
@@ -17,8 +17,7 @@ public class InboundPacket {
return packetId;
}
- @NotNull
- public ByteBuf getBody() {
+ public @NotNull ByteBuffer getBody() {
return body;
}
}
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 739c02f0a..5fc4b7e2f 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
@@ -1,7 +1,5 @@
package net.minestom.server.network.packet.server.play;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.Int2LongRBTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minestom.server.MinecraftServer;
@@ -15,22 +13,17 @@ import net.minestom.server.tag.Tag;
import net.minestom.server.utils.Utils;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
-import net.minestom.server.utils.cache.CacheablePacket;
-import net.minestom.server.utils.cache.TemporaryPacketCache;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.world.biomes.Biome;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.*;
-import java.util.concurrent.TimeUnit;
-public class ChunkDataPacket implements ServerPacket, CacheablePacket {
-
- public static final TemporaryPacketCache CACHE = new TemporaryPacketCache(5, TimeUnit.MINUTES);
+public class ChunkDataPacket implements ServerPacket {
public Biome[] biomes;
public int chunkX, chunkZ;
@@ -42,22 +35,13 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
private static final int MAX_BITS_PER_ENTRY = 16;
private static final int MAX_BUFFER_SIZE = (Short.BYTES + Byte.BYTES + 5 * Byte.BYTES + (4096 * MAX_BITS_PER_ENTRY / Long.SIZE * Long.BYTES)) * CHUNK_SECTION_COUNT + 256 * Integer.BYTES;
- // Cacheable data
- private final UUID identifier;
- private final long timestamp;
/**
* Heightmaps NBT, as read from raw packet data.
* Only filled by #read, and unused at the moment.
*/
public NBTCompound heightmapsNBT;
- private ChunkDataPacket() {
- this(new UUID(0, 0), 0);
- }
-
- public ChunkDataPacket(@Nullable UUID identifier, long timestamp) {
- this.identifier = identifier;
- this.timestamp = timestamp;
+ public ChunkDataPacket() {
}
@Override
@@ -65,7 +49,7 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
writer.writeInt(chunkX);
writer.writeInt(chunkZ);
- ByteBuf blocks = Unpooled.buffer(MAX_BUFFER_SIZE);
+ ByteBuffer blocks = ByteBuffer.allocate(MAX_BUFFER_SIZE);
Int2LongRBTreeMap maskMap = new Int2LongRBTreeMap();
@@ -120,9 +104,8 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
}
// Data
- writer.writeVarInt(blocks.writerIndex());
- writer.write(blocks);
- blocks.release();
+ writer.writeVarInt(blocks.position());
+ writer.write(blocks.flip());
// Block entities
if (entries == null || entries.isEmpty()) {
@@ -244,19 +227,4 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
public int getId() {
return ServerPacketIdentifier.CHUNK_DATA;
}
-
- @Override
- public @NotNull TemporaryPacketCache getCache() {
- return CACHE;
- }
-
- @Override
- public UUID getIdentifier() {
- return identifier;
- }
-
- @Override
- public long getTimestamp() {
- return timestamp;
- }
}
\ No newline at end of file
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 b5ba54808..cdffdf9d9 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
@@ -4,19 +4,12 @@ import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
-import net.minestom.server.utils.cache.CacheablePacket;
-import net.minestom.server.utils.cache.TemporaryPacketCache;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-public class UpdateLightPacket implements ServerPacket, CacheablePacket {
-
- public static final TemporaryPacketCache CACHE = new TemporaryPacketCache(5, TimeUnit.MINUTES);
+public class UpdateLightPacket implements ServerPacket {
public int chunkX;
public int chunkZ;
@@ -32,21 +25,10 @@ public class UpdateLightPacket implements ServerPacket, CacheablePacket {
public List skyLight = new ArrayList<>();
public List blockLight = new ArrayList<>();
- // Cacheable data
- private final UUID identifier;
- private final long timestamp;
-
/**
* Default constructor, required for reflection operations.
- * This one will make a packet that is not meant to be cached
*/
public UpdateLightPacket() {
- this(UUID.randomUUID(), Long.MAX_VALUE);
- }
-
- public UpdateLightPacket(@Nullable UUID identifier, long timestamp) {
- this.identifier = identifier;
- this.timestamp = timestamp;
}
@Override
@@ -118,19 +100,4 @@ public class UpdateLightPacket implements ServerPacket, CacheablePacket {
public int getId() {
return ServerPacketIdentifier.UPDATE_LIGHT;
}
-
- @Override
- public @NotNull TemporaryPacketCache getCache() {
- return CACHE;
- }
-
- @Override
- public UUID getIdentifier() {
- return identifier;
- }
-
- @Override
- public long getTimestamp() {
- return timestamp;
- }
}
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 9963b84c8..9ecfc5544 100644
--- a/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java
+++ b/src/main/java/net/minestom/server/network/player/NettyPlayerConnection.java
@@ -1,36 +1,36 @@
package net.minestom.server.network.player;
-import io.netty.buffer.ByteBuf;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.socket.SocketChannel;
import net.kyori.adventure.translation.GlobalTranslator;
import net.minestom.server.MinecraftServer;
import net.minestom.server.adventure.MinestomAdventure;
import net.minestom.server.entity.PlayerSkin;
-import net.minestom.server.extras.mojangAuth.Decrypter;
-import net.minestom.server.extras.mojangAuth.Encrypter;
-import net.minestom.server.extras.mojangAuth.MojangCrypt;
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.PacketProcessor;
import net.minestom.server.network.netty.packet.FramedPacket;
+import net.minestom.server.network.netty.packet.InboundPacket;
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;
+import net.minestom.server.network.socket.Server;
+import net.minestom.server.network.socket.Worker;
import net.minestom.server.utils.PacketUtils;
-import net.minestom.server.utils.cache.CacheablePacket;
+import net.minestom.server.utils.Utils;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.crypto.SecretKey;
+import java.io.IOException;
import java.net.SocketAddress;
+import java.nio.BufferOverflowException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.DataFormatException;
/**
* Represents a networking connection with Netty.
@@ -43,8 +43,8 @@ public class NettyPlayerConnection extends PlayerConnection {
private SocketAddress remoteAddress;
- private boolean encrypted = false;
- private boolean compressed = false;
+ private volatile boolean encrypted = false;
+ private volatile boolean compressed = false;
//Could be null. Only used for Mojang Auth
private byte[] nonce = new byte[4];
@@ -64,12 +64,89 @@ public class NettyPlayerConnection extends PlayerConnection {
private PlayerSkin bungeeSkin;
private final Object tickBufferLock = new Object();
- private volatile ByteBuf tickBuffer = BufUtils.direct();
+ private final ByteBuffer tickBuffer = ByteBuffer.allocateDirect(Server.SOCKET_BUFFER_SIZE);
+ private ByteBuffer cacheBuffer;
public NettyPlayerConnection(@NotNull SocketChannel channel) {
super();
this.channel = channel;
- this.remoteAddress = channel.remoteAddress();
+ try {
+ this.remoteAddress = channel.getRemoteAddress();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void processPackets(Worker.Context workerContext, PacketProcessor packetProcessor) {
+ final var readBuffer = workerContext.readBuffer;
+ final int limit = readBuffer.limit();
+ // Read all packets
+ while (readBuffer.remaining() > 0) {
+ readBuffer.mark(); // Mark the beginning of the packet
+ try {
+ // Read packet
+ final int packetLength = Utils.readVarInt(readBuffer);
+ final int packetEnd = readBuffer.position() + packetLength;
+ if (packetEnd > readBuffer.limit()) {
+ // Integrity fail
+ throw new BufferUnderflowException();
+ }
+
+ readBuffer.limit(packetEnd); // Ensure that the reader doesn't exceed packet bound
+
+ // Read protocol
+ var content = workerContext.contentBuffer.clear();
+ {
+ if (!compressed) {
+ // Compression disabled, payload is following
+ content = readBuffer;
+ } else {
+ final int dataLength = Utils.readVarInt(readBuffer);
+ if (dataLength == 0) {
+ // Data is too small to be compressed, payload is following
+ content = readBuffer;
+ } else {
+ // Decompress to content buffer
+ try {
+ final var inflater = workerContext.inflater;
+ inflater.setInput(readBuffer);
+ inflater.inflate(content);
+ inflater.reset();
+ } catch (DataFormatException e) {
+ e.printStackTrace();
+ }
+ content.flip();
+ }
+ }
+ }
+
+ // Process packet
+ final int packetId = Utils.readVarInt(content);
+ try {
+ packetProcessor.process(this, new InboundPacket(packetId, content));
+ } catch (Exception e) {
+ // Error while reading the packet
+ e.printStackTrace();
+ break;
+ }
+
+ // Return to original state (before writing)
+ readBuffer.limit(limit).position(packetEnd);
+ } catch (BufferUnderflowException e) {
+ readBuffer.reset();
+ this.cacheBuffer = ByteBuffer.allocateDirect(readBuffer.remaining());
+ this.cacheBuffer.put(readBuffer).flip();
+ break;
+ }
+ }
+ }
+
+ public void consumeCache(ByteBuffer buffer) {
+ if (cacheBuffer == null) {
+ return;
+ }
+ buffer.put(cacheBuffer);
+ this.cacheBuffer = null;
}
/**
@@ -81,10 +158,7 @@ public class NettyPlayerConnection extends PlayerConnection {
public void setEncryptionKey(@NotNull SecretKey secretKey) {
Check.stateCondition(encrypted, "Encryption is already enabled!");
this.encrypted = true;
- channel.pipeline().addBefore(NettyServer.GROUPED_PACKET_HANDLER_NAME, NettyServer.DECRYPT_HANDLER_NAME,
- new Decrypter(MojangCrypt.getCipher(2, secretKey)));
- channel.pipeline().addBefore(NettyServer.GROUPED_PACKET_HANDLER_NAME, NettyServer.ENCRYPT_HANDLER_NAME,
- new Encrypter(MojangCrypt.getCipher(1, secretKey)));
+ // TODO
}
/**
@@ -96,11 +170,8 @@ public class NettyPlayerConnection extends PlayerConnection {
Check.stateCondition(compressed, "Compression is already enabled!");
final int threshold = MinecraftServer.getCompressionThreshold();
Check.stateCondition(threshold == 0, "Compression cannot be enabled because the threshold is equal to 0");
-
this.compressed = true;
writeAndFlush(new SetCompressionPacket(threshold));
- channel.pipeline().addAfter(NettyServer.FRAMER_HANDLER_NAME, NettyServer.COMPRESSOR_HANDLER_NAME,
- new PacketCompressor(threshold));
}
/**
@@ -112,21 +183,12 @@ public class NettyPlayerConnection extends PlayerConnection {
*/
@Override
public void sendPacket(@NotNull ServerPacket serverPacket, boolean skipTranslating) {
- if (!channel.isActive())
+ if (!channel.isConnected())
return;
-
if (shouldSendPacket(serverPacket)) {
if (getPlayer() != null) {
// Flush happen during #update()
- if (serverPacket instanceof CacheablePacket && MinecraftServer.hasPacketCaching()) {
- synchronized (tickBufferLock) {
- if (tickBuffer.refCnt() == 0)
- return;
- CacheablePacket.writeCache(tickBuffer, serverPacket);
- }
- } else {
- write(serverPacket, skipTranslating);
- }
+ write(serverPacket, skipTranslating);
} else {
// Player is probably not logged yet
writeAndFlush(serverPacket);
@@ -141,93 +203,65 @@ public class NettyPlayerConnection extends PlayerConnection {
public void write(@NotNull Object message, boolean skipTranslating) {
if (message instanceof FramedPacket) {
final FramedPacket framedPacket = (FramedPacket) message;
- synchronized (tickBufferLock) {
- if (tickBuffer.refCnt() == 0)
- return;
- final ByteBuf body = framedPacket.getBody();
- tickBuffer.writeBytes(body, body.readerIndex(), body.readableBytes());
- }
+ attemptWrite(framedPacket.getBody());
return;
} else if (message instanceof ServerPacket) {
ServerPacket serverPacket = (ServerPacket) message;
-
if ((MinestomAdventure.AUTOMATIC_COMPONENT_TRANSLATION && !skipTranslating) && getPlayer() != null && serverPacket instanceof ComponentHoldingServerPacket) {
serverPacket = ((ComponentHoldingServerPacket) serverPacket).copyWithOperator(component ->
GlobalTranslator.render(component, Objects.requireNonNullElseGet(getPlayer().getLocale(), MinestomAdventure::getDefaultLocale)));
}
-
synchronized (tickBufferLock) {
- if (tickBuffer.refCnt() == 0)
- return;
PacketUtils.writeFramedPacket(tickBuffer, serverPacket);
}
return;
- } else if (message instanceof ByteBuf) {
- synchronized (tickBufferLock) {
- if (tickBuffer.refCnt() == 0)
- return;
- tickBuffer.writeBytes((ByteBuf) message);
- }
+ } else if (message instanceof ByteBuffer) {
+ attemptWrite((ByteBuffer) message);
return;
}
throw new UnsupportedOperationException("type " + message.getClass() + " is not supported");
}
- public void writeAndFlush(@NotNull Object message) {
- writeWaitingPackets();
- ChannelFuture channelFuture = channel.writeAndFlush(message);
-
- if (MinecraftServer.shouldProcessNettyErrors()) {
- channelFuture.addListener(future -> {
- if (!future.isSuccess() && channel.isActive()) {
- MinecraftServer.getExceptionManager().handleException(future.cause());
- }
- });
- }
+ public void writeAndFlush(@NotNull ServerPacket packet) {
+ attemptWrite(PacketUtils.createFramedPacket(packet));
+ flush();
}
- public void writeWaitingPackets() {
- if (tickBuffer.writerIndex() == 0) {
- // Nothing to write
- return;
- }
-
- // Retrieve safe copy
- final ByteBuf copy;
+ public void attemptWrite(ByteBuffer buffer) {
synchronized (tickBufferLock) {
- if (tickBuffer.refCnt() == 0)
- return;
- copy = tickBuffer;
- tickBuffer = tickBuffer.alloc().buffer(tickBuffer.writerIndex());
- }
-
- // Write copied buffer to netty
- ChannelFuture channelFuture = channel.write(new FramedPacket(copy));
- channelFuture.addListener(future -> copy.release());
-
- // Netty debug
- if (MinecraftServer.shouldProcessNettyErrors()) {
- channelFuture.addListener(future -> {
- if (!future.isSuccess() && channel.isActive()) {
- MinecraftServer.getExceptionManager().handleException(future.cause());
+ try {
+ this.tickBuffer.put(buffer);
+ } catch (BufferOverflowException e) {
+ try {
+ this.channel.write(tickBuffer);
+ this.channel.write(buffer);
+ } catch (IOException ex) {
+ MinecraftServer.getExceptionManager().handleException(ex);
+ } finally {
+ this.tickBuffer.clear();
}
- });
- }
- }
-
- public void flush() {
- final int bufferSize = tickBuffer.writerIndex();
- if (bufferSize > 0) {
- if (channel.isActive()) {
- writeWaitingPackets();
- channel.flush();
}
}
}
- @NotNull
+ public void flush() {
+ if (tickBuffer.remaining() == 0) {
+ // Nothing to write
+ return;
+ }
+ // Retrieve safe copy
+ synchronized (tickBufferLock) {
+ try {
+ channel.write(tickBuffer);
+ } catch (IOException e) {
+ MinecraftServer.getExceptionManager().handleException(e);
+ }
+ this.tickBuffer.clear();
+ }
+ }
+
@Override
- public SocketAddress getRemoteAddress() {
+ public @NotNull SocketAddress getRemoteAddress() {
return remoteAddress;
}
@@ -246,11 +280,14 @@ public class NettyPlayerConnection extends PlayerConnection {
@Override
public void disconnect() {
refreshOnline(false);
- this.channel.close();
+ try {
+ this.channel.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
- @NotNull
- public Channel getChannel() {
+ public @NotNull SocketChannel getChannel() {
return channel;
}
@@ -261,8 +298,7 @@ public class NettyPlayerConnection extends PlayerConnection {
*
* @return the username given by the client, unchecked
*/
- @Nullable
- public String getLoginUsername() {
+ public @Nullable String getLoginUsername() {
return loginUsername;
}
@@ -323,9 +359,7 @@ public class NettyPlayerConnection extends PlayerConnection {
this.protocolVersion = protocolVersion;
}
-
- @Nullable
- public UUID getBungeeUuid() {
+ public @Nullable UUID getBungeeUuid() {
return bungeeUuid;
}
@@ -333,8 +367,7 @@ public class NettyPlayerConnection extends PlayerConnection {
this.bungeeUuid = bungeeUuid;
}
- @Nullable
- public PlayerSkin getBungeeSkin() {
+ public @Nullable PlayerSkin getBungeeSkin() {
return bungeeSkin;
}
@@ -367,8 +400,7 @@ public class NettyPlayerConnection extends PlayerConnection {
* @param messageId the message id
* @return the channel linked to the message id, null if not found
*/
- @Nullable
- public String getPluginRequestChannel(int messageId) {
+ public @Nullable String getPluginRequestChannel(int messageId) {
return pluginRequestMap.get(messageId);
}
@@ -382,9 +414,6 @@ public class NettyPlayerConnection extends PlayerConnection {
}
public void releaseTickBuffer() {
- synchronized (tickBufferLock) {
- tickBuffer.release();
- }
}
public byte[] getNonce() {
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 6fc228b73..28feb23b5 100644
--- a/src/main/java/net/minestom/server/network/player/PlayerConnection.java
+++ b/src/main/java/net/minestom/server/network/player/PlayerConnection.java
@@ -135,7 +135,7 @@ public abstract class PlayerConnection {
* @return the server address used
*/
public @Nullable String getServerAddress() {
- return MinecraftServer.getNettyServer().getAddress();
+ return MinecraftServer.getServer().getAddress();
}
@@ -147,7 +147,7 @@ public abstract class PlayerConnection {
* @return the server port used
*/
public int getServerPort() {
- return MinecraftServer.getNettyServer().getPort();
+ return MinecraftServer.getServer().getPort();
}
/**
diff --git a/src/main/java/net/minestom/server/network/socket/Server.java b/src/main/java/net/minestom/server/network/socket/Server.java
new file mode 100644
index 000000000..b5c98dbf9
--- /dev/null
+++ b/src/main/java/net/minestom/server/network/socket/Server.java
@@ -0,0 +1,97 @@
+package net.minestom.server.network.socket;
+
+import net.minestom.server.network.PacketProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class Server {
+ public static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
+ public static final int WORKER_COUNT = Integer.getInteger("minestom.workers",
+ Runtime.getRuntime().availableProcessors() * 2);
+ public static final int SOCKET_BUFFER_SIZE = Integer.getInteger("minestom.buffer-size", 262143);
+ public static final int MAX_PACKET_SIZE = 2097151; // 3 bytes var-int
+ public static final boolean NO_DELAY = true;
+
+ private volatile boolean stop;
+
+ private final List workers = new ArrayList<>(WORKER_COUNT);
+ private int index;
+
+ private ServerSocketChannel serverSocket;
+ private String address;
+ private int port;
+
+ public Server(PacketProcessor packetProcessor) throws IOException {
+ // Create all workers
+ for (int i = 0; i < WORKER_COUNT; i++) {
+ this.workers.add(new Worker(this, packetProcessor));
+ }
+ }
+
+ public void start(SocketAddress address) throws IOException {
+ Selector selector = Selector.open();
+ this.serverSocket = ServerSocketChannel.open();
+ serverSocket.bind(address);
+ serverSocket.configureBlocking(false);
+ serverSocket.register(selector, SelectionKey.OP_ACCEPT);
+ serverSocket.socket().setReceiveBufferSize(SOCKET_BUFFER_SIZE);
+
+ LOGGER.info("Server starting, wait for connections");
+ new Thread(() -> {
+ while (!stop) {
+ // Busy wait for connections
+ try {
+ serverTick(selector, serverSocket);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ }
+
+ public boolean isOpen() {
+ return !stop;
+ }
+
+ public void stop() {
+ this.stop = true;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ private void serverTick(Selector selector, ServerSocketChannel socketChannel) throws IOException {
+ selector.select();
+ Set selectedKeys = selector.selectedKeys();
+ for (SelectionKey key : selectedKeys) {
+ if (key.isAcceptable()) {
+ // Register socket and forward to thread
+ Worker thread = findWorker();
+ final SocketChannel client = socketChannel.accept();
+ thread.receiveConnection(client);
+ LOGGER.info("new connection: " + client);
+ }
+ }
+ selectedKeys.clear();
+ }
+
+ private Worker findWorker() {
+ this.index = ++index % WORKER_COUNT;
+ return workers.get(index);
+ }
+}
diff --git a/src/main/java/net/minestom/server/network/socket/Worker.java b/src/main/java/net/minestom/server/network/socket/Worker.java
new file mode 100644
index 000000000..9e89d5396
--- /dev/null
+++ b/src/main/java/net/minestom/server/network/socket/Worker.java
@@ -0,0 +1,149 @@
+package net.minestom.server.network.socket;
+
+import net.minestom.server.MinecraftServer;
+import net.minestom.server.entity.Player;
+import net.minestom.server.network.PacketProcessor;
+import net.minestom.server.network.player.NettyPlayerConnection;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+public class Worker {
+ private final Map connectionMap = new ConcurrentHashMap<>();
+ private final Selector selector = Selector.open();
+ private final PacketProcessor packetProcessor;
+
+ public Worker(Server server, PacketProcessor packetProcessor) throws IOException {
+ this.packetProcessor = packetProcessor;
+ Thread.start(server, this::threadTick);
+ }
+
+ private void threadTick(Context workerContext) {
+ try {
+ selector.select();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ }
+ Set selectedKeys = selector.selectedKeys();
+ for (SelectionKey key : selectedKeys) {
+ SocketChannel channel = (SocketChannel) key.channel();
+ if (!channel.isOpen()) {
+ continue;
+ }
+ if (!key.isReadable()) {
+ // We only care about read
+ continue;
+ }
+ var connection = connectionMap.get(channel);
+ try {
+ ByteBuffer readBuffer = workerContext.readBuffer;
+ // Consume last incomplete packet
+ connection.consumeCache(readBuffer);
+
+ // Read socket
+ if (channel.read(readBuffer) == -1) {
+ // EOS
+ throw new IOException("Disconnected");
+ }
+ // Process data
+ readBuffer.flip();
+ connection.processPackets(workerContext, packetProcessor);
+ } catch (IOException e) {
+ e.printStackTrace();
+ try {
+ disconnect(connection, channel);
+ } catch (IOException ioException) {
+ ioException.printStackTrace();
+ }
+ } finally {
+ workerContext.clearBuffers();
+ }
+ }
+ selectedKeys.clear();
+ }
+
+ public void receiveConnection(SocketChannel channel) throws IOException {
+ var connection = new NettyPlayerConnection(channel);
+ this.connectionMap.put(channel, connection);
+ register(channel);
+ this.selector.wakeup();
+ }
+
+ private void register(SocketChannel channel) throws IOException {
+ channel.configureBlocking(false);
+ channel.register(selector, SelectionKey.OP_READ);
+ var socket = channel.socket();
+ socket.setSendBufferSize(Server.SOCKET_BUFFER_SIZE);
+ socket.setReceiveBufferSize(Server.SOCKET_BUFFER_SIZE);
+ socket.setTcpNoDelay(Server.NO_DELAY);
+ }
+
+ private void disconnect(NettyPlayerConnection connection, SocketChannel channel) throws IOException {
+ // Client close
+ channel.close();
+ connectionMap.remove(channel);
+ // Remove the connection
+ connection.refreshOnline(false);
+ Player player = connection.getPlayer();
+ if (player != null) {
+ player.remove();
+ MinecraftServer.getConnectionManager().removePlayer(connection);
+ }
+ }
+
+ static class Thread extends java.lang.Thread {
+ private static final AtomicInteger COUNTER = new AtomicInteger();
+
+ private Thread(Runnable runnable) {
+ super(null, runnable, "worker-" + COUNTER.getAndIncrement());
+ }
+
+ protected static void start(Server server, Consumer runnable) {
+ new Thread(() -> {
+ Context workerContext = new Context();
+ while (server.isOpen()) {
+ try {
+ runnable.accept(workerContext);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ }
+ }
+
+ /**
+ * Contains objects that we can be shared across all the connection of a {@link Worker worker}.
+ */
+ public static final class Context {
+ public final ByteBuffer readBuffer = allocate(Server.SOCKET_BUFFER_SIZE);
+ public final ByteBuffer writeBuffer = allocate(Server.SOCKET_BUFFER_SIZE);
+ /**
+ * Stores a single packet payload to be read.
+ */
+ public final ByteBuffer contentBuffer = allocate(Server.MAX_PACKET_SIZE);
+ public final Deflater deflater = new Deflater();
+ public final Inflater inflater = new Inflater();
+
+ public void clearBuffers() {
+ this.readBuffer.clear();
+ this.writeBuffer.clear();
+ }
+
+ private static ByteBuffer allocate(int size) {
+ return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
+ }
+ }
+}
diff --git a/src/main/java/net/minestom/server/ping/ServerListPingType.java b/src/main/java/net/minestom/server/ping/ServerListPingType.java
index 3da6e89f0..1a9e943cf 100644
--- a/src/main/java/net/minestom/server/ping/ServerListPingType.java
+++ b/src/main/java/net/minestom/server/ping/ServerListPingType.java
@@ -75,13 +75,13 @@ public enum ServerListPingType {
* @see OpenToLAN
*/
public static @NotNull String getOpenToLANPing(@NotNull ResponseData data) {
- return String.format(LAN_PING_FORMAT, SECTION.serialize(data.getDescription()), MinecraftServer.getNettyServer().getPort());
+ return String.format(LAN_PING_FORMAT, SECTION.serialize(data.getDescription()), MinecraftServer.getServer().getPort());
}
/**
* Creates a legacy ping response for client versions below the Netty rewrite (1.6-).
*
- * @param data the response data
+ * @param data the response data
* @param supportsVersions if the client supports recieving the versions of the server
* @return the response
*/
@@ -99,7 +99,7 @@ public enum ServerListPingType {
/**
* Creates a modern ping response for client versions above the Netty rewrite (1.7+).
*
- * @param data the response data
+ * @param data the response data
* @param supportsFullRgb if the client supports full RGB
* @return the response
*/
diff --git a/src/main/java/net/minestom/server/utils/BufUtils.java b/src/main/java/net/minestom/server/utils/BufUtils.java
deleted file mode 100644
index d87734a1c..000000000
--- a/src/main/java/net/minestom/server/utils/BufUtils.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package net.minestom.server.utils;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.PooledByteBufAllocator;
-
-public class BufUtils {
-
- private static final PooledByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;
-
- public static ByteBuf direct() {
- return alloc.ioBuffer();
- }
-}
diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java
index a0e4e7f5b..cd8dc753c 100644
--- a/src/main/java/net/minestom/server/utils/PacketUtils.java
+++ b/src/main/java/net/minestom/server/utils/PacketUtils.java
@@ -1,6 +1,5 @@
package net.minestom.server.utils;
-import io.netty.buffer.ByteBuf;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.minestom.server.MinecraftServer;
@@ -23,11 +22,10 @@ import java.util.Collection;
import java.util.zip.Deflater;
/**
- * Utils class for packets. Including writing a {@link ServerPacket} into a {@link ByteBuf}
+ * Utils class for packets. Including writing a {@link ServerPacket} into a {@link ByteBuffer}
* for network processing.
*/
public final class PacketUtils {
-
private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager();
private static final ThreadLocal COMPRESSOR = ThreadLocal.withInitial(Deflater::new);
@@ -91,9 +89,8 @@ public final class PacketUtils {
// Send grouped packet...
final boolean success = PACKET_LISTENER_MANAGER.processServerPacket(packet, players);
if (success) {
- final ByteBuf finalBuffer = createFramedPacket(packet);
+ final ByteBuffer finalBuffer = createFramedPacket(packet);
final FramedPacket framedPacket = new FramedPacket(finalBuffer);
-
// Send packet to all players
for (Player player : players) {
if (!player.isOnline())
@@ -110,7 +107,6 @@ public final class PacketUtils {
playerConnection.sendPacket(packet);
}
}
- finalBuffer.release(); // Release last reference
}
} else {
// Write the same packet for each individual players
@@ -118,7 +114,6 @@ public final class PacketUtils {
// Verify if the player should receive the packet
if (playerValidator != null && !playerValidator.isValid(player))
continue;
-
final PlayerConnection playerConnection = player.getPlayerConnection();
playerConnection.sendPacket(packet, false);
}
@@ -136,23 +131,14 @@ public final class PacketUtils {
}
/**
- * Writes a {@link ServerPacket} into a {@link ByteBuf}.
+ * Writes a {@link ServerPacket} into a {@link ByteBuffer}.
*
* @param buf the recipient of {@code packet}
* @param packet the packet to write into {@code buf}
*/
- public static void writePacket(@NotNull ByteBuf buf, @NotNull ServerPacket packet) {
+ public static void writePacket(@NotNull ByteBuffer buf, @NotNull ServerPacket packet) {
Utils.writeVarInt(buf, packet.getId());
- writePacketPayload(buf, packet);
- }
-
- /**
- * Writes a packet payload.
- *
- * @param packet the packet to write
- */
- private static void writePacketPayload(@NotNull ByteBuf buffer, @NotNull ServerPacket packet) {
- BinaryWriter writer = new BinaryWriter(buffer);
+ BinaryWriter writer = new BinaryWriter(buf);
try {
packet.write(writer);
} catch (Exception e) {
@@ -160,96 +146,42 @@ public final class PacketUtils {
}
}
- /**
- * Frames a buffer for it to be understood by a Minecraft client.
- *
- * The content of {@code packetBuffer} can be either a compressed or uncompressed packet buffer,
- * it depends of it the client did receive a {@link net.minestom.server.network.packet.server.login.SetCompressionPacket} packet before.
- *
- * @param packetBuffer the buffer containing compressed or uncompressed packet data
- * @param frameTarget the buffer which will receive the framed version of {@code from}
- */
- public static void frameBuffer(@NotNull ByteBuf packetBuffer, @NotNull ByteBuf frameTarget) {
- final int packetSize = packetBuffer.readableBytes();
- final int headerSize = Utils.getVarIntSize(packetSize);
- if (headerSize > 3) {
- throw new IllegalStateException("Unable to fit " + headerSize + " into 3");
- }
-
- frameTarget.ensureWritable(packetSize + headerSize);
-
- Utils.writeVarInt(frameTarget, packetSize);
- frameTarget.writeBytes(packetBuffer, packetBuffer.readerIndex(), packetSize);
- }
-
- /**
- * Compress using zlib the content of a packet.
- *
- * {@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 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, @NotNull ByteBuf packetBuffer, @NotNull ByteBuf compressionTarget) {
- final int packetLength = packetBuffer.readableBytes();
- final boolean compression = packetLength > MinecraftServer.getCompressionThreshold();
- Utils.writeVarInt(compressionTarget, compression ? packetLength : 0);
- if (compression) {
- compress(deflater, packetBuffer, compressionTarget);
- } else {
- compressionTarget.writeBytes(packetBuffer);
- }
- }
-
- private static void compress(@NotNull Deflater deflater, @NotNull ByteBuf uncompressed, @NotNull ByteBuf compressed) {
- deflater.setInput(uncompressed.nioBuffer());
- deflater.finish();
-
- while (!deflater.finished()) {
- ByteBuffer nioBuffer = compressed.nioBuffer(compressed.writerIndex(), compressed.writableBytes());
- compressed.writerIndex(deflater.deflate(nioBuffer) + compressed.writerIndex());
-
- if (compressed.writableBytes() == 0) {
- compressed.ensureWritable(8192);
- }
- }
-
- deflater.reset();
- }
-
- public static void writeFramedPacket(@NotNull ByteBuf buffer,
+ public static void writeFramedPacket(@NotNull ByteBuffer buffer,
@NotNull ServerPacket serverPacket) {
final int compressionThreshold = MinecraftServer.getCompressionThreshold();
// Index of the var-int containing the complete packet length
- final int packetLengthIndex = Utils.writeEmpty3BytesVarInt(buffer);
- final int startIndex = buffer.writerIndex(); // Index where the content starts (after length)
+ final int packetLengthIndex = Utils.writeEmptyVarIntHeader(buffer);
+ final int startIndex = buffer.position(); // Index where the content starts (after length)
if (compressionThreshold > 0) {
// Index of the uncompressed payload length
- final int dataLengthIndex = Utils.writeEmpty3BytesVarInt(buffer);
+ final int dataLengthIndex = Utils.writeEmptyVarIntHeader(buffer);
// Write packet
- final int contentIndex = buffer.writerIndex();
+ final int contentIndex = buffer.position();
writePacket(buffer, serverPacket);
- final int packetSize = buffer.writerIndex() - contentIndex;
+ final int packetSize = buffer.position() - contentIndex;
final int uncompressedLength = packetSize >= compressionThreshold ? packetSize : 0;
- Utils.write3BytesVarInt(buffer, dataLengthIndex, uncompressedLength);
+ Utils.writeVarIntHeader(buffer, dataLengthIndex, uncompressedLength);
if (uncompressedLength > 0) {
// Packet large enough, compress
- ByteBuf uncompressedCopy = buffer.copy(contentIndex, packetSize);
- buffer.writerIndex(contentIndex);
- compress(COMPRESSOR.get(), uncompressedCopy, buffer);
- uncompressedCopy.release();
+ ByteBuffer uncompressedCopy = buffer.duplicate().position(contentIndex).limit(contentIndex + packetSize);
+ buffer.position(contentIndex);
+
+ var deflater = COMPRESSOR.get();
+ deflater.setInput(uncompressedCopy);
+ deflater.finish();
+ deflater.deflate(buffer);
+ deflater.reset();
}
} else {
// No compression, write packet id + payload
writePacket(buffer, serverPacket);
}
// Total length
- final int totalPacketLength = buffer.writerIndex() - startIndex;
- Utils.write3BytesVarInt(buffer, packetLengthIndex, totalPacketLength);
+ final int totalPacketLength = buffer.position() - startIndex;
+ Utils.writeVarIntHeader(buffer, packetLengthIndex, totalPacketLength);
}
/**
@@ -259,8 +191,8 @@ public final class PacketUtils {
* 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.
*/
- public static @NotNull ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket) {
- ByteBuf packetBuf = BufUtils.direct();
+ public static @NotNull ByteBuffer createFramedPacket(@NotNull ServerPacket serverPacket) {
+ ByteBuffer packetBuf = ByteBuffer.allocate(2_000_000);
writeFramedPacket(packetBuf, serverPacket);
return packetBuf;
}
diff --git a/src/main/java/net/minestom/server/utils/Utils.java b/src/main/java/net/minestom/server/utils/Utils.java
index 27549130a..7fb846b24 100644
--- a/src/main/java/net/minestom/server/utils/Utils.java
+++ b/src/main/java/net/minestom/server/utils/Utils.java
@@ -1,13 +1,15 @@
package net.minestom.server.utils;
-import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.shorts.Short2ShortLinkedOpenHashMap;
import net.minestom.server.instance.palette.Palette;
import net.minestom.server.utils.binary.BinaryWriter;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
+import java.nio.ByteBuffer;
import java.util.UUID;
+@ApiStatus.Internal
public final class Utils {
private Utils() {
@@ -21,43 +23,46 @@ public final class Utils {
? 4 : 5;
}
- public static void writeVarInt(@NotNull ByteBuf buf, int value) {
- // Took from velocity
+ public static void writeVarInt(ByteBuffer buf, int value) {
if ((value & (0xFFFFFFFF << 7)) == 0) {
- buf.writeByte(value);
+ buf.put((byte) value);
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
- int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
- buf.writeShort(w);
+ buf.putShort((short) ((value & 0x7F | 0x80) << 8 | (value >>> 7)));
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
- int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
- buf.writeMedium(w);
+ buf.put((byte) (value & 0x7F | 0x80));
+ buf.put((byte) ((value >>> 7) & 0x7F | 0x80));
+ buf.put((byte) (value >>> 14));
+ } else if ((value & (0xFFFFFFFF << 28)) == 0) {
+ buf.putInt((value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
+ | ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21));
} else {
- int w = (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
- | ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80);
- buf.writeInt(w);
- buf.writeByte(value >>> 28);
+ buf.putInt((value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
+ | ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80));
+ buf.put((byte) (value >>> 28));
}
}
- public static void write3BytesVarInt(@NotNull ByteBuf buffer, int startIndex, int value) {
- final int indexCache = buffer.writerIndex();
- buffer.writerIndex(startIndex);
- final int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
- buffer.writeMedium(w);
- buffer.writerIndex(indexCache);
+ public static void writeVarIntHeader(@NotNull ByteBuffer buffer, int startIndex, int value) {
+ final int indexCache = buffer.position();
+ buffer.position(startIndex);
+ buffer.put((byte) (value & 0x7F | 0x80));
+ buffer.put((byte) ((value >>> 7) & 0x7F | 0x80));
+ buffer.put((byte) (value >>> 14));
+ buffer.position(indexCache);
}
- public static int writeEmpty3BytesVarInt(@NotNull ByteBuf buffer) {
- final int index = buffer.writerIndex();
- buffer.writeMedium(0);
+ public static int writeEmptyVarIntHeader(@NotNull ByteBuffer buffer) {
+ final int index = buffer.position();
+ buffer.putShort((short) 0);
+ buffer.put((byte) 0);
return index;
}
- public static int readVarInt(ByteBuf buf) {
+ public static int readVarInt(ByteBuffer buf) {
int i = 0;
- final int maxRead = Math.min(5, buf.readableBytes());
+ final int maxRead = Math.min(5, buf.remaining());
for (int j = 0; j < maxRead; j++) {
- final int k = buf.readByte();
+ final int k = buf.get();
i |= (k & 0x7F) << j * 7;
if ((k & 0x80) != 128) {
return i;
@@ -66,12 +71,12 @@ public final class Utils {
throw new RuntimeException("VarInt is too big");
}
- public static long readVarLong(@NotNull ByteBuf buffer) {
+ public static long readVarLong(@NotNull ByteBuffer buffer) {
int numRead = 0;
long result = 0;
byte read;
do {
- read = buffer.readByte();
+ read = buffer.get();
long value = (read & 0b01111111);
result |= (value << (7 * numRead));
@@ -117,13 +122,12 @@ public final class Utils {
return new UUID(uuidMost, uuidLeast);
}
- public static void writePaletteBlocks(ByteBuf buffer, Palette palette) {
-
+ public static void writePaletteBlocks(ByteBuffer buffer, Palette palette) {
final short blockCount = palette.getBlockCount();
final int bitsPerEntry = palette.getBitsPerEntry();
- buffer.writeShort(blockCount);
- buffer.writeByte((byte) bitsPerEntry);
+ buffer.putShort(blockCount);
+ buffer.put((byte) bitsPerEntry);
// Palette
if (bitsPerEntry < 9) {
@@ -138,7 +142,7 @@ public final class Utils {
final long[] blocks = palette.getBlocks();
writeVarInt(buffer, blocks.length);
for (long datum : blocks) {
- buffer.writeLong(datum);
+ buffer.putLong(datum);
}
}
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 9ea0cfa5e..d529ae4e7 100644
--- a/src/main/java/net/minestom/server/utils/binary/BinaryReader.java
+++ b/src/main/java/net/minestom/server/utils/binary/BinaryReader.java
@@ -1,7 +1,5 @@
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.coordinate.Point;
@@ -17,6 +15,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTReader;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.function.Supplier;
@@ -27,16 +26,15 @@ import java.util.function.Supplier;
* WARNING: not thread-safe.
*/
public class BinaryReader extends InputStream {
-
- private final ByteBuf buffer;
+ private final ByteBuffer buffer;
private final NBTReader nbtReader = new NBTReader(this, false);
- public BinaryReader(@NotNull ByteBuf buffer) {
+ public BinaryReader(@NotNull ByteBuffer buffer) {
this.buffer = buffer;
}
public BinaryReader(byte[] bytes) {
- this(Unpooled.wrappedBuffer(bytes));
+ this(ByteBuffer.wrap(bytes));
}
public int readVarInt() {
@@ -48,49 +46,49 @@ public class BinaryReader extends InputStream {
}
public boolean readBoolean() {
- return buffer.readBoolean();
+ return buffer.get() == 1;
}
public byte readByte() {
- return buffer.readByte();
+ return buffer.get();
}
public short readShort() {
- return buffer.readShort();
+ return buffer.getShort();
}
public char readChar() {
- return buffer.readChar();
+ return buffer.getChar();
}
public int readUnsignedShort() {
- return buffer.readUnsignedShort();
+ return buffer.getShort() & 0xFFFF;
}
/**
* Same as readInt
*/
public int readInteger() {
- return buffer.readInt();
+ return buffer.getInt();
}
/**
* Same as readInteger, created for parity with BinaryWriter
*/
public int readInt() {
- return buffer.readInt();
+ return buffer.getInt();
}
public long readLong() {
- return buffer.readLong();
+ return buffer.getLong();
}
public float readFloat() {
- return buffer.readFloat();
+ return buffer.getFloat();
}
public double readDouble() {
- return buffer.readDouble();
+ return buffer.getDouble();
}
/**
@@ -105,12 +103,9 @@ public class BinaryReader extends InputStream {
*/
public String readSizedString(int maxLength) {
final int length = readVarInt();
- Check.stateCondition(!buffer.isReadable(length),
- "Trying to read a string that is too long (wanted {0}, only have {1})",
- length,
- buffer.readableBytes());
- final String str = buffer.toString(buffer.readerIndex(), length, StandardCharsets.UTF_8);
- buffer.skipBytes(length);
+ byte[] bytes = new byte[length];
+ buffer.get(bytes);
+ final String str = new String(bytes, StandardCharsets.UTF_8);
Check.stateCondition(str.length() > maxLength,
"String length ({0}) was higher than the max length of {1}", length, maxLength);
return str;
@@ -121,10 +116,8 @@ public class BinaryReader extends InputStream {
}
public byte[] readBytes(int length) {
- ByteBuf buf = buffer.readBytes(length);
- byte[] bytes = new byte[buf.readableBytes()];
- buf.readBytes(bytes);
- buf.release();
+ byte[] bytes = new byte[length];
+ buffer.get(bytes);
return bytes;
}
@@ -164,14 +157,11 @@ public class BinaryReader extends InputStream {
}
public Point readBlockPosition() {
- final long value = buffer.readLong();
- return SerializerUtils.longToBlockPosition(value);
+ return SerializerUtils.longToBlockPosition(buffer.getLong());
}
public UUID readUuid() {
- final long most = readLong();
- final long least = readLong();
- return new UUID(most, least);
+ return new UUID(readLong(), readLong());
}
/**
@@ -225,7 +215,7 @@ public class BinaryReader extends InputStream {
return (T[]) result;
}
- public ByteBuf getBuffer() {
+ public ByteBuffer getBuffer() {
return buffer;
}
@@ -236,7 +226,7 @@ public class BinaryReader extends InputStream {
@Override
public int available() {
- return buffer.readableBytes();
+ return buffer.remaining();
}
public NBT readTag() throws IOException, NBTException {
@@ -251,11 +241,12 @@ public class BinaryReader extends InputStream {
* @param extractor the extraction code, simply call the reader's read* methods here.
*/
public byte[] extractBytes(Runnable extractor) {
- int startingPosition = getBuffer().readerIndex();
+ int startingPosition = buffer.position();
extractor.run();
- int endingPosition = getBuffer().readerIndex();
+ int endingPosition = getBuffer().position();
byte[] output = new byte[endingPosition - startingPosition];
- getBuffer().getBytes(startingPosition, output);
+ buffer.get(output, 0, output.length);
+ //buffer.get(startingPosition, output);
return output;
}
}
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 fc20c0aad..ce800e491 100644
--- a/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java
+++ b/src/main/java/net/minestom/server/utils/binary/BinaryWriter.java
@@ -1,8 +1,5 @@
package net.minestom.server.utils.binary;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
-import io.netty.buffer.Unpooled;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.MinecraftServer;
@@ -16,8 +13,11 @@ import org.jglrxavpok.hephaistos.nbt.NBTWriter;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.UUID;
import java.util.function.Consumer;
@@ -26,8 +26,7 @@ import java.util.function.Consumer;
* WARNING: not thread-safe.
*/
public class BinaryWriter extends OutputStream {
-
- private ByteBuf buffer;
+ private ByteBuffer buffer;
private NBTWriter nbtWriter; // Lazily initialized
/**
@@ -36,7 +35,7 @@ public class BinaryWriter extends OutputStream {
* @param initialCapacity the initial capacity of the binary writer
*/
public BinaryWriter(int initialCapacity) {
- this.buffer = Unpooled.buffer(initialCapacity);
+ this.buffer = ByteBuffer.allocate(initialCapacity);
}
/**
@@ -44,24 +43,15 @@ public class BinaryWriter extends OutputStream {
*
* @param buffer the writer buffer
*/
- public BinaryWriter(@NotNull ByteBuf buffer) {
+ public BinaryWriter(@NotNull ByteBuffer buffer) {
this.buffer = buffer;
}
- /**
- * Creates a {@link BinaryWriter} from multiple buffers.
- *
- * @param buffers the buffers making this
- */
- public BinaryWriter(@NotNull ByteBuf... buffers) {
- this.buffer = Unpooled.wrappedBuffer(buffers);
- }
-
/**
* Creates a {@link BinaryWriter} with a "reasonably small initial capacity".
*/
public BinaryWriter() {
- this.buffer = Unpooled.buffer();
+ this(500); // TODO prevent OOB
}
/**
@@ -79,7 +69,7 @@ public class BinaryWriter extends OutputStream {
* @param b the boolean to write
*/
public void writeBoolean(boolean b) {
- buffer.writeBoolean(b);
+ buffer.put((byte) (b ? 1 : 0));
}
/**
@@ -88,7 +78,7 @@ public class BinaryWriter extends OutputStream {
* @param b the byte to write
*/
public void writeByte(byte b) {
- buffer.writeByte(b);
+ buffer.put(b);
}
/**
@@ -97,7 +87,7 @@ public class BinaryWriter extends OutputStream {
* @param c the char to write
*/
public void writeChar(char c) {
- buffer.writeChar(c);
+ buffer.putChar(c);
}
/**
@@ -106,7 +96,7 @@ public class BinaryWriter extends OutputStream {
* @param s the short to write
*/
public void writeShort(short s) {
- buffer.writeShort(s);
+ buffer.putShort(s);
}
/**
@@ -115,7 +105,7 @@ public class BinaryWriter extends OutputStream {
* @param i the int to write
*/
public void writeInt(int i) {
- buffer.writeInt(i);
+ buffer.putInt(i);
}
/**
@@ -124,7 +114,7 @@ public class BinaryWriter extends OutputStream {
* @param l the long to write
*/
public void writeLong(long l) {
- buffer.writeLong(l);
+ buffer.putLong(l);
}
/**
@@ -133,7 +123,7 @@ public class BinaryWriter extends OutputStream {
* @param f the float to write
*/
public void writeFloat(float f) {
- buffer.writeFloat(f);
+ buffer.putFloat(f);
}
/**
@@ -142,7 +132,7 @@ public class BinaryWriter extends OutputStream {
* @param d the double to write
*/
public void writeDouble(double d) {
- buffer.writeDouble(d);
+ buffer.putDouble(d);
}
/**
@@ -171,9 +161,9 @@ public class BinaryWriter extends OutputStream {
* @param string the string to write
*/
public void writeSizedString(@NotNull String string) {
- final int utf8Bytes = ByteBufUtil.utf8Bytes(string);
- writeVarInt(utf8Bytes);
- buffer.writeCharSequence(string, StandardCharsets.UTF_8);
+ final var bytes = string.getBytes(StandardCharsets.UTF_8);
+ writeVarInt(bytes.length);
+ writeBytes(bytes);
}
/**
@@ -184,7 +174,8 @@ public class BinaryWriter extends OutputStream {
* @param charset the charset to encode in
*/
public void writeNullTerminatedString(@NotNull String string, @NotNull Charset charset) {
- buffer.writeCharSequence(string + '\0', charset);
+ final var bytes = (string + '\0').getBytes(charset);
+ writeBytes(bytes);
}
/**
@@ -223,8 +214,8 @@ public class BinaryWriter extends OutputStream {
*
* @param bytes the byte array to write
*/
- public void writeBytes(@NotNull byte[] bytes) {
- buffer.writeBytes(bytes);
+ public void writeBytes(byte @NotNull [] bytes) {
+ buffer.put(bytes);
}
/**
@@ -296,12 +287,12 @@ public class BinaryWriter extends OutputStream {
writeable.write(this);
}
- public void write(@NotNull BinaryWriter writer) {
- this.buffer.writeBytes(writer.getBuffer());
+ public void write(@NotNull ByteBuffer buffer) {
+ this.buffer.put(buffer);
}
- public void write(@NotNull ByteBuf buffer) {
- this.buffer.writeBytes(buffer);
+ public void write(@NotNull BinaryWriter writer) {
+ write(writer.getBuffer());
}
/**
@@ -323,46 +314,51 @@ public class BinaryWriter extends OutputStream {
* @return the byte array containing all the {@link BinaryWriter} data
*/
public byte[] toByteArray() {
- byte[] bytes = new byte[buffer.readableBytes()];
- final int readerIndex = buffer.readerIndex();
- buffer.getBytes(readerIndex, bytes);
+ byte[] bytes = new byte[buffer.remaining()];
+ buffer.get(bytes);
return bytes;
}
/**
- * Adds a {@link BinaryWriter}'s {@link ByteBuf} at the beginning of this writer.
+ * Adds a {@link BinaryWriter}'s {@link ByteBuffer} at the beginning of this writer.
*
* @param headerWriter the {@link BinaryWriter} to add at the beginning
*/
public void writeAtStart(@NotNull BinaryWriter headerWriter) {
// Get the buffer of the header
- final ByteBuf headerBuf = headerWriter.getBuffer();
+ final var headerBuf = headerWriter.getBuffer();
// Merge both the headerBuf and this buffer
- final ByteBuf finalBuffer = Unpooled.wrappedBuffer(headerBuf, buffer);
+ final var finalBuffer = concat(headerBuf, buffer);
// Change the buffer used by this writer
setBuffer(finalBuffer);
}
/**
- * Adds a {@link BinaryWriter}'s {@link ByteBuf} at the end of this writer.
+ * Adds a {@link BinaryWriter}'s {@link ByteBuffer} at the end of this writer.
*
* @param footerWriter the {@link BinaryWriter} to add at the end
*/
public void writeAtEnd(@NotNull BinaryWriter footerWriter) {
// Get the buffer of the footer
- final ByteBuf footerBuf = footerWriter.getBuffer();
+ final var footerBuf = footerWriter.getBuffer();
// Merge both this buffer and the footerBuf
- final ByteBuf finalBuffer = Unpooled.wrappedBuffer(buffer, footerBuf);
+ final var finalBuffer = concat(buffer, footerBuf);
// Change the buffer used by this writer
setBuffer(finalBuffer);
}
+ public static ByteBuffer concat(final ByteBuffer... buffers) {
+ final ByteBuffer combined = ByteBuffer.allocate(Arrays.stream(buffers).mapToInt(Buffer::remaining).sum());
+ Arrays.stream(buffers).forEach(b -> combined.put(b.duplicate()));
+ return combined;
+ }
+
/**
* Gets the raw buffer used by this binary writer.
*
* @return the raw buffer
*/
- public @NotNull ByteBuf getBuffer() {
+ public @NotNull ByteBuffer getBuffer() {
return buffer;
}
@@ -371,7 +367,7 @@ public class BinaryWriter extends OutputStream {
*
* @param buffer the new buffer used by this binary writer
*/
- public void setBuffer(ByteBuf buffer) {
+ public void setBuffer(ByteBuffer buffer) {
this.buffer = buffer;
}
@@ -381,7 +377,8 @@ public class BinaryWriter extends OutputStream {
}
public void writeUnsignedShort(int yourShort) {
- buffer.writeShort(yourShort & 0xFFFF);
+ // FIXME unsigned
+ buffer.putShort((short) (yourShort & 0xFFFF));
}
/**
diff --git a/src/main/java/net/minestom/server/utils/cache/CacheablePacket.java b/src/main/java/net/minestom/server/utils/cache/CacheablePacket.java
deleted file mode 100644
index 348bf7424..000000000
--- a/src/main/java/net/minestom/server/utils/cache/CacheablePacket.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package net.minestom.server.utils.cache;
-
-import io.netty.buffer.ByteBuf;
-import net.minestom.server.network.netty.packet.FramedPacket;
-import net.minestom.server.network.packet.server.ServerPacket;
-import net.minestom.server.utils.PacketUtils;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.UUID;
-
-/**
- * Implemented by {@link ServerPacket server packets} which can be temporary cached in memory to be re-sent later
- * without having to go through all the writing and compression.
- *
- * {@link #getIdentifier()} is to differentiate this packet from the others of the same type.
- */
-public interface CacheablePacket {
-
- /**
- * Gets the cache linked to this packet.
- *
- * WARNING: the cache needs to be shared between all the object instances, tips is to make it static.
- *
- * @return the temporary packet cache
- */
- @NotNull TemporaryPacketCache getCache();
-
- /**
- * Gets the identifier of this packet.
- *
- * Used to verify if this packet is already cached or not.
- *
- * @return this packet identifier, null to prevent caching
- */
- @Nullable UUID getIdentifier();
-
- /**
- * Gets the last time this packet changed.
- *
- * @return the last packet update time in milliseconds
- */
- long getTimestamp();
-
- static @Nullable FramedPacket getCache(@NotNull ServerPacket serverPacket) {
- if (!(serverPacket instanceof CacheablePacket))
- return null;
-
- final CacheablePacket cacheablePacket = (CacheablePacket) serverPacket;
- final UUID identifier = cacheablePacket.getIdentifier();
- if (identifier == null) {
- // This packet explicitly asks to do not retrieve the cache
- return null;
- } else {
- final long timestamp = cacheablePacket.getTimestamp();
- // Try to retrieve the cached buffer
- TemporaryCache temporaryCache = cacheablePacket.getCache();
- TimedBuffer timedBuffer = temporaryCache.retrieve(identifier);
-
- // Update the buffer if non-existent or outdated
- final boolean shouldUpdate = timedBuffer == null ||
- timestamp > timedBuffer.getTimestamp();
-
- if (shouldUpdate) {
- // Buffer freed by guava cache #removalListener
- final ByteBuf buffer = PacketUtils.createFramedPacket(serverPacket);
- timedBuffer = new TimedBuffer(buffer, timestamp);
- temporaryCache.cache(identifier, timedBuffer);
- }
-
- return new FramedPacket(timedBuffer.getBuffer());
- }
- }
-
- static void writeCache(@NotNull ByteBuf buffer, @NotNull ServerPacket serverPacket) {
- FramedPacket framedPacket = CacheablePacket.getCache(serverPacket);
- if (framedPacket == null) {
- PacketUtils.writeFramedPacket(buffer, serverPacket);
- return;
- }
- final ByteBuf body = framedPacket.getBody();
- synchronized (body) {
- if (framedPacket.getBody().refCnt() != 0) {
- buffer.writeBytes(body, body.readerIndex(), body.readableBytes());
- } else {
- PacketUtils.writeFramedPacket(buffer, serverPacket);
- }
- }
- }
-
-}
diff --git a/src/main/java/net/minestom/server/utils/cache/TemporaryCache.java b/src/main/java/net/minestom/server/utils/cache/TemporaryCache.java
deleted file mode 100644
index beb1bff2b..000000000
--- a/src/main/java/net/minestom/server/utils/cache/TemporaryCache.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package net.minestom.server.utils.cache;
-
-import com.github.benmanes.caffeine.cache.Cache;
-import com.github.benmanes.caffeine.cache.Caffeine;
-import com.github.benmanes.caffeine.cache.RemovalListener;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Cache objects with a timeout.
- *
- * @param the object type to cache
- */
-public class TemporaryCache {
-
- private final Cache cache;
-
- /**
- * Creates a new temporary cache.
- *
- * @param duration the time before considering an object unused
- */
- public TemporaryCache(long duration, TimeUnit timeUnit, RemovalListener removalListener) {
- this.cache = Caffeine.newBuilder()
- .expireAfterWrite(duration, timeUnit)
- .removalListener(removalListener)
- .build();
- }
-
- /**
- * Caches an object.
- *
- * @param identifier the object identifier
- * @param value the object to cache
- */
- public void cache(@NotNull UUID identifier, T value) {
- this.cache.put(identifier, value);
- }
-
- public void invalidate(@NotNull UUID identifier) {
- this.cache.invalidate(identifier);
- }
-
- /**
- * Retrieves an object from cache.
- *
- * @param identifier the object identifier
- * @return the retrieved object or null if not found
- */
- @Nullable
- public T retrieve(@NotNull UUID identifier) {
- return cache.getIfPresent(identifier);
- }
-}
diff --git a/src/main/java/net/minestom/server/utils/cache/TemporaryPacketCache.java b/src/main/java/net/minestom/server/utils/cache/TemporaryPacketCache.java
deleted file mode 100644
index 7d805e904..000000000
--- a/src/main/java/net/minestom/server/utils/cache/TemporaryPacketCache.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.minestom.server.utils.cache;
-
-import io.netty.buffer.ByteBuf;
-
-import java.util.concurrent.TimeUnit;
-
-public class TemporaryPacketCache extends TemporaryCache {
- public TemporaryPacketCache(long duration, TimeUnit timeUnit) {
- super(duration, timeUnit, (key, value, cause) -> {
- if (value == null)
- return;
- final ByteBuf buffer = value.getBuffer();
- synchronized (buffer) {
- buffer.release();
- }
- });
- }
-}
\ No newline at end of file
diff --git a/src/main/java/net/minestom/server/utils/cache/TimedBuffer.java b/src/main/java/net/minestom/server/utils/cache/TimedBuffer.java
deleted file mode 100644
index a74c8f764..000000000
--- a/src/main/java/net/minestom/server/utils/cache/TimedBuffer.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.minestom.server.utils.cache;
-
-import io.netty.buffer.ByteBuf;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Object containing a {@link ByteBuf buffer} and its timestamp.
- * Used for packet-caching to use the most recent.
- */
-public class TimedBuffer {
-
- private final ByteBuf buffer;
- private final long timestamp;
-
- public TimedBuffer(@NotNull ByteBuf buffer, long timestamp) {
- this.buffer = buffer;
- this.timestamp = timestamp;
- }
-
- @NotNull
- public ByteBuf getBuffer() {
- return buffer;
- }
-
- public long getTimestamp() {
- return timestamp;
- }
-}
diff --git a/src/test/java/demo/Main.java b/src/test/java/demo/Main.java
index 1343f44e9..1ea170024 100644
--- a/src/test/java/demo/Main.java
+++ b/src/test/java/demo/Main.java
@@ -24,6 +24,7 @@ public class Main {
public static void main(String[] args) {
MinecraftServer minecraftServer = MinecraftServer.init();
+ MinecraftServer.setCompressionThreshold(0);
BlockManager blockManager = MinecraftServer.getBlockManager();