diff --git a/build.gradle b/build.gradle
index 7e6b85d..7de1955 100644
--- a/build.gradle
+++ b/build.gradle
@@ -43,6 +43,19 @@ dependencies {
implementation 'io.projectreactor:reactor-core'
implementation 'io.netty:netty-all:4.1.22.Final'
+ implementation libs.guava
+
+ testImplementation libs.junit5.api
+ testImplementation libs.junit5.params
+ testRuntimeOnly libs.junit5.engine
+
+ testImplementation libs.lang3
}
-application.mainClassName = 'mc.server.Main'
\ No newline at end of file
+application {
+ mainClassName = 'mc.server.Main'
+}
+
+test {
+ useJUnitPlatform()
+}
diff --git a/buildSrc/src/main/java/ru/dmitriymx/gradle/extention/LibsExtention.java b/buildSrc/src/main/java/ru/dmitriymx/gradle/extention/LibsExtention.java
index 752fe92..05123ee 100644
--- a/buildSrc/src/main/java/ru/dmitriymx/gradle/extention/LibsExtention.java
+++ b/buildSrc/src/main/java/ru/dmitriymx/gradle/extention/LibsExtention.java
@@ -6,8 +6,12 @@ public class LibsExtention {
public final String lombok = "org.projectlombok:lombok:1.18.12";
public final String annotations = "com.google.code.findbugs:jsr305:3.0.2";
+ public final String guava = "com.google.guava:guava:30.1-jre";
+ public final String lang3 = "org.apache.commons:commons-lang3:3.11";
+
public final LoggerLibs logger = new LoggerLibs();
public final Dagger2Libs dagger2 = new Dagger2Libs();
+ public final Junit5Libs junit5 = new Junit5Libs();
public static final class LoggerLibs {
private final String slf4j_version = "1.7.30";
@@ -17,7 +21,6 @@ public class LibsExtention {
"org.slf4j:slf4j-api:" + slf4j_version,
"org.slf4j:jcl-over-slf4j:" + slf4j_version
);
- public final String slf4j_simple = "org.slf4j:slf4j-simple:" + slf4j_version;
public final List Data types
+ * | TYPE | SIZE (bytes) | ENCODING | NOTES |
+ * |----------------|-----------------------|-----------------------------------------------------|--------------------------------------------------------------------------|
+ * | Boolean | 1 | True или False | True = 0x01; False = 0x00 |
+ * | Byte | 1 | Число от -128 до 127 | 8-bit число со знаком |
+ * | Unsigned Byte | 1 | Число от 0 до 255 | 8-bit без знаковое число |
+ * | Short | 2 | Число от -32768 до 32767 | 16-bit число со знаком |
+ * | Unsigned Short | 2 | Число от -32768 до 32767 | 16-bit без знаковое число |
+ * | Int | 4 | Число от -2147483648 и 2147483647 | 32-bit число со знаком |
+ * | Long | 8 | Число от -9223372036854775808 и 9223372036854775807 | 64-bit число со знаком |
+ * | Float | 4 | 32-bit число одинарной точности (IEEE 754-2008) | [1] |
+ * | Double | 8 | 64-bit число одинарной точности (IEEE 754-2008) | [2] |
+ * | String (n) | >= 1 ; <= (n * 4) + 3 | Последовательность Unicode scalar values | В начале пишется длина строки в VarInt, после чего записываются символы. |
+ * | | | | Каждый символ может состоять максимум из 4 байт. [3] |
+ * | | | | Максимальная длина строки - 32767 (3 - это как раз размер VarInt для |
+ * | | | | этого числа). |
+ * | VarInt | >= 1 ; <= 5 | Число от -2147483648 и 2147483647 | 32-bit число с плавающей размерностью от 1 до 5 байт |
+ * | VarLong | >= 1 ; <= 10 | Число от -9223372036854775808 и 9223372036854775807 | 64-bit число с плавающей размерностью от 1 до 10 байт |
+ *
+ * [1] - Single-precision floating-point format
+ * [2] - Double-precision floating-point format
+ * [3] - Unicode Scalar Value
+ *
+ *
+ * @see Data types
+ */
+@Slf4j
+@RequiredArgsConstructor
+@EqualsAndHashCode(callSuper = false)
+@ToString
+public class NetByteBuf extends ByteBuf {
+
+ @Delegate
+ private final ByteBuf byteBuf;
+
+ //region String
+ public String readString() {
+ return readString(Short.MAX_VALUE);
+ }
+
+ @SuppressWarnings("java:S131")
+ public String readString(int maxLength) {
+ int length = readVarInt();
+
+ if (length == 0) {
+ return "";
+ } else if (length > maxLength) {
+ throw new DecoderException("String length exceeds maximum length: " + length + " > " + maxLength);
+ } else if (length < 0) {
+ throw new DecoderException("String length less zero!");
+ }
+
+ byte[] bytes = new byte[length * 4];
+ int readbleBytes = 0;
+ for (int i = 0; i < length && readableBytes() > 0; i++) {
+ byte b = readByte();
+ bytes[readbleBytes++] = b;
+
+ switch ((b & 0xFF) >> 4) {
+ case 0b1100:
+ case 0b1101:
+ bytes[readbleBytes++] = readByte();
+ break;
+ case 0b1110:
+ bytes[readbleBytes++] = readByte();
+ bytes[readbleBytes++] = readByte();
+ break;
+ case 0b1111:
+ bytes[readbleBytes++] = readByte();
+ bytes[readbleBytes++] = readByte();
+ bytes[readbleBytes++] = readByte();
+ break;
+ }
+ }
+
+ return new String(bytes, 0, readbleBytes, StandardCharsets.UTF_8);
+ }
+
+ public void writeString(String string) {
+ byte[] buf;
+ int length = (int) string.codePoints().count();
+
+ if (length > Short.MAX_VALUE) {
+ log.warn("String is too long: {} > {}", length, Short.MAX_VALUE);
+ buf = string.substring(0, Short.MAX_VALUE).getBytes(StandardCharsets.UTF_8);
+ writeVarInt(Short.MAX_VALUE);
+ } else {
+ buf = string.getBytes(StandardCharsets.UTF_8);
+ writeVarInt(length);
+ }
+
+ writeBytes(buf);
+ }
+ //endregion
+
+ //region VarInt
+ public int readVarInt() {
+ int numRead = 0;
+ int result = 0;
+ byte read;
+ do {
+ if ((numRead + 1) > 5) {
+ log.warn("VarInt is too big");
+ break;
+ }
+ read = readByte();
+ int value = (read & 0b01111111);
+ result |= (value << (7 * numRead));
+
+ numRead++;
+ } while ((read & 0b10000000) != 0);
+
+ return result;
+ }
+
+ public void writeVarInt(int value) {
+ while ((value & -128) != 0) {
+ writeByte(value & 127 | 128);
+ value >>>= 7;
+ }
+
+ writeByte(value);
+ }
+ //endregion
+
+ //region VarLong
+ public long readVarLong() {
+ int numRead = 0;
+ long result = 0L;
+ byte read;
+ do {
+ if (numRead > 10) {
+ log.warn("VarLong is too big");
+ break;
+ }
+
+ read = readByte();
+ long value = (read & 0b01111111);
+ result |= (value << (7 * numRead));
+
+ numRead++;
+ } while ((read & 0b10000000) != 0);
+
+ return result;
+ }
+
+ public void writeVarLong(long value) {
+ while ((value & -128L) != 0L) {
+ writeByte((int) (value & 127L) | 128);
+ value >>>= 7;
+ }
+
+ writeByte((int) value);
+ }
+ //endregion
+
+ //region UUID
+ public UUID readUUID() {
+ return new UUID(readLong(), readLong());
+ }
+
+ public void writeUUID(UUID uuid) {
+ writeLong(uuid.getMostSignificantBits());
+ writeLong(uuid.getLeastSignificantBits());
+ }
+ //endregion
+}
diff --git a/src/main/java/mc/protocol/io/codec/ProtocolDecoder.java b/src/main/java/mc/protocol/io/codec/ProtocolDecoder.java
new file mode 100644
index 0000000..a59f5a0
--- /dev/null
+++ b/src/main/java/mc/protocol/io/codec/ProtocolDecoder.java
@@ -0,0 +1,59 @@
+package mc.protocol.io.codec;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import mc.protocol.NetworkAttributes;
+import mc.protocol.State;
+import mc.protocol.io.NetByteBuf;
+import mc.protocol.packets.Packet;
+import mc.protocol.packets.PacketDirection;
+import mc.protocol.packets.UnknownPacket;
+
+import java.util.List;
+import java.util.Objects;
+
+@Slf4j
+@RequiredArgsConstructor
+public class ProtocolDecoder extends ByteToMessageDecoder {
+
+ private final boolean readUnknownPackets;
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ ctx.channel().attr(NetworkAttributes.STATE).set(State.HANDSHAKING);
+ super.channelActive(ctx);
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ ctx.channel().attr(NetworkAttributes.STATE).set(null);
+ super.channelInactive(ctx);
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List
Пример
+ *
+ * {
+ * "version": {
+ * "name": "1.8.7",
+ * "protocol": 47
+ * },
+ * "players": {
+ * "max": 100,
+ * "online": 5,
+ * "sample": [
+ * {
+ * "name": "thinkofdeath",
+ * "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"
+ * }
+ * ]
+ * },
+ * "description": {
+ * "text": "Hello world"
+ * },
+ * "favicon": "data:image/png;base64,<data>"
+ * }
+ *
+ */
+ private String info;
+
+ @Override
+ public void readSelf(NetByteBuf netByteBuf) {
+ info = netByteBuf.readString();
+ }
+
+ @Override
+ public void writeSelf(NetByteBuf netByteBuf) {
+ netByteBuf.writeString(info);
+ }
+}
diff --git a/src/main/java/mc/server/Main.java b/src/main/java/mc/server/Main.java
index bd5f9f7..a435547 100644
--- a/src/main/java/mc/server/Main.java
+++ b/src/main/java/mc/server/Main.java
@@ -13,6 +13,6 @@ public class Main {
NetworkComponent networkComponent = DaggerNetworkComponent.create();
Server server = networkComponent.getServer();
- server.start("127.0.0.1", 25565);
+ server.bind("127.0.0.1", 25565);
}
}
diff --git a/src/main/java/mc/server/di/NetworkModule.java b/src/main/java/mc/server/di/NetworkModule.java
index aae8cb4..c06bb2c 100644
--- a/src/main/java/mc/server/di/NetworkModule.java
+++ b/src/main/java/mc/server/di/NetworkModule.java
@@ -6,19 +6,26 @@ import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
-import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
+import lombok.extern.slf4j.Slf4j;
+import mc.protocol.io.codec.ProtocolDecoder;
+import mc.protocol.io.codec.ProtocolEncoder;
+import mc.protocol.io.codec.ProtocolSplitter;
import mc.server.network.Server;
+import mc.server.network.netty.HandshakeHandler;
import mc.server.network.netty.NettyServer;
+import mc.server.network.netty.StatusHandler;
-import javax.inject.Named;
-import java.util.Collections;
-import java.util.List;
+import javax.inject.Provider;
+import java.util.LinkedHashMap;
+import java.util.Map;
@Module
+@Slf4j
public class NetworkModule {
@Provides
@@ -27,14 +34,10 @@ public class NetworkModule {
}
@Provides
- ServerBootstrap provideServerBootstrap(
- @Named("boss-group") EventLoopGroup bossGroup,
- @Named("worker-group") EventLoopGroup workerGroup,
- ChannelInitializer