diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..bf17ed0 --- /dev/null +++ b/build.gradle @@ -0,0 +1,36 @@ +subprojects { + apply plugin: 'java' + + compileJava { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + options.encoding = 'UTF-8' + } + + repositories { + mavenLocal() + mavenCentral() + } + + configurations { + compile_excludeCopy + compile.extendsFrom compile_excludeCopy + } + + dependencies { + /* Core */ + compile (group: 'mc', name: 'core', version: '1.0-SNAPSHOT') + + /* Components */ + compile (group: 'org.projectlombok', name: 'lombok', version: '1.16.16') + } + + task copyDep(type: Copy) { + into 'libs' + from configurations.compile + configurations.runtime - configurations.compile_excludeCopy + } + + task cleanDep(type: Delete) { + delete 'libs' + } +} \ No newline at end of file diff --git a/proto125/build.gradle b/proto125/build.gradle new file mode 100644 index 0000000..581a12c --- /dev/null +++ b/proto125/build.gradle @@ -0,0 +1,2 @@ +group 'mc' +version '1.0-SNAPSHOT' diff --git a/proto125/src/main/java/mc/core/network/proto_125/ByteArrayOutputNetStream.java b/proto125/src/main/java/mc/core/network/proto_125/ByteArrayOutputNetStream.java new file mode 100644 index 0000000..80a1dcf --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/ByteArrayOutputNetStream.java @@ -0,0 +1,119 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.network.proto_125; + +import lombok.extern.slf4j.Slf4j; + +import java.io.ByteArrayOutputStream; + +@Slf4j +public class ByteArrayOutputNetStream extends NetStream_p125 { + private ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + @Override + public boolean readBoolean() { + throw new UnsupportedOperationException(); + } + + @Override + public byte readByte() { + throw new UnsupportedOperationException(); + } + + @Override + public void readBytes(byte[] buffer) { + throw new UnsupportedOperationException(); + } + + @Override + public int readUnsignedByte() { + throw new UnsupportedOperationException(); + } + + @Override + public int readUnsignedShort() { + throw new UnsupportedOperationException(); + } + + @Override + public short readShort() { + throw new UnsupportedOperationException(); + } + + @Override + public int readInt() { + throw new UnsupportedOperationException(); + } + + @Override + public float readFloat() { + throw new UnsupportedOperationException(); + } + + @Override + public double readDouble() { + throw new UnsupportedOperationException(); + } + + @Override + public void writeBoolean(boolean value) { + baos.write(value ? 1 : 0); + } + + @Override + public void writeByte(int value) { + baos.write(value); + } + + @Override + public void writeBytes(byte[] buffer) { + baos.write(buffer, 0, buffer.length); + } + + @Override + public void writeShort(int value) { + baos.write((byte) value >>> 8); + baos.write((byte) value); + } + + @Override + public void writeInt(int value) { + baos.write((byte)((int)(value >>> 24))); + baos.write((byte)((int)(value >>> 16))); + baos.write((byte)((int)(value >>> 8))); + baos.write((byte)((int)(value))); + } + + @Override + public void writeLong(long value) { + baos.write((byte)((int)(value >>> 56))); + baos.write((byte)((int)(value >>> 48))); + baos.write((byte)((int)(value >>> 40))); + baos.write((byte)((int)(value >>> 32))); + baos.write((byte)((int)(value >>> 24))); + baos.write((byte)((int)(value >>> 16))); + baos.write((byte)((int)(value >>> 8))); + baos.write((byte)((int)(value))); + } + + @Override + public void writeFloat(float value) { + writeInt(Float.floatToIntBits(value)); + } + + @Override + public void writeDouble(double value) { + writeLong(Double.doubleToLongBits(value)); + } + + @Override + public void skipBytes(int count) { + throw new UnsupportedOperationException(); + } + + public byte[] toByteArray() { + return baos.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/NetStream_p125.java b/proto125/src/main/java/mc/core/network/proto_125/NetStream_p125.java new file mode 100644 index 0000000..548f07a --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/NetStream_p125.java @@ -0,0 +1,60 @@ +/* + * DmitriyMX + * 2018-04-13 + */ +package mc.core.network.proto_125; + +import lombok.extern.slf4j.Slf4j; +import mc.core.network.NetStream; + +import java.nio.charset.StandardCharsets; + +@Slf4j +public abstract class NetStream_p125 extends NetStream { + @Override + public String readString() { + int size = readShort() * 2; + if (size == 0) { + log.warn("String zero length??"); + return ""; + } + + byte[] bytes = new byte[size]; + readBytes(bytes); + return new String(bytes, StandardCharsets.UTF_16BE); + } + + @Override + public void writeString(String value) { + if (value.length() > Short.MAX_VALUE) { + log.warn("String \"{}\" too long!", value); + byte[] buf = value.substring(0, Short.MAX_VALUE).getBytes(StandardCharsets.UTF_16BE); + writeShort(Short.MAX_VALUE); + writeBytes(buf); + } else { + byte[] buf = value.getBytes(StandardCharsets.UTF_16BE); + writeShort(value.length()); + writeBytes(buf); + } + } + + @Override + public int readVarInt() { + return readInt(); + } + + @Override + public void writeVarInt(int value) { + writeInt(value); + } + + @Override + public long readLong() { + return 0; //FIXME + } + + @Override + public void writeUnsignedByte(int value) { + writeByte(value); //FIXME + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/AnimationPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/AnimationPacket.java new file mode 100644 index 0000000..0daf4f4 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/AnimationPacket.java @@ -0,0 +1,43 @@ +/* + * DmitriyMX + * 2018-05-22 + */ +package mc.core.network.proto_125.packets; + +import lombok.*; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@Getter +@ToString +public class AnimationPacket implements SCPacket, CSPacket { + public static final int NO_ANIMATION = 0, + SWING_ARM = 1, + DAMAGE = 2, + LEAVE_BED = 3, + EAT_FOOD = 5, + CROUCH = 104, + UNCROUCH = 105; + + private int id; + private int animation; + + @Override + public void readSelf(NetStream netStream) { + id = netStream.readInt(); + animation = netStream.readByte(); + } + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + netStream.writeInt(id); + netStream.writeByte(animation); + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/ChatMessagePacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/ChatMessagePacket.java new file mode 100644 index 0000000..7ae72e8 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/ChatMessagePacket.java @@ -0,0 +1,32 @@ +/* + * DmitriyMX + * 2018-04-30 + */ +package mc.core.network.proto_125.packets; + +import lombok.*; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@ToString +public class ChatMessagePacket implements SCPacket, CSPacket { + private String message; + + @Override + public void readSelf(NetStream netStream) { + message = netStream.readString(); + } + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + netStream.writeString(message); + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/ChunkAllocationPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/ChunkAllocationPacket.java new file mode 100644 index 0000000..daf4ca2 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/ChunkAllocationPacket.java @@ -0,0 +1,28 @@ +/* + * DmitriyMX + * 2018-04-20 + */ +package mc.core.network.proto_125.packets; + +import lombok.Setter; +import lombok.ToString; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@Setter +@ToString +public class ChunkAllocationPacket implements SCPacket { + private int x, z; + private boolean initChunk; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(x); + netStream.writeInt(z); + netStream.writeBoolean(initChunk); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/ChunkDataPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/ChunkDataPacket.java new file mode 100644 index 0000000..6f088e3 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/ChunkDataPacket.java @@ -0,0 +1,133 @@ +/* + * DmitriyMX + * 2018-04-20 + */ +package mc.core.network.proto_125.packets; + +import lombok.Setter; +import lombok.ToString; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; +import mc.core.world.Chunk; + +import java.nio.ByteBuffer; +import java.util.zip.Deflater; + +@Setter +@ToString +public class ChunkDataPacket implements SCPacket { + private static final int blocktypeSize = 4096, + metadataSize = 2048, + blocklightSize = 2048, + skylightSize = 2048, + additionSize = 2048, + biomeSize = 256; + private static final int dataSize = blocktypeSize+metadataSize+blocklightSize+skylightSize+additionSize+biomeSize; + + private int x, z; + private boolean needInitChunk; + private int yMin,yMax; + private byte[] compressData; + + public void setChunk(Chunk chunk) { + ByteBuffer chunkData = ByteBuffer.allocate(dataSize); + + /* + * 0 - blocktype + * 1 - metadata + * 2 - blocklight + * 3 - skylight + * 4 - addition + * 5 - biome + */ + int[] idx = new int[6]; + + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + // Block type + int offset = 0; + chunkData.put((idx[0]++), (byte) chunk.getBlockType(x, y, z)); + + // Block metadata + offset = offset+blocktypeSize; + if ((idx[1] % 2) > 0) { + int i = (int) ((((idx[1]++) + 1) / 2d) - 1d); + byte b = chunkData.get(offset+i); + b = (byte)((chunk.getBlockMetadata(x, y, z) << 4) | b); + chunkData.put(offset+i, b); + } else { + int i = (int) ((((idx[1]++) + 1) / 2d) - .5d); + chunkData.put(offset+i, (byte) chunk.getBlockMetadata(x, y, z)); + } + + // Block light + offset = offset+metadataSize; + if ((idx[2] % 2) > 0) { + int i = (int) ((((idx[2]++) + 1) / 2d) - 1d); + byte b = chunkData.get(offset+i); + b = (byte)((b << 4) | (byte)chunk.getBlockLight(x, y, z)); + chunkData.put(offset+i, b); + } else { + int i = (int) ((((idx[2]++) + 1) / 2d) - .5d); + chunkData.put(offset+i, (byte) chunk.getBlockLight(x, y, z)); + } + + // Sky light + offset = offset+blocklightSize; + if ((idx[3] % 2) > 0) { + int i = (int) ((((idx[3]++) + 1) / 2d) - 1d); + byte b = chunkData.get(offset+i); + b = (byte)((b << 4) | (byte)chunk.getSkyLight(x, y, z)); + chunkData.put(offset+i, b); + } else { + int i = (int) ((((idx[3]++) + 1) / 2d) - .5d); + chunkData.put(offset+i, (byte) chunk.getSkyLight(x, y, z)); + } + + // Addition + offset = offset+skylightSize; + if ((idx[4] % 2) > 0) { + int i = (int) ((((idx[4]++) + 1) / 2d) - 1d); + byte b = chunkData.get(offset+i); + b = (byte)((b << 4) | (byte)chunk.getAddition(x, y, z)); + chunkData.put(offset+i, b); + } else { + int i = (int) ((((idx[4]++) + 1) / 2d) - .5d); + chunkData.put(offset+i, (byte) chunk.getAddition(x, y, z)); + } + + // Biome + if (idx[5] == 256) continue; + offset = offset+additionSize; + chunkData.put(offset+(idx[5]++), (byte) chunk.getBiome(x, z)); + } + } + } + + Deflater zlib = new Deflater(Deflater.DEFAULT_COMPRESSION); + zlib.setInput(chunkData.array()); + zlib.finish(); + byte[] preCompileData = new byte[dataSize]; + int compressSize = zlib.deflate(preCompileData); + + compressData = new byte[compressSize]; + System.arraycopy(preCompileData, 0, compressData, 0, compressSize); + } + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(x); + netStream.writeInt(z); + netStream.writeBoolean(needInitChunk); + netStream.writeShort(yMin); + netStream.writeShort(yMax); + netStream.writeInt(compressData.length); + netStream.writeInt(0); + netStream.writeBytes(compressData); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/DestroyEntityPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/DestroyEntityPacket.java new file mode 100644 index 0000000..c2f48b1 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/DestroyEntityPacket.java @@ -0,0 +1,23 @@ +/* + * DmitriyMX + * 2018-05-11 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.ToString; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@AllArgsConstructor +@ToString +public class DestroyEntityPacket implements SCPacket { + private final int id; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + netStream.writeInt(id); + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookHeadPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookHeadPacket.java new file mode 100644 index 0000000..bcec432 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookHeadPacket.java @@ -0,0 +1,31 @@ +/* + * DmitriyMX + * 2018-05-12 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@ToString +public class EntityLookHeadPacket implements SCPacket { + private int id; + private double yaw; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(id); + netStream.writeByte((byte)(int)((yaw * 256f) / 360f)); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookPacket.java new file mode 100644 index 0000000..448a04d --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookPacket.java @@ -0,0 +1,33 @@ +/* + * DmitriyMX + * 2018-05-12 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; +import mc.core.player.Look; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@ToString +public class EntityLookPacket implements SCPacket { + private int id; + private Look look; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(id); + netStream.writeByte((byte)(int)((look.getYaw() * 256f) / 360f)); + netStream.writeByte((byte)(int)((look.getPitch() * 256f) / 360f)); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookRelativeMovePacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookRelativeMovePacket.java new file mode 100644 index 0000000..e0eef27 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityLookRelativeMovePacket.java @@ -0,0 +1,38 @@ +/* + * DmitriyMX + * 2018-05-12 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import mc.core.Location; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; +import mc.core.player.Look; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@ToString +public class EntityLookRelativeMovePacket implements SCPacket { + private int id; + private Location location; + private Look look; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(id); + netStream.writeByte((byte) (location.getX() * 32d)); + netStream.writeByte((byte) (location.getY() * 32d)); + netStream.writeByte((byte) (location.getZ() * 32d)); + netStream.writeByte((byte)(int)((look.getYaw() * 256f) / 360f)); + netStream.writeByte((byte)(int)((look.getPitch() * 256f) / 360f)); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/EntityRelativeMovePacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityRelativeMovePacket.java new file mode 100644 index 0000000..18ab67c --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityRelativeMovePacket.java @@ -0,0 +1,34 @@ +/* + * DmitriyMX + * 2018-05-11 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import mc.core.Location; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@ToString +public class EntityRelativeMovePacket implements SCPacket { + private int id; + private Location location; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(id); + netStream.writeByte((byte) (location.getX() * 32d)); + netStream.writeByte((byte) (location.getY() * 32d)); + netStream.writeByte((byte) (location.getZ() * 32d)); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/EntityTeleportPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityTeleportPacket.java new file mode 100644 index 0000000..b3034c3 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/EntityTeleportPacket.java @@ -0,0 +1,38 @@ +/* + * DmitriyMX + * 2018-05-11 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import mc.core.Location; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; +import mc.core.player.Look; + +@AllArgsConstructor +@NoArgsConstructor +@Setter +@ToString +public class EntityTeleportPacket implements SCPacket { + private int id; + private Location location; + private Look look; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(id); + netStream.writeInt((int) (location.getBlockX() * 32d)); + netStream.writeInt((int) (location.getBlockY() * 32d)); + netStream.writeInt((int) (location.getBlockZ() * 32d)); + netStream.writeByte((byte)(int)((look.getYaw() * 256f) / 360f)); + netStream.writeByte((byte)(int)((look.getPitch() * 256f) / 360f)); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/HandshakePacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/HandshakePacket.java new file mode 100644 index 0000000..c9afd57 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/HandshakePacket.java @@ -0,0 +1,42 @@ +/* + * DmitriyMX + * 2018-04-10 + */ +package mc.core.network.proto_125.packets; + +import lombok.Getter; +import lombok.ToString; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@Getter +@ToString +public class HandshakePacket implements CSPacket, SCPacket { + private String playerName; + private String host; + private int port; + + @Override + public void readSelf(NetStream netStream) { + String[] str = netStream.readString().split(";"); + + playerName = str[0]; + if (str[1].contains(":")) { + str = str[1].split(":"); + host = str[0]; + port = Integer.parseInt(str[1]); + } else { + host = str[1]; + port = 25565; + } + } + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + netStream.writeString("-"); + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/KeepAlivePacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/KeepAlivePacket.java new file mode 100644 index 0000000..3a151eb --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/KeepAlivePacket.java @@ -0,0 +1,27 @@ +/* + * DmitriyMX + * 2018-04-21 + */ +package mc.core.network.proto_125.packets; + +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +import java.util.Random; + +public class KeepAlivePacket implements SCPacket, CSPacket { + private static final Random rand = new Random(); + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + netStream.writeInt(rand.nextInt(Integer.MAX_VALUE)); + return netStream.toByteArray(); + } + + @Override + public void readSelf(NetStream netStream) { + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/KickPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/KickPacket.java new file mode 100644 index 0000000..10e080e --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/KickPacket.java @@ -0,0 +1,44 @@ +/* + * DmitriyMX + * 2018-04-10 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@Slf4j +@NoArgsConstructor +@AllArgsConstructor +@Setter +@ToString +public class KickPacket implements SCPacket, CSPacket { + private String reason; + + public String getReason() { + return (reason == null ? "" : reason); + } + + @Override + public void readSelf(NetStream netStream) { + try { + reason = netStream.readString(); + } catch (NegativeArraySizeException e) { + log.warn("Invalid packet"); + } + } + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + netStream.writeString(reason); + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/LoginPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/LoginPacket.java new file mode 100644 index 0000000..2567d73 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/LoginPacket.java @@ -0,0 +1,57 @@ +/* + * DmitriyMX + * 2018-04-10 + */ +package mc.core.network.proto_125.packets; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; +import mc.core.player.PlayerMode; + +@ToString +public class LoginPacket implements CSPacket, SCPacket { + @Getter + private int protocol; + @Getter + private String playerName; + + @Setter + private int playerId; + @Setter + private String levelType; + @Setter + private PlayerMode defaultPlayerMode; + @Setter + private int dimension; + @Setter + private int difficulty; + @Setter + private int maxPlayers; + + @Override + public void readSelf(NetStream netStream) { + protocol = netStream.readInt(); + playerName = netStream.readString(); + } + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(playerId); + netStream.writeString(""); + netStream.writeString(levelType); + netStream.writeInt(defaultPlayerMode.getId()); + netStream.writeInt(dimension); + netStream.writeByte(difficulty); + netStream.writeByte(0); + netStream.writeByte(maxPlayers); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/PacketManager.java b/proto125/src/main/java/mc/core/network/proto_125/packets/PacketManager.java new file mode 100644 index 0000000..cd4a493 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/PacketManager.java @@ -0,0 +1,48 @@ +/* + * DmitriyMX + * 2018-04-10 + */ +package mc.core.network.proto_125.packets; + +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import mc.core.network.CSPacket; +import mc.core.network.SCPacket; + +public class PacketManager { + private static final BiMap> packetMap = ImmutableBiMap.>builder() + .put(0x00, KeepAlivePacket.class) + .put(0x01, LoginPacket.class) + .put(0x02, HandshakePacket.class) + .put(0x03, ChatMessagePacket.class) + .put(0x04, TimeUpdatePacket.class) + .put(0x06, SpawnPositionPacket.class) + .put(0x07, UseEntityPacket.class) + .put(0x0B, PlayerPositionPacket.class) + .put(0x0C, PlayerLookPacket.class) + .put(0x0D, PositionAndLookPacket.class) + .put(0x12, AnimationPacket.class) + .put(0x14, SpawnNamedEntityPacket.class) + .put(0x1D, DestroyEntityPacket.class) + .put(0x1F, EntityRelativeMovePacket.class) + .put(0x20, EntityLookPacket.class) + .put(0x21, EntityLookRelativeMovePacket.class) + .put(0x22, EntityTeleportPacket.class) + .put(0x23, EntityLookHeadPacket.class) + .put(0x32, ChunkAllocationPacket.class) + .put(0x33, ChunkDataPacket.class) + .put(0xC9, PlayerInfoPacket.class) + .put(0xCA, PlayerAbilitiesPacket.class) + .put(0xFE, PingPacket.class) + .put(0xFF, KickPacket.class) + .build(); + + @SuppressWarnings("unchecked") + public static Class getClientSidePacket(int id) { + return (Class) packetMap.get(id); + } + + public static Integer getServirSidePacket(Class clazz) { + return packetMap.inverse().get(clazz); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/PingPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/PingPacket.java new file mode 100644 index 0000000..1e5ca8e --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/PingPacket.java @@ -0,0 +1,16 @@ +/* + * DmitriyMX + * 2018-04-10 + */ +package mc.core.network.proto_125.packets; + +import lombok.ToString; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; + +@ToString +public class PingPacket implements CSPacket { + @Override + public void readSelf(NetStream netStream) { + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerAbilitiesPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerAbilitiesPacket.java new file mode 100644 index 0000000..9a979ab --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerAbilitiesPacket.java @@ -0,0 +1,43 @@ +/* + * DmitriyMX + * 2018-04-19 + */ +package mc.core.network.proto_125.packets; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@Getter +@Setter +@ToString +public class PlayerAbilitiesPacket implements SCPacket, CSPacket { + private boolean godMode = false; + private boolean flying = false; + private boolean canFly = false; + private boolean instantDestroyBlocks = false; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeBoolean(godMode); + netStream.writeBoolean(flying); + netStream.writeBoolean(canFly); + netStream.writeBoolean(instantDestroyBlocks); + + return netStream.toByteArray(); + } + + @Override + public void readSelf(NetStream netStream) { + godMode = netStream.readBoolean(); + flying = netStream.readBoolean(); + canFly = netStream.readBoolean(); + instantDestroyBlocks = netStream.readBoolean(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerInfoPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerInfoPacket.java new file mode 100644 index 0000000..a70a84d --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerInfoPacket.java @@ -0,0 +1,29 @@ +/* + * DmitriyMX + * 2018-04-19 + */ +package mc.core.network.proto_125.packets; + +import lombok.Setter; +import lombok.ToString; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@Setter +@ToString +public class PlayerInfoPacket implements SCPacket { + private String playerName; + private boolean online; + private int ping; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeString(playerName); + netStream.writeBoolean(online); + netStream.writeShort(ping); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerLookPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerLookPacket.java new file mode 100644 index 0000000..9eb3f89 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerLookPacket.java @@ -0,0 +1,24 @@ +/* + * DmitriyMX + * 2018-04-22 + */ +package mc.core.network.proto_125.packets; + +import lombok.Getter; +import lombok.ToString; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; + +@Getter +@ToString +public class PlayerLookPacket implements CSPacket { + private float yaw, pitch; + private boolean onGround; + + @Override + public void readSelf(NetStream netStream) { + yaw = netStream.readFloat(); + pitch = netStream.readFloat(); + onGround = netStream.readBoolean(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerPositionPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerPositionPacket.java new file mode 100644 index 0000000..a41fefc --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/PlayerPositionPacket.java @@ -0,0 +1,27 @@ +/* + * DmitriyMX + * 2018-04-22 + */ +package mc.core.network.proto_125.packets; + +import lombok.Getter; +import lombok.ToString; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; + +@Getter +@ToString +public class PlayerPositionPacket implements CSPacket { + private double x, y, z; + private double stance; + private boolean onGround; + + @Override + public void readSelf(NetStream netStream) { + this.x = netStream.readDouble(); + this.y = netStream.readDouble(); + this.stance = netStream.readDouble(); + this.z = netStream.readDouble(); + this.onGround = netStream.readBoolean(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/PositionAndLookPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/PositionAndLookPacket.java new file mode 100644 index 0000000..8761e76 --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/PositionAndLookPacket.java @@ -0,0 +1,54 @@ +/* + * DmitriyMX + * 2018-04-15 + */ +package mc.core.network.proto_125.packets; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import mc.core.Location; +import mc.core.player.Look; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@Getter +@Setter +@ToString +public class PositionAndLookPacket implements SCPacket, CSPacket { + private Location location; + private double stance; + private Look look; + private boolean onGround; + + @Override + public void readSelf(NetStream netStream) { + double x = netStream.readDouble(); + double y = netStream.readDouble(); + stance = netStream.readDouble(); + double z = netStream.readDouble(); + float yaw = netStream.readFloat(); + float pitch = netStream.readFloat(); + onGround = netStream.readBoolean(); + + location = new Location(x, y, z); + look = new Look(yaw, pitch); + } + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeDouble(location.getX()); + netStream.writeDouble(location.getY()); + netStream.writeDouble(stance); + netStream.writeDouble(location.getZ()); + netStream.writeFloat(look.getYaw()); + netStream.writeFloat(look.getPitch()); + netStream.writeBoolean(onGround); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/SpawnNamedEntityPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/SpawnNamedEntityPacket.java new file mode 100644 index 0000000..845d05f --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/SpawnNamedEntityPacket.java @@ -0,0 +1,40 @@ +/* + * DmitriyMX + * 2018-04-30 + */ +package mc.core.network.proto_125.packets; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import mc.core.Location; +import mc.core.player.Look; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@Getter +@Setter +@ToString +public class SpawnNamedEntityPacket implements SCPacket { + private int id; + private String entityName; + private Location position; + private Look look; + private final int currentItem = 0; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt(id); + netStream.writeString(entityName); + netStream.writeInt((int) (position.getBlockX() * 32d)); + netStream.writeInt((int) (position.getBlockY() * 32d)); + netStream.writeInt((int) (position.getBlockZ() * 32d)); + netStream.writeByte((byte)(int)((look.getYaw() * 256f) / 360f)); + netStream.writeByte((byte)(int)((look.getPitch() * 256f) / 360f)); + netStream.writeShort(currentItem); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/SpawnPositionPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/SpawnPositionPacket.java new file mode 100644 index 0000000..c5e0c0c --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/SpawnPositionPacket.java @@ -0,0 +1,28 @@ +/* + * DmitriyMX + * 2018-04-19 + */ +package mc.core.network.proto_125.packets; + +import lombok.Setter; +import lombok.ToString; +import mc.core.Location; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@Setter +@ToString +public class SpawnPositionPacket implements SCPacket { + private Location location; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + + netStream.writeInt((int) location.getX()); + netStream.writeInt((int) location.getY()); + netStream.writeInt((int) location.getZ()); + + return netStream.toByteArray(); + } +} diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/TimeUpdatePacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/TimeUpdatePacket.java new file mode 100644 index 0000000..46bfeee --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/TimeUpdatePacket.java @@ -0,0 +1,36 @@ +/* + * DmitriyMX + * 2018-04-21 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.ByteArrayOutputNetStream; + +@NoArgsConstructor +@AllArgsConstructor +@Setter +@ToString +public class TimeUpdatePacket implements SCPacket, CSPacket { + private long time; + + @Override + public byte[] toByteArray() { + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + netStream.writeLong(time); + return netStream.toByteArray(); + } + + // нахрена вообще клиент шлет нам этот пакет??? + @Override + public void readSelf(NetStream netStream) { + netStream.skipBytes(8); + } +} + diff --git a/proto125/src/main/java/mc/core/network/proto_125/packets/UseEntityPacket.java b/proto125/src/main/java/mc/core/network/proto_125/packets/UseEntityPacket.java new file mode 100644 index 0000000..b36fd2a --- /dev/null +++ b/proto125/src/main/java/mc/core/network/proto_125/packets/UseEntityPacket.java @@ -0,0 +1,29 @@ +/* + * DmitriyMX + * 2018-05-23 + */ +package mc.core.network.proto_125.packets; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@ToString +public class UseEntityPacket implements CSPacket { + private int playerId; + private int targetId; + private boolean leftMouseButton; + + @Override + public void readSelf(NetStream netStream) { + playerId = netStream.readInt(); + targetId = netStream.readInt(); + leftMouseButton = netStream.readBoolean(); + } +} diff --git a/proto125_netty/README.MD b/proto125_netty/README.MD new file mode 100644 index 0000000..7f4d9e2 --- /dev/null +++ b/proto125_netty/README.MD @@ -0,0 +1,29 @@ +# Protocol 1.2.5 (Netty impl.) + +Реализация протокола "1.2.5" на сетевом движке Netty. + +## Spring beans + +### NettyServer + +Bean: + +```xml + + + + + + + + + +``` + +`workerGroupCount` - максимальное количество потоков для обработки соединений + +Для логирования содержимого пакетов, можно добавить следующий bean: + +```xml + +``` \ No newline at end of file diff --git a/proto125_netty/build.gradle b/proto125_netty/build.gradle new file mode 100644 index 0000000..8876ed3 --- /dev/null +++ b/proto125_netty/build.gradle @@ -0,0 +1,14 @@ +group 'mc' +version '1.0-SNAPSHOT' + +ext { + netty_version = '4.1.22.Final' +} + +dependencies { + /* Protocol 1.2.5 */ + compile_excludeCopy project(':proto125') + + /* Netty */ + compile (group: 'io.netty', name: 'netty-all', version: netty_version) +} diff --git a/proto125_netty/src/main/java/mc/core/network/proto_125/netty/EventListener.java b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/EventListener.java new file mode 100644 index 0000000..4c64088 --- /dev/null +++ b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/EventListener.java @@ -0,0 +1,42 @@ +/* + * DmitriyMX + * 2018-05-02 + */ +package mc.core.network.proto_125.netty; + +import com.google.common.eventbus.Subscribe; +import lombok.RequiredArgsConstructor; +import mc.core.Config; +import mc.core.player.Player; +import mc.core.player.PlayerManager; +import mc.core.events.LoginEvent; +import mc.core.events.ServerPingEvent; + +import java.util.Optional; + +@RequiredArgsConstructor +public class EventListener { + private final Config config; + private final PlayerManager playerManager; + + @Subscribe + public void onServerPingEvent(ServerPingEvent event) { + if (event.isLastProcess() || event.isCanceled()) return; + + event.setDescription(config.getDescriptionServer()); + event.setOnline(playerManager.getCountOnlinePlayers()); + event.setMaxOnline(config.getMaxPlayers()); + } + + @Subscribe + public void onLoginEvent(LoginEvent event) { + if (event.isLastProcess()) return; + + Optional optPlayer = playerManager.getPlayer(event.getPlayerName()); + + if (optPlayer.isPresent() && optPlayer.get().isOnline()) { + event.setDeny(true); + event.setDenyReason("Player is exists in server"); + } + } +} diff --git a/proto125_netty/src/main/java/mc/core/network/proto_125/netty/NettyServer.java b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/NettyServer.java new file mode 100644 index 0000000..74dda1f --- /dev/null +++ b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/NettyServer.java @@ -0,0 +1,101 @@ +/* + * DmitriyMX + * 2018-04-10 + */ +package mc.core.network.proto_125.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +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 lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import mc.core.Config; +import mc.core.player.PlayerManager; +import mc.core.events.EventBusGetter; +import mc.core.network.Server; +import mc.core.network.StartServerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +public class NettyServer implements Server { + @Autowired + private ApplicationContext applicationContext; + @Setter + private String host; + @Setter + private int port; + @Setter + private int workerGroupCount = 0; + private EventLoopGroup bossGroup, workerGroup; + private EventListener eventListener; + + @PostConstruct + public void init() { + eventListener = new EventListener( + applicationContext.getBean(Config.class), + applicationContext.getBean(PlayerManager.class)); + EventBusGetter.INSTANCE.register(eventListener); + } + + @PreDestroy + public void destruct() { + EventBusGetter.INSTANCE.unregister(eventListener); + } + + private ChannelInitializer buildChannelInitializer() { + return new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel socketChannel) { + Map beans = applicationContext.getBeansOfType(ChannelHandler.class); + beans.entrySet().stream() + .sorted((e1, e2) -> e1.getKey().compareToIgnoreCase(e2.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + .forEach(socketChannel.pipeline()::addLast); + } + }; + } + + private ServerBootstrap buildServerBootstrap() { + ServerBootstrap bootstrap = new ServerBootstrap(); + + bootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(buildChannelInitializer()); + + return bootstrap; + } + + @Override + public void start() throws StartServerException { + log.info("Use protocol 1.2.5"); + bossGroup = new NioEventLoopGroup(1); + workerGroup = new NioEventLoopGroup(workerGroupCount); + + ServerBootstrap serverBootstrap = buildServerBootstrap(); + + log.info("Start server: {}:{}", host, port); + try { + serverBootstrap.bind(host, port).sync().channel().closeFuture().sync(); + } catch (InterruptedException e) { + throw new StartServerException(e); + } + } + + @Override + public void stop() { + log.info("Server shutdown"); + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } +} diff --git a/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketDecoder.java b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketDecoder.java new file mode 100644 index 0000000..d2f2f06 --- /dev/null +++ b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketDecoder.java @@ -0,0 +1,41 @@ +/* + * DmitriyMX + * 2018-03-25 + */ +package mc.core.network.proto_125.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import lombok.extern.slf4j.Slf4j; +import mc.core.network.CSPacket; +import mc.core.network.NetStream; +import mc.core.network.proto_125.netty.wrappers.WrapperNetStream; +import mc.core.network.proto_125.packets.PacketManager; + +import java.util.List; + +@Slf4j +public class PacketDecoder extends ByteToMessageDecoder { + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + log.debug("ByteBuf readableBytes: {}", in.readableBytes()); + int id = in.readUnsignedByte(); + log.debug("Pkt-Id: {} / 0x{}", id, Integer.toHexString(id).toUpperCase()); + + Class packetClass = PacketManager.getClientSidePacket(id); + if (packetClass != null) { + NetStream netStream = new WrapperNetStream(in); + CSPacket packet = packetClass.newInstance(); + packet.readSelf(netStream); + + out.add(packet); + log.debug("{}: {}", packet.getClass().getSimpleName(), packet.toString()); + } else { + log.debug("Unknown packet"); + } + + if (in.readableBytes() > 0) + in.skipBytes(in.readableBytes()); + } +} diff --git a/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketEncoder.java b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketEncoder.java new file mode 100644 index 0000000..dc6ea89 --- /dev/null +++ b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketEncoder.java @@ -0,0 +1,29 @@ +/* + * DmitriyMX + * 2018-04-10 + */ +package mc.core.network.proto_125.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import lombok.extern.slf4j.Slf4j; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.packets.PacketManager; + +@Slf4j +public class PacketEncoder extends MessageToByteEncoder { + @Override + protected void encode(ChannelHandlerContext ctx, SCPacket pkt, ByteBuf out) throws Exception { + log.debug("{}: {}", pkt.getClass().getSimpleName(), pkt.toString()); + Integer id = PacketManager.getServirSidePacket(pkt.getClass()); + if (id == null) { + log.warn("Not defined ID packet \"{}\"", pkt.getClass().getSimpleName()); + return; + } + byte[] bytes = pkt.toByteArray(); + + out.writeByte(id); + out.writeBytes(bytes); + } +} diff --git a/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketHandler.java b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketHandler.java new file mode 100644 index 0000000..41ce17c --- /dev/null +++ b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/PacketHandler.java @@ -0,0 +1,313 @@ +/* + * DmitriyMX + * 2018-04-10 + */ + +package mc.core.network.proto_125.netty; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.util.AttributeKey; +import lombok.extern.slf4j.Slf4j; +import mc.core.*; +import mc.core.chat.ChatProcessor; +import mc.core.chat.ChatStyle; +import mc.core.events.*; +import mc.core.network.CSPacket; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.netty.wrappers.WrapperNetChannel; +import mc.core.network.proto_125.packets.*; +import mc.core.player.Look; +import mc.core.player.Player; +import mc.core.player.PlayerManager; +import mc.core.player.PlayerMode; +import mc.core.world.World; +import org.springframework.beans.factory.annotation.Autowired; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +@Slf4j +public class PacketHandler extends SimpleChannelInboundHandler { + private static final AttributeKey ATTR_PLAYER = AttributeKey.newInstance("ATTR_PLAYER"); + @Autowired + private Config config; + @Autowired + private PlayerManager playerManager; + @Autowired + private World world; + @Autowired + private ChatProcessor chatProcessor; + + @Override + public void channelInactive(ChannelHandlerContext context) throws Exception { + super.channelInactive(context); + Player player = context.channel().attr(ATTR_PLAYER).get(); + if (player != null) { + playerManager.leftServer(player); + player.setChannel(null); + playerManager.getBroadcastChannel().writeAndFlush(new DestroyEntityPacket(player.getId())); + } + context.channel().attr(ATTR_PLAYER).set(null); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, CSPacket packet) throws Exception { + Optional optionalMethod = Arrays.stream(this.getClass().getDeclaredMethods()) + .filter(method -> method.getName().equals("on" + packet.getClass().getSimpleName()) + && method.getParameterCount() == 2 + && method.getParameterTypes()[0].isAssignableFrom(Channel.class) + && method.getParameterTypes()[1].isAssignableFrom(packet.getClass())) + .findFirst(); + + if (optionalMethod.isPresent()) { + Method method = optionalMethod.get(); + method.invoke(this, ctx.channel(), packet); + } + } + + private void onPingPacket(Channel channel, PingPacket packet) { + ServerPingEvent event = new ServerPingEvent(channel.remoteAddress()); + EventBusGetter.INSTANCE.post(event); + + if (event.isCanceled()) { + channel.disconnect(); + } else { + String response = String.format("%s%s%d%s%d", + event.getDescription(), ChatStyle.SPECIAL_CHAR, + event.getOnline(), ChatStyle.SPECIAL_CHAR, + event.getMaxOnline() + ); + + KickPacket pkt = new KickPacket(); + pkt.setReason(response); + channel.writeAndFlush(pkt); + } + } + + private void onHandshakePacket(Channel channel, HandshakePacket packet) { + channel.writeAndFlush(packet); + } + + private void onLoginPacket(Channel channel, LoginPacket packet) { + LoginEvent event = new LoginEvent(channel.remoteAddress()); + event.setPlayerName(packet.getPlayerName()); + EventBusGetter.INSTANCE.post(event); + + if (event.isDeny()) { + channel.writeAndFlush(new KickPacket(event.getDenyReason())) + .addListener(ChannelFutureListener.CLOSE); + } else { + Player player = playerManager.getPlayer(packet.getPlayerName()) + .orElseGet(() -> playerManager.createPlayer( + packet.getPlayerName(), + world.getSpawn(), + new Look(0f, 0f))); + + // Response login + packet.setPlayerId(player.getId()); + packet.setLevelType("flat"); + packet.setDefaultPlayerMode(PlayerMode.CREATIVE); + packet.setDimension(0/*Overworld*/); + packet.setDifficulty(0/*Peaceful*/); + packet.setMaxPlayers(config.getMaxPlayers()); + channel.write(packet); + + // send Spawn position + SpawnPositionPacket spawnPkt = new SpawnPositionPacket(); + spawnPkt.setLocation(world.getSpawn()); + channel.write(spawnPkt); + + // send Player abilities + PlayerAbilitiesPacket abilitiesPkt = new PlayerAbilitiesPacket(); + abilitiesPkt.setCanFly(true); + abilitiesPkt.setFlying(true); + abilitiesPkt.setGodMode(true); + abilitiesPkt.setInstantDestroyBlocks(true); + channel.write(abilitiesPkt); + + // send Chunk allocation + ChunkAllocationPacket chInitPkt = new ChunkAllocationPacket(); + chInitPkt.setX(0); + chInitPkt.setZ(0); + chInitPkt.setInitChunk(true); + channel.write(chInitPkt); + + // send Chunk data + ChunkDataPacket chDataPkt = new ChunkDataPacket(); + chDataPkt.setX(0); + chDataPkt.setZ(0); + chDataPkt.setChunk(world.getChunk(0, 0)); + chDataPkt.setNeedInitChunk(true); + chDataPkt.setYMin(1); + chDataPkt.setYMax(0); + channel.write(chDataPkt); + + // send Position and look + PositionAndLookPacket posLookPkt = new PositionAndLookPacket(); + posLookPkt.setLocation(player.getLocation()); + posLookPkt.setStance(player.getLocation().getY() + 1.64d); + posLookPkt.setLook(player.getLook()); + posLookPkt.setOnGround(false); + channel.write(posLookPkt); + channel.flush(); + + // send Spawn named entity + SpawnNamedEntityPacket spawnPlayer = new SpawnNamedEntityPacket(); + spawnPlayer.setId(player.getId()); + spawnPlayer.setEntityName(player.getName()); + spawnPlayer.setPosition(player.getLocation()); + spawnPlayer.setLook(player.getLook()); + playerManager.getBroadcastChannel().writeAndFlush(spawnPlayer); + + // send Spawn named entity (another players) + List players = playerManager.getPlayers(); + players.forEach(pl -> { + SpawnNamedEntityPacket spawnAnotherPlayer = new SpawnNamedEntityPacket(); + spawnAnotherPlayer.setId(pl.getId()); + spawnAnotherPlayer.setEntityName(pl.getName()); + spawnAnotherPlayer.setPosition(pl.getLocation()); + spawnAnotherPlayer.setLook(pl.getLook()); + channel.write(spawnAnotherPlayer); + }); + channel.flush(); + + // join server + channel.attr(ATTR_PLAYER).set(player); + player.setChannel(new WrapperNetChannel(channel)); + playerManager.joinServer(player); + + // send Player info + players.forEach(pl -> { + PlayerInfoPacket infoPkt = new PlayerInfoPacket(); + infoPkt.setPlayerName(pl.getName()); + infoPkt.setOnline(true); + infoPkt.setPing(4); + playerManager.getBroadcastChannel().writeAndFlush(infoPkt); + }); + } + } + + private void onKickPacket(Channel channel, KickPacket packet) { + if (packet.getReason().equals("Quitting")) { + channel.disconnect(); + } + } + + private void onPlayerPositionPacket(Channel channel, PlayerPositionPacket packet) { + Player player = channel.attr(ATTR_PLAYER).get(); + PlayerPositionEvent event = new PlayerPositionEvent(player); + event.setNewPosition(new Location(packet.getX(), packet.getY(), packet.getZ())); + EventBusGetter.INSTANCE.post(event); + + if (!event.isCanceled()) { + Location diffLoc = event.getNewPosition().diff(player.getLocation()); + player.getLocation().set(event.getNewPosition()); + + //TODO если позиция была изменена, нужно оповестить клиент + + final SCPacket pkt; + if ((diffLoc.getBlockX() >= 4 || diffLoc.getBlockX() <= -4) + || (diffLoc.getBlockY() >= 4 || diffLoc.getBlockY() <= -4) + || (diffLoc.getBlockZ() >= 4 || diffLoc.getBlockZ() <= -4)) { + pkt = new EntityTeleportPacket(player.getId(), player.getLocation(), player.getLook()); + } else { + pkt = new EntityRelativeMovePacket(player.getId(), diffLoc); + } + playerManager.getPlayers().stream() + .filter(pl -> pl.getId() != player.getId()) + .forEach(pl -> pl.getChannel().writeAndFlush(pkt)); + } + } + + private void onPlayerLookPacket(Channel channel, PlayerLookPacket packet) { + Player player = channel.attr(ATTR_PLAYER).get(); + PlayerLookEvent event = new PlayerLookEvent(player); + event.setNewLook(new Look(packet.getYaw(), packet.getPitch())); + EventBusGetter.INSTANCE.post(event); + + if (!event.isCanceled()) { + player.getLook().set(event.getNewLook()); + + //TODO если обзор был изменен, нужно оповестить клиент + + final SCPacket pkt1 = new EntityLookPacket(player.getId(), player.getLook()); + final SCPacket pkt2 = new EntityLookHeadPacket(player.getId(), player.getLook().getYaw()); + playerManager.getPlayers().stream() + .filter(pl -> pl.getId() != player.getId()) + .forEach(pl -> { + pl.getChannel().write(pkt1); + pl.getChannel().write(pkt2); + pl.getChannel().flush(); + }); + } + } + + private void onPositionAndLookPacket(Channel channel, PositionAndLookPacket packet) { + Player player = channel.attr(ATTR_PLAYER).get(); + + Location diffLoc = packet.getLocation().diff(player.getLocation()); + player.getLocation().set(packet.getLocation()); + player.getLook().set(packet.getLook()); + + Stream stream = playerManager.getPlayers().stream() + .filter(pl -> pl.getId() != player.getId()); + + if ((diffLoc.getBlockX() >= 4 || diffLoc.getBlockX() <= -4) + || (diffLoc.getBlockY() >= 4 || diffLoc.getBlockY() <= -4) + || (diffLoc.getBlockZ() >= 4 || diffLoc.getBlockZ() <= -4)) { + final SCPacket pkt = new EntityTeleportPacket(player.getId(), player.getLocation(), player.getLook()); + stream.forEach(pl -> pl.getChannel().writeAndFlush(pkt)); + } else { + final SCPacket pkt1 = new EntityLookRelativeMovePacket(player.getId(), diffLoc, player.getLook()); + final SCPacket pkt2 = new EntityLookHeadPacket(player.getId(), player.getLook().getYaw()); + stream.forEach(pl -> { + pl.getChannel().write(pkt1); + pl.getChannel().write(pkt2); + pl.getChannel().flush(); + }); + } + } + + private void onPlayerAbilitiesPacket(Channel channel, PlayerAbilitiesPacket packet) { + Player player = channel.attr(ATTR_PLAYER).get(); + player.setFlying(packet.isFlying()); + } + + private void onChatMessagePacket(Channel channel, ChatMessagePacket packet) { + chatProcessor.process( + channel.attr(ATTR_PLAYER).get(), + packet.getMessage() + ); + } + + private void onAnimationPacket(Channel channel, AnimationPacket packet) { + Player player = channel.attr(ATTR_PLAYER).get(); + playerManager.getPlayers().stream().filter(pl -> !pl.equals(player)).forEach(pl -> { + pl.getChannel().writeAndFlush(packet); + }); + } + + private void onUseEntityPacket(Channel channel, UseEntityPacket packet) { + Optional optPlayer = playerManager.getPlayerById(packet.getPlayerId()); + if (!optPlayer.isPresent()) { + log.debug("Player id {} not found"); + return; + } + Player player = optPlayer.get(); + + optPlayer = playerManager.getPlayerById(packet.getTargetId()); + if (!optPlayer.isPresent()) { + log.debug("Target id {} not found"); + return; + } + Player target = optPlayer.get(); + + log.info("<{}> {} clicked <{}>", player.getName(), (packet.isLeftMouseButton() ? "left" : "right"), target.getName()); + } +} diff --git a/proto125_netty/src/main/java/mc/core/network/proto_125/netty/wrappers/WrapperNetChannel.java b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/wrappers/WrapperNetChannel.java new file mode 100644 index 0000000..d9c7905 --- /dev/null +++ b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/wrappers/WrapperNetChannel.java @@ -0,0 +1,48 @@ +/* + * DmitriyMX + * 2018-04-13 + */ +package mc.core.network.proto_125.netty.wrappers; + +import io.netty.channel.Channel; +import lombok.RequiredArgsConstructor; +import mc.core.network.NetChannel; +import mc.core.network.SCPacket; +import mc.core.network.proto_125.packets.ChatMessagePacket; +import mc.core.network.proto_125.packets.KeepAlivePacket; +import mc.core.network.proto_125.packets.TimeUpdatePacket; + +@RequiredArgsConstructor +public class WrapperNetChannel implements NetChannel { + private final Channel channel; + + @Override + public void sendKeepAlive() { + channel.writeAndFlush(new KeepAlivePacket()); + } + + @Override + public void sendTimeUpdate(long value) { + channel.writeAndFlush(new TimeUpdatePacket(value)); + } + + @Override + public void sendChatMessage(String message) { + channel.writeAndFlush(new ChatMessagePacket(message)); + } + + @Override + public void writeAndFlush(SCPacket pkt) { + channel.writeAndFlush(pkt); + } + + @Override + public void write(SCPacket pkt) { + channel.write(pkt); + } + + @Override + public void flush() { + channel.flush(); + } +} diff --git a/proto125_netty/src/main/java/mc/core/network/proto_125/netty/wrappers/WrapperNetStream.java b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/wrappers/WrapperNetStream.java new file mode 100644 index 0000000..fe8677e --- /dev/null +++ b/proto125_netty/src/main/java/mc/core/network/proto_125/netty/wrappers/WrapperNetStream.java @@ -0,0 +1,104 @@ +/* + * DmitriyMX + * 2018-04-08 + */ +package mc.core.network.proto_125.netty.wrappers; + +import io.netty.buffer.ByteBuf; +import lombok.RequiredArgsConstructor; +import mc.core.network.proto_125.NetStream_p125; + +@RequiredArgsConstructor +public class WrapperNetStream extends NetStream_p125 { + private final ByteBuf byteBuf; + + @Override + public boolean readBoolean() { + return byteBuf.readBoolean(); + } + + @Override + public byte readByte() { + return byteBuf.readByte(); + } + + @Override + public void readBytes(byte[] buffer) { + byteBuf.readBytes(buffer); + } + + @Override + public int readUnsignedByte() { + return byteBuf.readUnsignedByte(); + } + + @Override + public int readUnsignedShort() { + return byteBuf.readUnsignedShort(); + } + + @Override + public short readShort() { + return byteBuf.readShort(); + } + + @Override + public int readInt() { + return byteBuf.readInt(); + } + + @Override + public float readFloat() { + return byteBuf.readFloat(); + } + + @Override + public double readDouble() { + return byteBuf.readDouble(); + } + + @Override + public void writeBoolean(boolean value) { + byteBuf.writeBoolean(value); + } + + @Override + public void writeByte(int value) { + byteBuf.writeByte(value); + } + + @Override + public void writeBytes(byte[] buffer) { + byteBuf.writeBytes(buffer); + } + + @Override + public void writeShort(int value) { + byteBuf.writeShort(value); + } + + @Override + public void writeInt(int value) { + byteBuf.writeInt(value); + } + + @Override + public void writeLong(long value) { + byteBuf.writeLong(value); + } + + @Override + public void writeFloat(float value) { + byteBuf.writeFloat(value); + } + + @Override + public void writeDouble(double value) { + byteBuf.writeDouble(value); + } + + @Override + public void skipBytes(int count) { + byteBuf.skipBytes(count); + } +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..3c74f54 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'mc-server-proto-125' + +include('proto125') // Protocol 1.2.5 +include('proto125_netty') // Protocol 1.2.5 (Netty impl.) \ No newline at end of file