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 acd3f8b62..a073de622 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 @@ -10,6 +10,7 @@ import net.minestom.server.instance.palette.Palette; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; import net.minestom.server.tag.Tag; +import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.Utils; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryWriter; @@ -32,8 +33,7 @@ public class ChunkDataPacket implements ServerPacket { public Map entries = new HashMap<>(); private static final byte CHUNK_SECTION_COUNT = 16; - 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; + private static final PacketUtils.Cache BLOCK_CACHE = PacketUtils.Cache.get("chunk-block-cache", 262_144); /** * Heightmaps NBT, as read from raw packet data. @@ -49,7 +49,7 @@ public class ChunkDataPacket implements ServerPacket { writer.writeInt(chunkX); writer.writeInt(chunkZ); - ByteBuffer blocks = ByteBuffer.allocate(MAX_BUFFER_SIZE); + ByteBuffer blocks = BLOCK_CACHE.retrieveLocal(); Int2LongRBTreeMap maskMap = new Int2LongRBTreeMap(); diff --git a/src/main/java/net/minestom/server/utils/PacketUtils.java b/src/main/java/net/minestom/server/utils/PacketUtils.java index 4139b0771..a10776cf9 100644 --- a/src/main/java/net/minestom/server/utils/PacketUtils.java +++ b/src/main/java/net/minestom/server/utils/PacketUtils.java @@ -21,6 +21,8 @@ import org.jetbrains.annotations.NotNull; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.zip.Deflater; /** @@ -30,22 +32,12 @@ import java.util.zip.Deflater; public final class PacketUtils { private static final PacketListenerManager PACKET_LISTENER_MANAGER = MinecraftServer.getPacketListenerManager(); private static final ThreadLocal COMPRESSOR = ThreadLocal.withInitial(Deflater::new); - private static final ThreadLocal PACKET_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(Server.SOCKET_BUFFER_SIZE)); - private static final ThreadLocal COMPRESSION_CACHE = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(Server.SOCKET_BUFFER_SIZE)); + private static final Cache PACKET_BUFFER = Cache.get("packet-buffer", Server.SOCKET_BUFFER_SIZE); + private static final Cache COMPRESSION_CACHE = Cache.get("compression-buffer", Server.SOCKET_BUFFER_SIZE); private PacketUtils() { } - @ApiStatus.Internal - static ByteBuffer localBuffer() { - return PACKET_BUFFER.get().clear(); - } - - @ApiStatus.Internal - static ByteBuffer compressionCache() { - return COMPRESSION_CACHE.get().clear(); - } - /** * Sends a packet to an audience. This method performs the following steps in the * following order: @@ -162,7 +154,7 @@ public final class PacketUtils { // Packet large enough, compress final int limitCache = buffer.limit(); buffer.position(contentStart).limit(contentStart + packetSize); - var uncompressedCopy = compressionCache().put(buffer).flip(); + var uncompressedCopy = COMPRESSION_CACHE.retrieveLocal().put(buffer).flip(); buffer.position(contentStart).limit(limitCache); var deflater = COMPRESSOR.get(); @@ -198,15 +190,40 @@ public final class PacketUtils { } public static ByteBuffer createFramedPacket(@NotNull ServerPacket packet) { - return createFramedPacket(localBuffer(), packet); + return createFramedPacket(PACKET_BUFFER.retrieveLocal(), packet); } public static ByteBuffer createFramedPacket(@NotNull ServerPacket packet, boolean compression) { - return createFramedPacket(localBuffer(), packet, compression); + return createFramedPacket(PACKET_BUFFER.retrieveLocal(), packet, compression); } public static ByteBuffer allocateTrimmedPacket(@NotNull ServerPacket packet) { final var temp = PacketUtils.createFramedPacket(packet); return ByteBuffer.allocateDirect(temp.position()).put(temp.flip()).asReadOnlyBuffer(); } + + @ApiStatus.Internal + public static final class Cache { + private static final Map CACHES = new ConcurrentHashMap<>(); + + private final String name; + private final ThreadLocal cache; + + private Cache(String name, int size) { + this.name = name; + this.cache = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(size)); + } + + public static Cache get(String name, int size) { + return CACHES.computeIfAbsent(name, s -> new Cache(name, size)); + } + + public String name() { + return name; + } + + public ByteBuffer retrieveLocal() { + return cache.get().clear(); + } + } }