diff --git a/anvil-loader/src/main/java/mc/world/anvil/AnvilBlock.java b/anvil-loader/src/main/java/mc/world/anvil/AnvilBlock.java index a445b0f..730b7a4 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/AnvilBlock.java +++ b/anvil-loader/src/main/java/mc/world/anvil/AnvilBlock.java @@ -1,5 +1,6 @@ package mc.world.anvil; +import com.flowpowered.nbt.CompoundTag; import lombok.extern.slf4j.Slf4j; import mc.core.world.block.Block; import mc.core.world.block.BlockLocation; @@ -49,6 +50,20 @@ public class AnvilBlock implements Block { return globalLocation; } + @Override + public CompoundTag getNBTData() { + CompoundTag compoundTag = chunkSection.getParent().getNbtByGlobalXYZ( + (chunkSection.getX() << 4) + location.getX(), + (chunkSection.getY() << 4) + location.getY(), + (chunkSection.getZ() << 4) + location.getZ() + ); + + compoundTag.getValue().remove("Items"); + compoundTag.getValue().remove("Lock"); + + return compoundTag; + } + @Override public String toString() { return "AnvilBlock{" + diff --git a/anvil-loader/src/main/java/mc/world/anvil/AnvilChunk.java b/anvil-loader/src/main/java/mc/world/anvil/AnvilChunk.java index 877d648..5207948 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/AnvilChunk.java +++ b/anvil-loader/src/main/java/mc/world/anvil/AnvilChunk.java @@ -20,6 +20,7 @@ public class AnvilChunk implements Chunk { private int z; private TByteList biomes = new TByteArrayList(256); private List sections; + private ListTag tileEntities; @SuppressWarnings("unchecked") AnvilChunk(CompoundTag chunkTag) { @@ -29,6 +30,7 @@ public class AnvilChunk implements Chunk { this.z = ((IntTag) levelTagMap.get("zPos")).getValue(); biomes.add(((ByteArrayTag) levelTagMap.get("Biomes")).getValue()); + tileEntities = (ListTag) levelTagMap.get("TileEntities"); List sections = ((ListTag) levelTagMap.get("Sections")).getValue(); this.sections = new ArrayList<>(sections.size()); @@ -49,6 +51,18 @@ public class AnvilChunk implements Chunk { } } + CompoundTag getNbtByGlobalXYZ(int x, int y, int z) { + for (CompoundTag compoundTag : tileEntities.getValue()) { + CompoundMap compoundMap = compoundTag.getValue(); + if (((IntTag)compoundMap.get("x")).getValue() == x + && ((IntTag)compoundMap.get("y")).getValue() == y + && ((IntTag)compoundMap.get("z")).getValue() == z) { + return compoundTag; + } + } + return null; + } + @Override public ChunkSection getChunkSection(int height) { if (height > sections.size()-1) return null; diff --git a/anvil-loader/src/test/java/mc/world/anvil/RegionTest.java b/anvil-loader/src/test/java/mc/world/anvil/RegionTest.java index 6169964..922711f 100644 --- a/anvil-loader/src/test/java/mc/world/anvil/RegionTest.java +++ b/anvil-loader/src/test/java/mc/world/anvil/RegionTest.java @@ -1,5 +1,9 @@ package mc.world.anvil; +import com.flowpowered.nbt.CompoundMap; +import com.flowpowered.nbt.CompoundTag; +import com.flowpowered.nbt.IntTag; +import com.flowpowered.nbt.StringTag; import lombok.SneakyThrows; import mc.core.world.block.Block; import mc.core.world.block.BlockType; @@ -60,6 +64,16 @@ class RegionTest { } } + private CompoundTag createExceptedNBT(Block block) { + CompoundMap compoundMap = new CompoundMap(); + compoundMap.put(new IntTag("x", block.getLocation().getX())); + compoundMap.put(new IntTag("y", block.getLocation().getY())); + compoundMap.put(new IntTag("z", block.getLocation().getZ())); + compoundMap.put(new StringTag("id", block.getBlockType().getNamedId())); + + return new CompoundTag("", compoundMap); + } + private void checkSection2(ChunkSection chunkSection) { for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { @@ -70,6 +84,23 @@ class RegionTest { // @formatter:off if (y == 0) assertEquals(BlockType.DIRT, block.getBlockType(), msg); else if (y == 1) assertEquals(BlockType.GRASS, block.getBlockType(), msg); + else if (y == 2) { + if ((x == 2 || x == 4 || x == 5) && z == 1) { + assertEquals(BlockType.CHEST_NORTH, block.getBlockType(), msg); + assertEquals(createExceptedNBT(block), block.getNBTData()); + } else if ((x == 2 || x == 3 || x == 5) && z == 6) { + assertEquals(BlockType.CHEST_SOUTH, block.getBlockType(), msg); + assertEquals(createExceptedNBT(block), block.getNBTData()); + } else if (x == 1 && (z == 2 || z == 3 || z == 5)) { + assertEquals(BlockType.CHEST_WEST, block.getBlockType(), msg); + assertEquals(createExceptedNBT(block), block.getNBTData()); + } else if (x == 6 && (z == 2 || z == 4 || z == 5)) { + assertEquals(BlockType.CHEST_EAST, block.getBlockType(), msg); + assertEquals(createExceptedNBT(block), block.getNBTData()); + } else { + assertEquals(BlockType.AIR, block.getBlockType(), msg); + } + } else assertEquals(BlockType.AIR, block.getBlockType(), msg); // @formatter:on } diff --git a/anvil-loader/src/test/resources/region/r.0.0.mca b/anvil-loader/src/test/resources/region/r.0.0.mca index 214dbf0..7052a51 100644 Binary files a/anvil-loader/src/test/resources/region/r.0.0.mca and b/anvil-loader/src/test/resources/region/r.0.0.mca differ diff --git a/core/src/main/java/mc/core/network/NetInputStream.java b/core/src/main/java/mc/core/network/NetInputStream.java index 2c40cb6..79a5cfc 100644 --- a/core/src/main/java/mc/core/network/NetInputStream.java +++ b/core/src/main/java/mc/core/network/NetInputStream.java @@ -1,5 +1,6 @@ package mc.core.network; +import com.flowpowered.nbt.Tag; import lombok.Getter; import lombok.Setter; @@ -29,6 +30,7 @@ public abstract class NetInputStream extends InputStream { public abstract double readDouble(); public abstract String readString(); public abstract UUID readUUID(); + public abstract Tag readNBT(); public abstract void skipBytes(int count); diff --git a/core/src/main/java/mc/core/network/NetOutputStream.java b/core/src/main/java/mc/core/network/NetOutputStream.java index b7bf437..2f29ca5 100644 --- a/core/src/main/java/mc/core/network/NetOutputStream.java +++ b/core/src/main/java/mc/core/network/NetOutputStream.java @@ -1,5 +1,7 @@ package mc.core.network; +import com.flowpowered.nbt.Tag; + import java.io.IOException; import java.io.OutputStream; import java.util.UUID; @@ -20,6 +22,7 @@ public abstract class NetOutputStream extends OutputStream { public abstract void writeDouble(double value); public abstract void writeString(String value); public abstract void writeUUID(UUID uuid); + public abstract void writeNBT(Tag tag); @Override public void write(int b) throws IOException { diff --git a/core/src/main/java/mc/core/world/block/Block.java b/core/src/main/java/mc/core/world/block/Block.java index a23d3df..167f15a 100644 --- a/core/src/main/java/mc/core/world/block/Block.java +++ b/core/src/main/java/mc/core/world/block/Block.java @@ -1,8 +1,17 @@ package mc.core.world.block; +import com.flowpowered.nbt.CompoundTag; + public interface Block { int getLight(); void setLight(int light); BlockType getBlockType(); BlockLocation getLocation(); + + default CompoundTag getNBTData() { + return null; + } + + default void setNBTData(CompoundTag nbtData) { + } } diff --git a/core/src/main/java/mc/core/world/block/BlockType.java b/core/src/main/java/mc/core/world/block/BlockType.java index e410777..20a2f40 100644 --- a/core/src/main/java/mc/core/world/block/BlockType.java +++ b/core/src/main/java/mc/core/world/block/BlockType.java @@ -96,11 +96,10 @@ public enum BlockType { MOSS_STONE(48, 0), OBSIDIAN(49, 0), MONSTER_SPAWNER(52, 0), - //BAG CHUNK -// CHEST_NORTH(54, 2), -// CHEST_SOUTH(54, 3), -// CHEST_WEST (54, 4), -// CHEST_EAST (54, 5), + CHEST_NORTH(54, 2, "minecraft:chest"), + CHEST_SOUTH(54, 3, "minecraft:chest"), + CHEST_WEST (54, 4, "minecraft:chest"), + CHEST_EAST (54, 5, "minecraft:chest"), ORE_DIAMOND(56, 0), ORE_REDSTONE(73, 0), ORE_GLOWING_REDSTONE(74, 0), @@ -118,6 +117,12 @@ public enum BlockType { PEONY(175, 5), ROSE_BUSH_10(175, 10); + BlockType(int id, int meta) { + this.id = id; + this.meta = meta; + this.namedId = null; + } + public static BlockType getByIdMeta(int id, int meta) { if (id < 0) { log.warn("Incorrect id \"{}\"", id); @@ -137,4 +142,6 @@ public enum BlockType { private final int id; @Getter private final int meta; + @Getter + private final String namedId; } diff --git a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetInputStream_p340.java b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetInputStream_p340.java index 93f8530..53472f7 100644 --- a/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetInputStream_p340.java +++ b/proto_1.12.2/src/main/java/mc/core/network/proto_1_12_2/NetInputStream_p340.java @@ -1,13 +1,18 @@ package mc.core.network.proto_1_12_2; +import com.flowpowered.nbt.Tag; +import com.flowpowered.nbt.stream.NBTInputStream; import lombok.extern.slf4j.Slf4j; import mc.core.network.NetInputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.UUID; @Slf4j public abstract class NetInputStream_p340 extends NetInputStream { + private NBTInputStream nbtInputStream; + @Override public int readVarInt(int[] countReadBytes) { int numRead = 0; @@ -51,4 +56,23 @@ public abstract class NetInputStream_p340 extends NetInputStream { public UUID readUUID() { return new UUID(readLong(), readLong()); } + + @Override + public Tag readNBT() { + if (nbtInputStream == null) { + try { + nbtInputStream = new NBTInputStream(this, false); + } catch (IOException e) { + log.error("Create NBT stream", e); + return null; + } + } + + try { + return nbtInputStream.readTag(); + } catch (IOException e) { + log.error("Read NBT", e); + return null; + } + } } 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 b4bcdbd..396ec74 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 @@ -1,13 +1,18 @@ package mc.core.network.proto_1_12_2; +import com.flowpowered.nbt.Tag; +import com.flowpowered.nbt.stream.NBTOutputStream; import lombok.extern.slf4j.Slf4j; import mc.core.network.NetOutputStream; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.UUID; @Slf4j public abstract class NetOutputStream_p340 extends NetOutputStream { + private NBTOutputStream nbtOutputStream; + @Override public void writeVarInt(int value) { while ((value & -128) != 0) { @@ -37,4 +42,22 @@ public abstract class NetOutputStream_p340 extends NetOutputStream { writeLong(uuid.getMostSignificantBits()); writeLong(uuid.getLeastSignificantBits()); } + + @Override + public void writeNBT(Tag tag) { + if (nbtOutputStream == null) { + try { + nbtOutputStream = new NBTOutputStream(this, false); + } catch (IOException e) { + log.error("Create NBT stream", e); + return; + } + } + + try { + nbtOutputStream.writeTag(tag); + } catch (IOException e) { + log.error("Write NBT", e); + } + } } 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 index adc1789..37cb6ad 100644 --- 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 @@ -1,5 +1,6 @@ package mc.core.network.proto_1_12_2.packets; +import com.flowpowered.nbt.CompoundTag; import gnu.trove.list.TIntList; import gnu.trove.list.array.TIntArrayList; import lombok.NoArgsConstructor; @@ -15,6 +16,7 @@ import mc.core.world.block.BlockType; import mc.core.world.chunk.Chunk; import mc.core.world.chunk.ChunkSection; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -133,6 +135,8 @@ public class ChunkDataPacket implements SCPacket { final ByteArrayOutputNetStream biomes = new ByteArrayOutputNetStream(); boolean biomeWrite = true; + List nbtList = new ArrayList<>(); + for (int h = 0; h < maxH; h++) { ChunkSection chunkSection = null; @@ -151,11 +155,18 @@ public class ChunkDataPacket implements SCPacket { for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { + Block block = chunkSection.getBlockLocal(x, y, z); + palettedChunkSection.addBlock( - chunkSection.getBlockLocal(x, y, z), + block, chunkSection.getSkyLightLocal(x, y, z) ); + CompoundTag nbt = block.getNBTData(); + if (nbt != null) { + nbtList.add(nbt); + } + if (biomeWrite) { biomes.writeByte(chunk.getBiomeLocal(x, z).getId()); if (x == 15 && z == 15) { @@ -176,8 +187,12 @@ public class ChunkDataPacket implements SCPacket { netStream.writeVarInt(data.size()); // Size of Data netStream.writeBytes(data.toByteArray()); // Data - netStream.writeVarInt(0); // Number of block entities - /* writeNBT */ + netStream.writeVarInt(nbtList.size()); // Number of block entities + // + for (CompoundTag compoundTag : nbtList) { + netStream.writeNBT(compoundTag); + } + // } @Override diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacketTest.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacketTest.java index a984e3d..47e00d8 100644 --- a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacketTest.java +++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ChunkDataPacketTest.java @@ -1,10 +1,10 @@ package mc.core.network.proto_1_12_2.packets; +import com.flowpowered.nbt.*; import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream; import mc.core.network.proto_1_12_2.packets.DumbChunkData.DumbChunkSection; import mc.core.world.Biome; -import mc.core.world.block.BlockFactory; -import mc.core.world.block.BlockType; +import mc.core.world.block.*; import mc.core.world.chunk.Chunk; import mc.core.world.chunk.ChunkSection; import org.apache.commons.io.IOUtils; @@ -72,6 +72,29 @@ class ChunkDataPacketTest { setupActualData(); } + private static Block createChestBlock(BlockType type, int x, int y, int z, int height) { + final BlockLocation location = new BlockLocation(x, y, z); + + final CompoundMap compoundMap = new CompoundMap(); + compoundMap.put(new IntTag("x", x)); + compoundMap.put(new IntTag("y", (height << 4) + y)); + compoundMap.put(new IntTag("z", z)); + compoundMap.put(new StringTag("id", type.getNamedId())); + final CompoundTag compoundTag = new CompoundTag("", compoundMap); + + return new AbstractBlock(type) { + @Override + public BlockLocation getLocation() { + return location; + } + + @Override + public CompoundTag getNBTData() { + return compoundTag; + } + }; + } + private static ChunkSection createChunkSection(int height) { final ChunkSection chunkSection = mock(ChunkSection.class); when(chunkSection.getSkyLightLocal(anyInt(), anyInt(), anyInt())).thenReturn(0); @@ -107,13 +130,23 @@ class ChunkDataPacketTest { BlockFactory blockFactory = new BlockFactory(); - if (y == 0) { - return blockFactory.create(BlockType.DIRT, x, y, z); - } else if (y == 1) { - return blockFactory.create(BlockType.GRASS, x, y, z); - } else { - return blockFactory.create(BlockType.AIR, x, y, z); + // @formatter:off + if (y == 0) return blockFactory.create(BlockType.DIRT, x, y, z); + else if (y == 1) return blockFactory.create(BlockType.GRASS, x, y, z); + else if (y == 2) { + if ((x == 2 || x == 4 || x == 5) && z == 1) + return createChestBlock(BlockType.CHEST_NORTH, x, y, z, height); + else if ((x == 2 || x == 3 || x == 5) && z == 6) + return createChestBlock(BlockType.CHEST_SOUTH, x, y, z, height); + else if (x == 1 && (z == 2 || z == 3 || z == 5)) + return createChestBlock(BlockType.CHEST_WEST, x, y, z, height); + else if (x == 6 && (z == 2 || z == 4 || z == 5)) + return createChestBlock(BlockType.CHEST_EAST, x, y, z, height); + else + return blockFactory.create(BlockType.AIR, x, y, z); } + else return blockFactory.create(BlockType.AIR, x, y, z); + // @formatter:on }); } @@ -132,6 +165,11 @@ class ChunkDataPacketTest { @Test void testNBT() { assertEquals(expectedDumbChunkData.getNumberNBT(), actualDumbChunkData.getNumberNBT()); + assertEquals(expectedDumbChunkData.getNbt().size(), actualDumbChunkData.getNbt().size()); + + for (Tag tag : actualDumbChunkData.getNbt()) { + assertTrue(expectedDumbChunkData.getNbt().contains(tag)); + } } @Test diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/DumbChunkData.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/DumbChunkData.java index 129c3d5..0662b7d 100644 --- a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/DumbChunkData.java +++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/DumbChunkData.java @@ -1,5 +1,6 @@ package mc.core.network.proto_1_12_2.packets; +import com.flowpowered.nbt.Tag; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -8,6 +9,7 @@ import mc.core.world.block.BlockType; import java.nio.ByteBuffer; import java.nio.LongBuffer; import java.util.ArrayList; +import java.util.Collections; import java.util.List; @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -23,9 +25,10 @@ class DumbChunkData { private byte[] biomes; private int numberNBT; + private List> nbt; private static BlockType deserializeBlockState(int blockState) { - return BlockType.getByIdMeta((blockState & 0xF0) >> 4, blockState & 0x0F); + return BlockType.getByIdMeta(blockState >> 4, blockState & 0x0F); } static DumbChunkData ReadFromNetInputStream(byte[] bytes) { @@ -95,6 +98,14 @@ class DumbChunkData { netStream.readBytes(dumbChunkData.biomes); dumbChunkData.numberNBT = netStream.readVarInt(); + if (dumbChunkData.numberNBT > 0) { + dumbChunkData.nbt = new ArrayList<>(dumbChunkData.numberNBT); + for (int i = 0; i < dumbChunkData.numberNBT; i++) { + dumbChunkData.nbt.add(netStream.readNBT()); + } + } else { + dumbChunkData.nbt = Collections.emptyList(); + } return dumbChunkData; } diff --git a/proto_1.12.2/src/test/resources/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.bin b/proto_1.12.2/src/test/resources/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.bin index 23e919c..6125544 100644 Binary files a/proto_1.12.2/src/test/resources/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.bin and b/proto_1.12.2/src/test/resources/mc/core/network/proto_1_12_2/packets/ChunkDataPacket.bin differ