diff --git a/libs.gradle b/libs.gradle index 691322b..402450a 100644 --- a/libs.gradle +++ b/libs.gradle @@ -19,7 +19,8 @@ ext { yaml : 'org.yaml:snakeyaml:1.28', json : 'com.eclipsesource.minimal-json:minimal-json:0.9.5', ioutils : 'commons-io:commons-io:2.6', - jopt : 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' + jopt : 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3', + objpool : 'org.apache.commons:commons-pool2:2.9.0' ] libs.logger = [ diff --git a/protocol/build.gradle b/protocol/build.gradle index b8fb582..8b01584 100644 --- a/protocol/build.gradle +++ b/protocol/build.gradle @@ -5,6 +5,7 @@ dependencies { implementation libs.netty implementation libs.json + implementation libs.objpool testImplementation libs.lang3 } diff --git a/protocol/src/main/java/mc/protocol/NettyServer.java b/protocol/src/main/java/mc/protocol/NettyServer.java index 16325ac..3020209 100644 --- a/protocol/src/main/java/mc/protocol/NettyServer.java +++ b/protocol/src/main/java/mc/protocol/NettyServer.java @@ -16,6 +16,7 @@ import mc.protocol.api.Server; import mc.protocol.io.codec.ProtocolDecoder; import mc.protocol.io.codec.ProtocolEncoder; import mc.protocol.io.codec.ProtocolSplitter; +import mc.protocol.utils.PacketPool; import javax.annotation.Nonnull; import java.util.LinkedHashMap; @@ -26,6 +27,7 @@ import java.util.function.Consumer; @RequiredArgsConstructor public class NettyServer implements Server { + private final PacketPool packetPool; private Consumer> consumerNewConnection; private Consumer> consumerDisconnect; @@ -77,9 +79,9 @@ public class NettyServer implements Server { map.put("packet_splitter", new ProtocolSplitter()); map.put("logger", new LoggingHandler(LogLevel.DEBUG)); - map.put("packet_decoder", new ProtocolDecoder(true, consumerNewConnection, consumerDisconnect)); + map.put("packet_decoder", new ProtocolDecoder(true, packetPool, consumerNewConnection, consumerDisconnect)); map.put("packet_encoder", new ProtocolEncoder()); - map.put("packet_handler", new PacketInboundHandler()); + map.put("packet_handler", new PacketInboundHandler(packetPool)); return map; } diff --git a/protocol/src/main/java/mc/protocol/PacketInboundHandler.java b/protocol/src/main/java/mc/protocol/PacketInboundHandler.java index 3f170e9..6ce4c3e 100644 --- a/protocol/src/main/java/mc/protocol/PacketInboundHandler.java +++ b/protocol/src/main/java/mc/protocol/PacketInboundHandler.java @@ -5,19 +5,24 @@ import io.netty.channel.SimpleChannelInboundHandler; import lombok.RequiredArgsConstructor; import mc.protocol.api.ConnectionContext; import mc.protocol.packets.ClientSidePacket; +import mc.protocol.utils.PacketPool; import reactor.core.publisher.Sinks; @RequiredArgsConstructor public class PacketInboundHandler extends SimpleChannelInboundHandler { + private final PacketPool poolPackets; + @SuppressWarnings("rawtypes") @Override - protected void channelRead0(ChannelHandlerContext ctx, ClientSidePacket packet) { + protected void channelRead0(ChannelHandlerContext ctx, ClientSidePacket packet) throws Exception { Sinks.Many packetSinks = ctx.channel().attr(NetworkAttributes.STATE) .get().getPacketSinks(packet.getClass()); if (packetSinks != null) { packetSinks.tryEmitNext(new NettyConnectionContext<>(ctx, packet)); } + + poolPackets.returnObject(packet); } } diff --git a/protocol/src/main/java/mc/protocol/State.java b/protocol/src/main/java/mc/protocol/State.java index 72e39b4..3e3c4ff 100644 --- a/protocol/src/main/java/mc/protocol/State.java +++ b/protocol/src/main/java/mc/protocol/State.java @@ -81,6 +81,7 @@ public enum State { @Getter private final int id; + @Getter private final Map> clientSidePackets; private final Map, Integer> serverSidePackets; diff --git a/protocol/src/main/java/mc/protocol/di/ProtocolModule.java b/protocol/src/main/java/mc/protocol/di/ProtocolModule.java index c298873..e228b86 100644 --- a/protocol/src/main/java/mc/protocol/di/ProtocolModule.java +++ b/protocol/src/main/java/mc/protocol/di/ProtocolModule.java @@ -3,15 +3,40 @@ package mc.protocol.di; import dagger.Module; import dagger.Provides; import mc.protocol.NettyServer; +import mc.protocol.State; import mc.protocol.api.Server; +import mc.protocol.packets.ClientSidePacket; +import mc.protocol.packets.UnknownPacket; +import mc.protocol.utils.PacketFactory; +import mc.protocol.utils.PacketPool; +import org.apache.commons.pool2.ObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPool; + +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; @Module public class ProtocolModule { @Provides @ServerScope - Server provideServer() { - return new NettyServer(); + Server provideServer(PacketPool packetPool) { + return new NettyServer(packetPool); } + @Provides + @ServerScope + @SuppressWarnings({ "rawtypes", "unchecked" }) + PacketPool providePacketPool() { + Map, ObjectPool> map = Stream.of(State.values()) + .flatMap(state -> state.getClientSidePackets().values().stream()) + .distinct() + .collect(Collectors.toMap( + packetClass -> packetClass, + packetClass -> new GenericObjectPool(new PacketFactory<>(packetClass)))); + map.put(UnknownPacket.class, new GenericObjectPool(new PacketFactory<>(UnknownPacket.class))); + + return new PacketPool(map); + } } diff --git a/protocol/src/main/java/mc/protocol/io/codec/ProtocolDecoder.java b/protocol/src/main/java/mc/protocol/io/codec/ProtocolDecoder.java index 6bc2e65..af11952 100644 --- a/protocol/src/main/java/mc/protocol/io/codec/ProtocolDecoder.java +++ b/protocol/src/main/java/mc/protocol/io/codec/ProtocolDecoder.java @@ -12,17 +12,19 @@ import mc.protocol.api.ConnectionContext; import mc.protocol.io.NetByteBuf; import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.UnknownPacket; +import mc.protocol.utils.PacketPool; import javax.annotation.Nonnull; import java.util.List; import java.util.Objects; import java.util.function.Consumer; -@Slf4j @RequiredArgsConstructor +@Slf4j public class ProtocolDecoder extends ByteToMessageDecoder { private final boolean readUnknownPackets; + private final PacketPool poolPackets; private final Consumer> consumerNewConnection; private final Consumer> consumerDisconnect; @@ -49,14 +51,17 @@ public class ProtocolDecoder extends ByteToMessageDecoder { log.warn("Unknown packet: State {} ; Id 0x{}", state, packetIdAsHexcode(packetId)); if (readUnknownPackets) { - UnknownPacket unknownPacket = new UnknownPacket(state, packetId, netByteBuf.readableBytes()); + UnknownPacket unknownPacket = poolPackets.borrowObject(UnknownPacket.class); + unknownPacket.setState(state); + unknownPacket.setId(packetId); + unknownPacket.setDataSize(netByteBuf.readableBytes()); unknownPacket.readSelf(netByteBuf); out.add(unknownPacket); } else { netByteBuf.skipBytes(netByteBuf.readableBytes()); } } else { - ClientSidePacket packet = packetClass.getDeclaredConstructor().newInstance(); + ClientSidePacket packet = poolPackets.borrowObject(packetClass); packet.readSelf(netByteBuf); log.debug("IN: {}:{}", state, packet); out.add(packet); diff --git a/protocol/src/main/java/mc/protocol/packets/ClientSidePacket.java b/protocol/src/main/java/mc/protocol/packets/ClientSidePacket.java index 6629750..735a823 100644 --- a/protocol/src/main/java/mc/protocol/packets/ClientSidePacket.java +++ b/protocol/src/main/java/mc/protocol/packets/ClientSidePacket.java @@ -8,4 +8,6 @@ import mc.protocol.io.NetByteBuf; public interface ClientSidePacket extends Packet { void readSelf(NetByteBuf netByteBuf); + + void passivate(); } diff --git a/protocol/src/main/java/mc/protocol/packets/EmptyPacket.java b/protocol/src/main/java/mc/protocol/packets/EmptyPacket.java index 60c800f..0e3a740 100644 --- a/protocol/src/main/java/mc/protocol/packets/EmptyPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/EmptyPacket.java @@ -13,4 +13,9 @@ public abstract class EmptyPacket implements ClientSidePacket, ServerSidePacket public void writeSelf(NetByteBuf netByteBuf) { // empty } + + @Override + public void passivate() { + // pass + } } diff --git a/protocol/src/main/java/mc/protocol/packets/PingPacket.java b/protocol/src/main/java/mc/protocol/packets/PingPacket.java index f40b121..3b1ee48 100644 --- a/protocol/src/main/java/mc/protocol/packets/PingPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/PingPacket.java @@ -34,6 +34,11 @@ public class PingPacket implements ClientSidePacket, ServerSidePacket { payload = netByteBuf.readLong(); } + @Override + public void passivate() { + this.payload = null; + } + @Override public void writeSelf(NetByteBuf netByteBuf) { netByteBuf.writeLong(payload); diff --git a/protocol/src/main/java/mc/protocol/packets/UnknownPacket.java b/protocol/src/main/java/mc/protocol/packets/UnknownPacket.java index aef7b74..6722e5f 100644 --- a/protocol/src/main/java/mc/protocol/packets/UnknownPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/UnknownPacket.java @@ -1,17 +1,19 @@ package mc.protocol.packets; import lombok.Data; +import lombok.NoArgsConstructor; import lombok.ToString; import mc.protocol.State; import mc.protocol.io.NetByteBuf; +@NoArgsConstructor @Data @ToString(exclude = "rawData") public class UnknownPacket implements ClientSidePacket { - private final State state; - private final int id; - private final int dataSize; + private State state; + private int id; + private int dataSize; private byte[] rawData; @Override @@ -19,4 +21,12 @@ public class UnknownPacket implements ClientSidePacket { rawData = new byte[dataSize]; netByteBuf.readBytes(rawData); } + + @Override + public void passivate() { + this.state = null; + this.id = 0; + this.dataSize = 0; + this.rawData = null; + } } diff --git a/protocol/src/main/java/mc/protocol/packets/client/CPlayerPositionAndLookPacket.java b/protocol/src/main/java/mc/protocol/packets/client/CPlayerPositionAndLookPacket.java index ed5c002..d7be871 100644 --- a/protocol/src/main/java/mc/protocol/packets/client/CPlayerPositionAndLookPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/client/CPlayerPositionAndLookPacket.java @@ -51,6 +51,13 @@ public class CPlayerPositionAndLookPacket implements ClientSidePacket { this.onGround = netByteBuf.readBoolean(); } + @Override + public void passivate() { + this.position = null; + this.look = null; + this.onGround = false; + } + public double getYPositionHead() { return this.position.getY() + 1.62f; } diff --git a/protocol/src/main/java/mc/protocol/packets/client/ClientSettingsPacket.java b/protocol/src/main/java/mc/protocol/packets/client/ClientSettingsPacket.java index 680db2c..b78dab3 100644 --- a/protocol/src/main/java/mc/protocol/packets/client/ClientSettingsPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/client/ClientSettingsPacket.java @@ -64,6 +64,16 @@ public class ClientSettingsPacket implements ClientSidePacket { this.mainHand = MainHand.valueById(netByteBuf.readVarInt()); } + @Override + public void passivate() { + this.locale = null; + this.viewDistance = 0; + this.chatMode = null; + this.chatColors = false; + this.$displayedSkinPartsBitMask = 0; + this.mainHand = null; + } + public boolean isCapeEnabled() { return ($displayedSkinPartsBitMask & 0x01) > 0; } diff --git a/protocol/src/main/java/mc/protocol/packets/client/HandshakePacket.java b/protocol/src/main/java/mc/protocol/packets/client/HandshakePacket.java index ccb86b5..005c6b9 100644 --- a/protocol/src/main/java/mc/protocol/packets/client/HandshakePacket.java +++ b/protocol/src/main/java/mc/protocol/packets/client/HandshakePacket.java @@ -47,4 +47,12 @@ public class HandshakePacket implements ClientSidePacket { nextState = State.getById(netByteBuf.readVarInt()); } + @Override + public void passivate() { + this.protocolVersion = 0; + this.host = null; + this.port = 0; + this.nextState = null; + } + } diff --git a/protocol/src/main/java/mc/protocol/packets/client/LoginStartPacket.java b/protocol/src/main/java/mc/protocol/packets/client/LoginStartPacket.java index 77775b6..0462715 100644 --- a/protocol/src/main/java/mc/protocol/packets/client/LoginStartPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/client/LoginStartPacket.java @@ -34,4 +34,9 @@ public class LoginStartPacket implements ClientSidePacket { this.name = netByteBuf.readString(); } + @Override + public void passivate() { + this.name = null; + } + } diff --git a/protocol/src/main/java/mc/protocol/packets/client/PlayerLookPacket.java b/protocol/src/main/java/mc/protocol/packets/client/PlayerLookPacket.java index 0f6ec16..35568fa 100644 --- a/protocol/src/main/java/mc/protocol/packets/client/PlayerLookPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/client/PlayerLookPacket.java @@ -39,4 +39,10 @@ public class PlayerLookPacket implements ClientSidePacket { this.onGround = netByteBuf.readBoolean(); } + + @Override + public void passivate() { + this.look = null; + this.onGround = false; + } } diff --git a/protocol/src/main/java/mc/protocol/packets/client/PlayerPositionPacket.java b/protocol/src/main/java/mc/protocol/packets/client/PlayerPositionPacket.java index abbc609..3be5633 100644 --- a/protocol/src/main/java/mc/protocol/packets/client/PlayerPositionPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/client/PlayerPositionPacket.java @@ -43,6 +43,12 @@ public class PlayerPositionPacket implements ClientSidePacket { this.onGround = netByteBuf.readBoolean(); } + @Override + public void passivate() { + this.position = null; + this.onGround = false; + } + public double getYPositionHead() { return this.position.getY() + 1.62f; } diff --git a/protocol/src/main/java/mc/protocol/packets/client/PluginMessagePacket.java b/protocol/src/main/java/mc/protocol/packets/client/PluginMessagePacket.java index 622bd4e..2ae5ab0 100644 --- a/protocol/src/main/java/mc/protocol/packets/client/PluginMessagePacket.java +++ b/protocol/src/main/java/mc/protocol/packets/client/PluginMessagePacket.java @@ -39,4 +39,10 @@ public class PluginMessagePacket implements ClientSidePacket { this.rawData = new byte[netByteBuf.readableBytes()]; netByteBuf.readBytes(this.rawData); } + + @Override + public void passivate() { + this.channelName = null; + this.rawData = null; + } } diff --git a/protocol/src/main/java/mc/protocol/packets/client/TeleportConfirmPacket.java b/protocol/src/main/java/mc/protocol/packets/client/TeleportConfirmPacket.java index eee3df4..fd120e9 100644 --- a/protocol/src/main/java/mc/protocol/packets/client/TeleportConfirmPacket.java +++ b/protocol/src/main/java/mc/protocol/packets/client/TeleportConfirmPacket.java @@ -33,4 +33,9 @@ public class TeleportConfirmPacket implements ClientSidePacket { public void readSelf(NetByteBuf netByteBuf) { this.teleportId = netByteBuf.readVarInt(); } + + @Override + public void passivate() { + this.teleportId = 0; + } } diff --git a/protocol/src/main/java/mc/protocol/utils/PacketFactory.java b/protocol/src/main/java/mc/protocol/utils/PacketFactory.java new file mode 100644 index 0000000..8c17dca --- /dev/null +++ b/protocol/src/main/java/mc/protocol/utils/PacketFactory.java @@ -0,0 +1,30 @@ +package mc.protocol.utils; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import mc.protocol.packets.ClientSidePacket; +import org.apache.commons.pool2.BasePooledObjectFactory; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.impl.DefaultPooledObject; + +@Slf4j +@RequiredArgsConstructor +public class PacketFactory

extends BasePooledObjectFactory

{ + + private final Class

clazz; + + @Override + public P create() throws Exception { + return clazz.getDeclaredConstructor().newInstance(); + } + + @Override + public PooledObject

wrap(P packet) { + return new DefaultPooledObject<>(packet); + } + + @Override + public void passivateObject(PooledObject

pooledPacket) { + pooledPacket.getObject().passivate(); + } +} diff --git a/protocol/src/main/java/mc/protocol/utils/PacketPool.java b/protocol/src/main/java/mc/protocol/utils/PacketPool.java new file mode 100644 index 0000000..447d7c2 --- /dev/null +++ b/protocol/src/main/java/mc/protocol/utils/PacketPool.java @@ -0,0 +1,26 @@ +package mc.protocol.utils; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import mc.protocol.packets.ClientSidePacket; +import org.apache.commons.pool2.ObjectPool; + +import java.util.Map; + +@Slf4j +@RequiredArgsConstructor +public class PacketPool { + + @SuppressWarnings("rawtypes") + private final Map, ObjectPool> mapPoolPackets; + + @SuppressWarnings("unchecked") + public

P borrowObject(Class

packetClass) throws Exception { + return (P) mapPoolPackets.get(packetClass).borrowObject(); + } + + @SuppressWarnings("unchecked") + public

void returnObject(P packet) throws Exception { + mapPoolPackets.get(packet.getClass()).returnObject(packet); + } +}