diff --git a/core/src/main/java/mc/core/world/Block.java b/core/src/main/java/mc/core/world/Block.java new file mode 100644 index 0000000..ba2865a --- /dev/null +++ b/core/src/main/java/mc/core/world/Block.java @@ -0,0 +1,15 @@ +/* + * DmitriyMX + * 2018-08-02 + */ +package mc.core.world; + +import mc.core.Location; + +public interface Block { + int getId(); + int getState(); + int getMetadata(); + int getLight(); + Location getLocation(); +} diff --git a/core/src/main/java/mc/core/world/Chunk.java b/core/src/main/java/mc/core/world/Chunk.java index 15ee0fa..db8f289 100644 --- a/core/src/main/java/mc/core/world/Chunk.java +++ b/core/src/main/java/mc/core/world/Chunk.java @@ -4,16 +4,15 @@ */ package mc.core.world; +import mc.core.Location; + /* 16x16x16 */ public interface Chunk { - int getBlockType(int x, int y, int z); - void setBlockType(int x, int y, int z, int type); - - int getBlockMetadata(int x, int y, int z); - void setBlockMetadata(int x, int y, int z, int metadata); - - int getBlockLight(int x, int y, int z); - void setBlockLight(int x, int y, int z, int lightLevel); + Block getBlock(int x, int y, int z); + default Block getBlock(Location location) { + return getBlock(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + void setBlock(Block block); int getSkyLight(int x, int y, int z); void setSkyLight(int x, int y, int z, int lightLevel); diff --git a/flat_world/src/main/java/mc/world/flat/SimpleBlock.java b/flat_world/src/main/java/mc/world/flat/SimpleBlock.java new file mode 100644 index 0000000..5691732 --- /dev/null +++ b/flat_world/src/main/java/mc/world/flat/SimpleBlock.java @@ -0,0 +1,20 @@ +/* + * DmitriyMX + * 2018-08-02 + */ +package mc.world.flat; + +import lombok.Getter; +import lombok.Setter; +import mc.core.Location; +import mc.core.world.Block; + +@Getter +@Setter +public class SimpleBlock implements Block { + private int id; + private int state; + private int metadata; + private int light; + private Location location; +} diff --git a/flat_world/src/main/java/mc/world/flat/SimpleChunk.java b/flat_world/src/main/java/mc/world/flat/SimpleChunk.java index 5186615..a44c23a 100644 --- a/flat_world/src/main/java/mc/world/flat/SimpleChunk.java +++ b/flat_world/src/main/java/mc/world/flat/SimpleChunk.java @@ -4,40 +4,26 @@ */ package mc.world.flat; +import mc.core.world.Block; import mc.core.world.Chunk; public class SimpleChunk implements Chunk { @Override - public int getBlockType(int x, int y, int z) { - if (y == 0) return 7; - else if (y >= 1 && y <= 2) return 3; - else if (y == 3) return 2; - else return 0; + public Block getBlock(int x, int y, int z) { + SimpleBlock block = new SimpleBlock(); + block.setMetadata(0); + block.setLight(0); + + if (y == 0) block.setId(7); + else if (y >= 1 && y <= 2) block.setId(3); + else if (y == 3) block.setId(2); + else block.setId(0); + + return block; } @Override - public void setBlockType(int x, int y, int z, int type) { - - } - - @Override - public int getBlockMetadata(int x, int y, int z) { - return 0; - } - - @Override - public void setBlockMetadata(int x, int y, int z, int metadata) { - - } - - @Override - public int getBlockLight(int x, int y, int z) { - return 0; - } - - @Override - public void setBlockLight(int x, int y, int z, int lightLevel) { - + public void setBlock(Block block) { } @Override diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/ByteArrayOutputNetStream.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/ByteArrayOutputNetStream.java index 15810b7..d496e05 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/ByteArrayOutputNetStream.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/ByteArrayOutputNetStream.java @@ -65,6 +65,10 @@ public class ByteArrayOutputNetStream extends NetOutputStream_p340 { writeLong(Double.doubleToLongBits(value)); } + public int size() { + return baos.size(); + } + public byte[] toByteArray() { return baos.toByteArray(); } diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/State.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/State.java index 237aa4f..feb4f26 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/State.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/State.java @@ -87,6 +87,7 @@ public enum State { .put(PluginMessagePacket.class, 0x18) .put(ChangeGameState.class, 0x1E) .put(KeepAlivePacket.class, 0x1F) + .put(ChunkDataPacket.class, 0x20) .put(JoinGamePacket.class, 0x23) .put(PlayerAbilitiesPacket.class, 0x2C) .put(PlayerListItemPacket.class, 0x2E) diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java new file mode 100644 index 0000000..af8789f --- /dev/null +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java @@ -0,0 +1,165 @@ +/* + * DmitriyMX + * 2018-07-21 + */ +package mc.core.network.proto_1_12_2.packets; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import mc.core.network.NetOutputStream; +import mc.core.network.SCPacket; +import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream; +import mc.core.world.Block; +import mc.core.world.Chunk; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/* +Packet structure + +- https://wiki.vg/Chunk_Format#Packet_structure + ++------------------------------------------------------+ +| Field | Type | +|--------------------------|---------------------------| +| Chunk X | int | +|--------------------------|---------------------------| +| Chunk Y | int | +|--------------------------|---------------------------| +| Init Chunk | boolean | ("Ground-Up Continuous") +|--------------------------|---------------------------| +| Primary Bit Mask | VarInt | +|--------------------------|---------------------------| +| Size of Data | VarInt | +|--------------------------|---------------------------| +| Data | Byte array | - https://wiki.vg/Chunk_Format#Data_structure +| +------------------------------------------------+ | +| | Chunk Section | Byte array | | - https://wiki.vg/Chunk_Format#Chunk_Section_structure +| | +------------------------------------------+ | | +| | | Bits Per Block | Unsigned Byte | | | (we use 4 bits per block) +| | |--------------------|---------------------| | | +| | | Palette | Byte array | | | - https://wiki.vg/Chunk_Format#Palettes +| | | +------------------------------------+ | | | (we use Indirect type palette) +| | | | Size of palette | VarInt | | | | +| | | |-----------------|------------------| | | | +| | | | Palette | Array of VarInt | | | | +| | | +------------------------------------+ | | | +| | |--------------------|---------------------| | | +| | | Size of Data Array | VarInt | | | +| | |--------------------|---------------------| | | +| | | Data Array | Array of Long | | | +| | |--------------------|---------------------| | | +| | | Block Light | Byte Array | | | (Half byte per block) +| | |--------------------|---------------------| | | +| | | Sky Light | Optional Byte Array | | | (Only if in the Overworld; half byte per block) +| | +------------------------------------------+ | | +| |-----------------------|------------------------| | +| | Biomes | Optional Byte array | | +| +------------------------------------------------+ | +|--------------------------|---------------------------| +| Number of block entities | VarInt | +|--------------------------|---------------------------| +| Block entities | Array of NBT | ++------------------------------------------------------+ + */ + +@Slf4j +@NoArgsConstructor +public class ChunkDataPacket implements SCPacket { + @Setter + private int x; + @Setter + private int z; + @Setter + private boolean initChunk = true; // "Ground-Up Continuous" + @Getter + private List chunks = new ArrayList<>(); + + private long serializeBlockState(Block block) { + return (block.getId() << 4) | block.getState(); + } + + @Override + public void writeSelf(NetOutputStream netStream) { + netStream.writeInt(x); // Chunk X + netStream.writeInt(z); // Chunk Y + netStream.writeBoolean(initChunk); // Init Chunk + netStream.writeVarInt(0b11111111); // Primary Bit Mask + + final ByteArrayOutputNetStream data = new ByteArrayOutputNetStream(); + + for (Chunk chunk : chunks) { + final List palette = new ArrayList<>(); + final ByteArrayOutputNetStream dataArray = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream blockLight = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream skyLight = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream biomes = new ByteArrayOutputNetStream(); + + int blockLightCompacted = 0; + boolean flagFirstHalf = true; + + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + Block block = chunk.getBlock(x, y, z); + long blockState = serializeBlockState(block); + + int currentIndexPaletteBlock; + if (!palette.contains(blockState)) { + palette.add(blockState); + currentIndexPaletteBlock = palette.size()-1; + } else { + currentIndexPaletteBlock = palette.indexOf(blockState); + } + + dataArray.writeLong(currentIndexPaletteBlock); + if (flagFirstHalf) { + blockLightCompacted = block.getLight(); + flagFirstHalf = false; + } else { + blockLightCompacted = (blockLightCompacted << 4) | block.getLight(); + blockLight.writeByte(blockLightCompacted); + flagFirstHalf = true; + skyLight.writeByte(0b11111111); //FIXME + } + + biomes.writeByte(chunk.getBiome(x, z)); + } + } + } + + // + // + data.writeUnsignedByte(4); // Bits Per Block + data.writeVarInt(palette.size()); // Size of palette + palette.stream() + .mapToInt(Long::intValue) + .forEach(data::writeVarInt); // Palette + // + // + data.writeVarInt(dataArray.size()); // Size of Data Array + data.writeBytes(dataArray.toByteArray()); // Data Array + // + // + data.writeBytes(blockLight.toByteArray()); + // + // + data.writeBytes(skyLight.toByteArray()); + // + // + // + data.writeBytes(biomes.toByteArray()); + // + } + + netStream.writeVarInt(data.size()); // Size of Data + netStream.writeBytes(data.toByteArray()); // Data + netStream.writeVarInt(0); // Number of block entities + /* writeNBT */ + } +} diff --git a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java index eb5cd65..a0c9591 100644 --- a/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java +++ b/proto_1.12.2_netty/src/main/java/mc/core/network/proto_1_12_2/netty/handlers/LoginHandler.java @@ -80,6 +80,22 @@ public class LoginHandler extends AbstractStateHandler implements LoginStateHand channel.write(pkt3); channel.flush(); + // Send chunk data + ChunkDataPacket pkt8 = new ChunkDataPacket(); + pkt8.setX(0); + pkt8.setZ(0); + pkt8.getChunks().add(world.getChunk(0,0)); + pkt8.setInitChunk(true); + channel.writeAndFlush(pkt8); + + // One Chunk + ChunkDataPacket pkt9 = new ChunkDataPacket(); + pkt9.setInitChunk(true); + pkt9.setX(0); + pkt9.setZ(0); + pkt9.getChunks().add(world.getChunk(0, 0)); + channel.writeAndFlush(pkt9); + // Player Position And Look PlayerPositionAndLookPacket pkt4 = new PlayerPositionAndLookPacket(); pkt4.setLocation(player.getLocation());