diff --git a/pom.xml b/pom.xml index 768b838..b5e43b8 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,11 @@ commons-io 2.6 + + com.google.guava + guava + 24.1-jre + diff --git a/src/main/java/mc/core/CSPacket.java b/src/main/java/mc/core/CSPacket.java index 45394cb..319ba81 100644 --- a/src/main/java/mc/core/CSPacket.java +++ b/src/main/java/mc/core/CSPacket.java @@ -5,4 +5,6 @@ package mc.core; public interface CSPacket { + default void readSelf(NetStream netStream) { + } } diff --git a/src/main/java/mc/core/NetStream.java b/src/main/java/mc/core/NetStream.java new file mode 100644 index 0000000..898e5cb --- /dev/null +++ b/src/main/java/mc/core/NetStream.java @@ -0,0 +1,57 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core; + +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; + +@Slf4j +public abstract class NetStream { + public static int sizeVarInt(final int value) { + byte size = 0; + int v = value; + + do { + v >>>= 7; + size++; + } while (v != 0); + + return size; + } + + public int readVarInt() { + int result = 0; + byte read; + byte numRead = 0; + + do { + read = readByte(); + int value = (read & 0b01111111); + result |= (value << (7 * numRead)); + + numRead++; + if (numRead > 5) { + log.debug("VarInt is too big!"); + break; + } + } while ((read & 0b10000000) != 0); + + return result; + } + + public String readString() { + int length = readVarInt(); + + byte[] buffer = new byte[length]; + readBytes(buffer); + + return new String(buffer, StandardCharsets.UTF_8); + } + + public abstract byte readByte(); + public abstract void readBytes(byte[] buffer); + public abstract int readUnsignedShort(); +} diff --git a/src/main/java/mc/core/netty/PacketDecoder.java b/src/main/java/mc/core/netty/PacketDecoder.java index 0da59da..6e83d75 100644 --- a/src/main/java/mc/core/netty/PacketDecoder.java +++ b/src/main/java/mc/core/netty/PacketDecoder.java @@ -8,62 +8,60 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import lombok.extern.slf4j.Slf4j; +import mc.core.CSPacket; +import mc.core.NetStream; import mc.core.netty.packets.RawPacket; import java.util.List; @Slf4j public class PacketDecoder extends ByteToMessageDecoder { - private int readVarInt(ByteBuf byteBuf) throws Exception { - int result = 0; - byte read; - byte numRead = 0; - - do { - read = byteBuf.readByte(); - int value = (read & 0b01111111); - result |= (value << (7 * numRead)); - - numRead++; - if (numRead > 5) - throw new Exception("VarInt is too big"); - } while ((read & 0b10000000) != 0); - - return result; + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + ctx.channel().attr(State.ATTR_STATE).set(State.STATUS); + ctx.fireChannelActive(); } - private int sizeVarInt(final int value) { - byte size = 0; - int v = value; - - do { - v >>>= 7; - size++; - } while (v != 0); - - return size; + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + ctx.channel().attr(State.ATTR_STATE).set(null); + ctx.fireChannelInactive(); } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + State state = ctx.channel().attr(State.ATTR_STATE).get(); + NetStream netStream = new WrapperNetStream(in); + log.debug("ByteBuf readableBytes: {}", in.readableBytes()); - int size = readVarInt(in); + int size = netStream.readVarInt(); log.debug("Pkt-Size: {}", size); - int id = readVarInt(in); + int id = netStream.readVarInt(); log.debug("Pkt-Id: {}", id); - byte[] rawData; - if (size > in.readableBytes()) { - rawData = new byte[in.readableBytes()]; - } else { - rawData = new byte[size - sizeVarInt(id)]; - } - in.readBytes(rawData); + Class clientSidePacketClass = state.getClientSidePacket(id); + if (clientSidePacketClass == null) { + log.warn("Unknown packet: {}:{}", state.name(), id); - RawPacket packet = new RawPacket(); - packet.setRawData(rawData); - out.add(packet); + if (log.isDebugEnabled()) { + byte[] rawData; + if (size > in.readableBytes()) { + rawData = new byte[in.readableBytes()]; + } else { + rawData = new byte[size - NetStream.sizeVarInt(id)]; + } + in.readBytes(rawData); + + RawPacket packet = new RawPacket(); + packet.setRawData(rawData); + out.add(packet); + } + } else { + CSPacket packet = clientSidePacketClass.newInstance(); + packet.readSelf(netStream); + out.add(packet); + } log.debug("ByteBuf readableBytes: {}", in.readableBytes()); in.skipBytes(in.readableBytes()); diff --git a/src/main/java/mc/core/netty/PacketHandler.java b/src/main/java/mc/core/netty/PacketHandler.java index 5a1305f..5458007 100644 --- a/src/main/java/mc/core/netty/PacketHandler.java +++ b/src/main/java/mc/core/netty/PacketHandler.java @@ -8,11 +8,32 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import lombok.extern.slf4j.Slf4j; import mc.core.CSPacket; +import mc.core.netty.packets.StatusRequest; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Optional; @Slf4j public class PacketHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, CSPacket packet) throws Exception { - log.info("{}: {}", packet.getClass().getSimpleName(), packet.toString()); + log.debug("{}: {}", packet.getClass().getSimpleName(), packet.toString()); + + Optional optionalMethod = Arrays.stream(this.getClass().getDeclaredMethods()) + .filter(method -> method.getName().equals("on" + packet.getClass().getSimpleName()) + && method.getParameterCount() == 2 + && method.getParameterTypes()[0].isAssignableFrom(ctx.getClass()) + && method.getParameterTypes()[1].isAssignableFrom(packet.getClass())) + .findFirst(); + + if (optionalMethod.isPresent()) { + Method method = optionalMethod.get(); + method.invoke(this, ctx, packet); + } + } + + public void onStatusRequest(ChannelHandlerContext ctx, StatusRequest packet) { + log.info("Catch!"); } } diff --git a/src/main/java/mc/core/netty/State.java b/src/main/java/mc/core/netty/State.java new file mode 100644 index 0000000..d118d5c --- /dev/null +++ b/src/main/java/mc/core/netty/State.java @@ -0,0 +1,40 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.netty; + +import com.google.common.collect.ImmutableMap; +import io.netty.util.AttributeKey; +import lombok.RequiredArgsConstructor; +import mc.core.CSPacket; +import mc.core.netty.packets.StatusRequest; + +import java.util.Arrays; +import java.util.Map; +import java.util.Optional; + +@RequiredArgsConstructor +public enum State { + UNKNOWN(0, ImmutableMap.of()), + STATUS(1, ImmutableMap.of( + 0, StatusRequest.class + )); + + public static final AttributeKey ATTR_STATE = AttributeKey.newInstance("ATTR_STATE"); + + public static State getStateById(final int id) { + Optional optionalState = Arrays.stream(State.values()) + .filter(state -> state.id == id) + .findFirst(); + + return optionalState.orElse(UNKNOWN); + } + + private final int id; + private final Map> clientSidePackets; + + public Class getClientSidePacket(final int packetId) { + return clientSidePackets.get(packetId); + } +} diff --git a/src/main/java/mc/core/netty/WrapperNetStream.java b/src/main/java/mc/core/netty/WrapperNetStream.java new file mode 100644 index 0000000..e69b067 --- /dev/null +++ b/src/main/java/mc/core/netty/WrapperNetStream.java @@ -0,0 +1,29 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.netty; + +import io.netty.buffer.ByteBuf; +import lombok.RequiredArgsConstructor; +import mc.core.NetStream; + +@RequiredArgsConstructor +public class WrapperNetStream extends NetStream { + private final ByteBuf byteBuf; + + @Override + public byte readByte() { + return byteBuf.readByte(); + } + + @Override + public void readBytes(byte[] buffer) { + byteBuf.readBytes(buffer); + } + + @Override + public int readUnsignedShort() { + return byteBuf.readUnsignedShort(); + } +} diff --git a/src/main/java/mc/core/netty/packets/StatusRequest.java b/src/main/java/mc/core/netty/packets/StatusRequest.java new file mode 100644 index 0000000..305b33d --- /dev/null +++ b/src/main/java/mc/core/netty/packets/StatusRequest.java @@ -0,0 +1,38 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.netty.packets; + +import lombok.Getter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import mc.core.CSPacket; +import mc.core.NetStream; +import mc.core.netty.State; + +@Slf4j +@Getter +@ToString +public class StatusRequest implements CSPacket { + private int protocolVersion; + private String serverAddress; + private int serverPort; + private State nextState; + + @Override + public void readSelf(NetStream netStream) { + protocolVersion = netStream.readVarInt(); + serverAddress = netStream.readString(); + serverPort = netStream.readUnsignedShort(); + + int nextStateId = netStream.readVarInt(); + State state = State.getStateById(nextStateId); + if (state != null) { + nextState = state; + } else { + log.warn("Unknown state ({})!", nextStateId); + nextState = State.UNKNOWN; + } + } +}