diff --git a/core/src/main/java/mc/core/block/AbstractBlock.java b/core/src/main/java/mc/core/world/block/AbstractBlock.java similarity index 95% rename from core/src/main/java/mc/core/block/AbstractBlock.java rename to core/src/main/java/mc/core/world/block/AbstractBlock.java index 126e605..1e98d40 100644 --- a/core/src/main/java/mc/core/block/AbstractBlock.java +++ b/core/src/main/java/mc/core/world/block/AbstractBlock.java @@ -1,4 +1,4 @@ -package mc.core.block; +package mc.core.world.block; import com.flowpowered.nbt.Tag; import lombok.Getter; @@ -10,7 +10,8 @@ import java.util.Map; import java.util.stream.Stream; public abstract class AbstractBlock implements Block { - @Getter@Setter + @Getter + @Setter private Location location; @Getter private int meta; diff --git a/core/src/main/java/mc/core/block/Block.java b/core/src/main/java/mc/core/world/block/Block.java similarity index 71% rename from core/src/main/java/mc/core/block/Block.java rename to core/src/main/java/mc/core/world/block/Block.java index fd67fa8..655bf18 100644 --- a/core/src/main/java/mc/core/block/Block.java +++ b/core/src/main/java/mc/core/world/block/Block.java @@ -1,6 +1,5 @@ -package mc.core.block; +package mc.core.world.block; -import com.flowpowered.nbt.Tag; import mc.core.Location; import mc.core.nbt.Taggable; @@ -29,20 +28,6 @@ import java.io.Serializable; public interface Block extends Taggable, Serializable{ - static Block airBlock (int x, int y, int z) { - return new AbstractBlock(BlockType.AIR) { - @Override - public Tag getTag(String name) { - return null; - } - - @Override - public Location getLocation() { - return new Location(x, y, z); - } - }; - } - /** Block id */ int getId(); diff --git a/core/src/main/java/mc/core/block/BlockFactory.java b/core/src/main/java/mc/core/world/block/BlockFactory.java similarity index 78% rename from core/src/main/java/mc/core/block/BlockFactory.java rename to core/src/main/java/mc/core/world/block/BlockFactory.java index 8552e4c..2405524 100644 --- a/core/src/main/java/mc/core/block/BlockFactory.java +++ b/core/src/main/java/mc/core/world/block/BlockFactory.java @@ -1,4 +1,4 @@ -package mc.core.block; +package mc.core.world.block; import mc.core.Location; @@ -8,6 +8,10 @@ public class BlockFactory { return new EmbeddedBlock(blockType, meta); } + public Block create(BlockType blockType) { + return create(blockType, 0); + } + /** * For first-time generation */ diff --git a/core/src/main/java/mc/core/block/BlockType.java b/core/src/main/java/mc/core/world/block/BlockType.java similarity index 100% rename from core/src/main/java/mc/core/block/BlockType.java rename to core/src/main/java/mc/core/world/block/BlockType.java diff --git a/core/src/main/java/mc/core/world/Chunk.java b/core/src/main/java/mc/core/world/chunk/Chunk.java similarity index 67% rename from core/src/main/java/mc/core/world/Chunk.java rename to core/src/main/java/mc/core/world/chunk/Chunk.java index 0b712b0..c99afc3 100644 --- a/core/src/main/java/mc/core/world/Chunk.java +++ b/core/src/main/java/mc/core/world/chunk/Chunk.java @@ -4,7 +4,8 @@ */ package mc.core.world; -import mc.core.block.Block; +import mc.core.Location; +import mc.core.world.block.Block; import java.io.Serializable; @@ -23,14 +24,11 @@ import java.io.Serializable; */ /* 16x16x16 */ public interface Chunk extends Serializable{ - 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); @@ -44,7 +42,4 @@ public interface Chunk extends Serializable{ int getX(); int getY(); int getZ(); - - void setBlock (int x, int y, int z, Block block); - Block getBlock (int x, int y, int z); } diff --git a/core/src/main/java/mc/core/world/ChunkLoader.java b/core/src/main/java/mc/core/world/chunk/ChunkLoader.java similarity index 100% rename from core/src/main/java/mc/core/world/ChunkLoader.java rename to core/src/main/java/mc/core/world/chunk/ChunkLoader.java 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 03ad6ad..7a9cb4a 100644 --- a/flat_world/src/main/java/mc/world/flat/SimpleChunk.java +++ b/flat_world/src/main/java/mc/world/flat/SimpleChunk.java @@ -4,44 +4,33 @@ */ package mc.world.flat; -import mc.core.block.Block; -import mc.core.block.BlockFactory; -import mc.core.block.BlockType; import mc.core.world.Biome; -import mc.core.world.Chunk; +import mc.core.world.block.Block; +import mc.core.world.block.BlockFactory; +import mc.core.world.chunk.Chunk; + +import static mc.core.world.block.BlockType.*; 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; + private static BlockFactory blockFactory = new BlockFactory(); + private final int x, y, z; + + public SimpleChunk(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; } @Override - public void setBlockType(int x, int y, int z, int type) { - + public Block getBlock(int x, int y, int z) { + if (y == 0) return blockFactory.create(BEDROCK); + else if (y >= 1 && y <= 2) return blockFactory.create(DIRT); + else if (y == 3) return blockFactory.create(GRASS); + else return blockFactory.create(AIR); } @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 @@ -77,31 +66,16 @@ public class SimpleChunk implements Chunk { @Override public int getX() { - return 0; + return x; } @Override public int getY() { - return 0; + return y; } @Override public int getZ() { - return 0; - } - - @Override - public void setBlock(int x, int y, int z, Block block) { - - } - - @Override - public Block getBlock(int x, int y, int z) { - BlockFactory blockFactory = new BlockFactory(); - - if (y == 0) return blockFactory.create(BlockType.BEDROCK, 0); - else if (y >= 1 && y <= 2) return blockFactory.create(BlockType.DIRT, 0); - else if (y == 3) return blockFactory.create(BlockType.GRASS, 0); - else return Block.airBlock(x, y, z); + return z; } } 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/NetOutputStream_p340.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetOutputStream_p340.java index ea609cf..cbc3239 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetOutputStream_p340.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetOutputStream_p340.java @@ -14,17 +14,16 @@ import java.util.UUID; public abstract class NetOutputStream_p340 extends NetOutputStream { @Override public void writeVarInt(int value) { - do { - byte temp = (byte)(value & 0b01111111); - // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone + while ((value & -128) != 0) { + writeByte(value & 127 | 128); value >>>= 7; - if (value != 0) { - temp |= 0b10000000; - } - writeByte(temp); - } while (value != 0); + } + + writeByte(value); } + + @Override public void writeString(String value) { if (value.length() > Short.MAX_VALUE) { 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..aecbbc6 --- /dev/null +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.java @@ -0,0 +1,190 @@ +/* + * 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.Location; +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.*; +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 int serializeBlockState(int id, int state) { + return (id << 4) | state; + } + + @Override + public void writeSelf(NetOutputStream netStream) { + netStream.writeInt(x); // Chunk X + netStream.writeInt(z); // Chunk Y + netStream.writeBoolean(initChunk); // Init Chunk + netStream.writeVarInt(0b00000001); // Primary Bit Mask + + final ByteArrayOutputNetStream data = new ByteArrayOutputNetStream(); + int dataItems = 0; + final int airBlockPalette = serializeBlockState(0, 0); + + for (Chunk chunk : chunks) { + final List palette = new ArrayList<>(); + palette.add(airBlockPalette); + final ByteArrayOutputNetStream dataArray = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream blockLight = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream skyLight = new ByteArrayOutputNetStream(); + final ByteArrayOutputNetStream biomes = new ByteArrayOutputNetStream(); + + long dataValueCompacted = 0; + int blockLightCompacted = 0; + int skyLightCompacted = 0; + + int idxHalfLong = 0; + int idxHalfByte = 0; + boolean biomeFinally = false; + + 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); + int blockState = serializeBlockState(block.getId(), block.getState()); + + int currentIndexPaletteBlock; + if (!palette.contains(blockState)) { + palette.add(blockState); + currentIndexPaletteBlock = palette.size()-1; + } else { + currentIndexPaletteBlock = palette.indexOf(blockState); + } + + if (idxHalfLong == 0) { + dataValueCompacted = currentIndexPaletteBlock; + idxHalfLong++; + } else if (idxHalfLong > 0 && idxHalfLong < 15) { + dataValueCompacted = (dataValueCompacted << 4) | currentIndexPaletteBlock; + idxHalfLong++; + } else { + dataValueCompacted = (dataValueCompacted << 4) | currentIndexPaletteBlock; + dataArray.writeLong(dataValueCompacted); + idxHalfLong = 0; + dataItems++; + } + + if (idxHalfByte == 0) { + blockLightCompacted = block.getLight(); + skyLightCompacted = chunk.getSkyLight(x, y, z); + idxHalfByte++; + } else { + blockLightCompacted = (blockLightCompacted << 4) | block.getLight(); + blockLight.writeByte(blockLightCompacted); + skyLightCompacted = (skyLightCompacted << 4) | chunk.getSkyLight(x, y, z); + skyLight.writeByte(skyLightCompacted); + idxHalfByte = 0; + } + + if (!biomeFinally) { + biomes.writeByte(chunk.getBiome(x, z)); + if (x == 15 && z == 15) { + biomeFinally = true; + } + } + } + } + } + + // + // + data.writeUnsignedByte(4); // Bits Per Block + data.writeVarInt(palette.size()); // Size of palette + palette.forEach(data::writeVarInt); // Palette + // + // + data.writeVarInt(dataItems); // 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());