From 6f490ff946a982624b1c3d4c9c463e387926dc20 Mon Sep 17 00:00:00 2001 From: DmitriyMX Date: Sun, 23 Dec 2018 00:35:11 +0300 Subject: [PATCH] =?UTF-8?q?=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=20ChunkDataPacket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mc/world/anvil/AnvilChunkSection.java | 6 - build.gradle | 4 +- .../mc/core/world/chunk/ChunkSection.java | 18 --- .../proto_1_12_2/packets/ChunkDataPacket.java | 5 +- .../packets/ByteArrayInputNetStream.java | 17 +- .../packets/ChunkDataPacketTest.java | 146 +++++++++++++----- .../proto_1_12_2/packets/DumbChunkData.java | 112 ++++++++++++++ .../mc/world/simple/SimpleChunkSection.java | 6 - .../world/simple/SimpleChunkSectionTest.java | 2 + 9 files changed, 234 insertions(+), 82 deletions(-) create mode 100644 proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/DumbChunkData.java diff --git a/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkSection.java b/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkSection.java index 0278f8c..f164945 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkSection.java +++ b/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkSection.java @@ -5,7 +5,6 @@ import gnu.trove.list.linked.TByteLinkedList; import lombok.Getter; import lombok.Setter; import mc.core.utils.NibbleArray; -import mc.core.world.Biome; import mc.core.world.block.Block; import mc.core.world.chunk.ChunkSection; @@ -64,9 +63,4 @@ public class AnvilChunkSection implements ChunkSection { public void setAddition(int x, int y, int z, int value) { } - - @Override - public Biome getBiomeLocal(int x, int z) { - return parent.getBiome(x, z); - } } diff --git a/build.gradle b/build.gradle index 1d30ef7..094e972 100644 --- a/build.gradle +++ b/build.gradle @@ -33,9 +33,7 @@ allprojects { repositories { mavenCentral() - maven { - url 'https://oss.sonatype.org/content/groups/public/' - } + maven { url 'https://oss.sonatype.org/content/groups/public/' } } } diff --git a/core/src/main/java/mc/core/world/chunk/ChunkSection.java b/core/src/main/java/mc/core/world/chunk/ChunkSection.java index de91971..09d9544 100644 --- a/core/src/main/java/mc/core/world/chunk/ChunkSection.java +++ b/core/src/main/java/mc/core/world/chunk/ChunkSection.java @@ -91,22 +91,4 @@ public interface ChunkSection { int getAddition(int x, int y, int z); void setAddition(int x, int y, int z, int value); - - /** - * Получиь данные по биому - * @param x глобальный X - * @param z глобальный Z - * @return - */ - default Biome getBiome(int x, int z) { - return getBiomeLocal(x >> 4, z >> 4); - } - - /** - * Получиь данные по биому - * @param x локальный X (0-15) - * @param z локальный Z (0-15) - * @return - */ - Biome getBiomeLocal(int x, int z); } 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 cb19e6d..adc1789 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 @@ -147,7 +147,6 @@ public class ChunkDataPacket implements SCPacket { } final PalettedChunkSection palettedChunkSection = new PalettedChunkSection(); - palettedChunkSection.addBlockType(BlockType.AIR); for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { @@ -158,7 +157,7 @@ public class ChunkDataPacket implements SCPacket { ); if (biomeWrite) { - biomes.writeByte(chunkSection.getBiomeLocal(x, z).getId()); + biomes.writeByte(chunk.getBiomeLocal(x, z).getId()); if (x == 15 && z == 15) { biomeWrite = false; } @@ -246,7 +245,7 @@ public class ChunkDataPacket implements SCPacket { palette.forEach(value -> { netOutputStream.writeVarInt(value); return true; }); // Palette // // - final int dataLength = (4096/*16*16*16*/ * bitsPerBlock) / 64; + final int dataLength = (4096/*16*16*16*/ * bitsPerBlock) / 64/*size of long in bits*/; netOutputStream.writeVarInt(dataLength); // Size of Data Array // long value = 0; diff --git a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java index c51beaf..83ab4e1 100644 --- a/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java +++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/ByteArrayInputNetStream.java @@ -3,17 +3,18 @@ package mc.core.network.proto_1_12_2.packets; import mc.core.network.proto_1_12_2.NetInputStream_p340; import java.io.ByteArrayInputStream; +import java.io.IOException; public class ByteArrayInputNetStream extends NetInputStream_p340 { private ByteArrayInputStream bais; - public ByteArrayInputNetStream(byte[] buff) { + ByteArrayInputNetStream(byte[] buff) { bais = new ByteArrayInputStream(buff); } @Override public boolean readBoolean() { - return false; + return readByte() != 0; } @Override @@ -23,11 +24,19 @@ public class ByteArrayInputNetStream extends NetInputStream_p340 { @Override public void readBytes(byte[] buffer) { + try { + int read = bais.read(buffer); + if (read < buffer.length) { + throw new IOException("not enough data"); + } + } catch (IOException e) { + e.printStackTrace(); + } } @Override public int readUnsignedByte() { - return 0; + return bais.read() & 0xFF; } @Override @@ -47,7 +56,7 @@ public class ByteArrayInputNetStream extends NetInputStream_p340 { int ch3 = bais.read(); int ch4 = bais.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) return 0; - return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); + return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4)); } @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 8d45513..a5d3e32 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 @@ -2,42 +2,77 @@ package mc.core.network.proto_1_12_2.packets; import mc.core.network.proto_1_12_2.ByteArrayOutputNetStream; import mc.core.world.Biome; -import mc.core.world.World; -import mc.core.world.WorldType; import mc.core.world.block.BlockFactory; import mc.core.world.block.BlockType; import mc.core.world.chunk.Chunk; import mc.core.world.chunk.ChunkSection; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; class ChunkDataPacketTest { - private static byte[] expectedPacketData; - private World world; + private static DumbChunkData expectedDumbChunkData; + private static DumbChunkData actualDumbChunkData; + + private static void setupExpectedData() throws IOException { + InputStream inputStream = ChunkDataPacketTest.class.getResourceAsStream("ChunkDataPacket.bin"); + assertNotNull(inputStream); + expectedDumbChunkData = DumbChunkData.ReadFromNetInputStream(IOUtils.toByteArray(inputStream)); + } + + private static Chunk createMockChunk() { + final ChunkSection chunkSection0 = createChunkSection(0); + final ChunkSection chunkSection1 = createChunkSection(1); + + final Chunk chunk = mock(Chunk.class); + when(chunk.getX()).thenReturn(0); + when(chunk.getZ()).thenReturn(0); + when(chunk.getBiomeLocal(anyInt(), anyInt())).thenReturn(Biome.PLAINS); + when(chunk.getChunkSection(0)).thenReturn(chunkSection0); + when(chunk.getChunkSection(1)).thenReturn(chunkSection1); + + return chunk; + } + + private static void verifyMock(Chunk chunk) { + verify(chunk).getX(); + verify(chunk).getZ(); + verify(chunk, times(256)).getBiomeLocal(anyInt(), anyInt()); + verify(chunk, atLeast(2)).getChunkSection(anyInt()); + } + + private static void setupActualData() { + Chunk chunk = createMockChunk(); + + ChunkDataPacket packet = new ChunkDataPacket(); + packet.setX(chunk.getX()); + packet.setZ(chunk.getZ()); + packet.setChunk(chunk); + packet.setInitChunk(true); + + ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); + packet.writeSelf(netStream); + + verifyMock(chunk); + + actualDumbChunkData = DumbChunkData.ReadFromNetInputStream(netStream.toByteArray()); + } @BeforeAll static void beforeClassTest() throws IOException { - InputStream inputStream = ChunkDataPacketTest.class.getResourceAsStream("ChunkDataPacket.bin"); - assertNotNull(inputStream); - expectedPacketData = IOUtils.toByteArray(inputStream); - assertEquals(12571, expectedPacketData.length); + setupExpectedData(); + setupActualData(); } - private ChunkSection createChunkSection(int height) { + private static ChunkSection createChunkSection(int height) { final ChunkSection chunkSection = mock(ChunkSection.class); - when(chunkSection.getBiomeLocal(anyInt(), anyInt())).thenReturn(Biome.PLAINS); when(chunkSection.getSkyLightLocal(anyInt(), anyInt(), anyInt())).thenReturn(0); when(chunkSection.getY()).thenReturn(height); @@ -70,36 +105,63 @@ class ChunkDataPacketTest { return chunkSection; } - @BeforeEach - void prepareWorld() { - final ChunkSection chunkSection0 = createChunkSection(0); - final ChunkSection chunkSection1 = createChunkSection(1); - - final Chunk chunk = mock(Chunk.class); - when(chunk.getX()).thenReturn(0); - when(chunk.getZ()).thenReturn(0); - when(chunk.getBiomeLocal(anyInt(), anyInt())).thenReturn(Biome.PLAINS); - when(chunk.getChunkSection(0)).thenReturn(chunkSection0); - when(chunk.getChunkSection(1)).thenReturn(chunkSection1); - - world = mock(World.class); - when(world.getWorldType()).thenReturn(WorldType.FLAT); - when(world.getChunk(0, 0)).thenReturn(chunk); + @Test + void testGeneral() { + assertEquals(expectedDumbChunkData.getX(), actualDumbChunkData.getX()); + assertEquals(expectedDumbChunkData.getZ(), actualDumbChunkData.getZ()); + assertEquals(expectedDumbChunkData.isInitChunk(), actualDumbChunkData.isInitChunk()); + assertEquals(expectedDumbChunkData.getBitMask(), actualDumbChunkData.getBitMask()); + assertArrayEquals(expectedDumbChunkData.getBiomes(), actualDumbChunkData.getBiomes()); } @Test - void writePacket() { - ChunkDataPacket packet = new ChunkDataPacket(); - packet.setX(0); - packet.setZ(0); - packet.setChunk(world.getChunk(0, 0)); - packet.setInitChunk(true); + void testNBT() { + assertEquals(expectedDumbChunkData.getNumberNBT(), actualDumbChunkData.getNumberNBT()); + } - ByteArrayOutputNetStream netStream = new ByteArrayOutputNetStream(); - packet.writeSelf(netStream); - byte[] actualPacketData = netStream.toByteArray(); + @Test + void testData() { + assertEquals(expectedDumbChunkData.getData().length, actualDumbChunkData.getData().length); - assertEquals(expectedPacketData.length, actualPacketData.length); - assertArrayEquals(expectedPacketData, actualPacketData); + for (int numberSection = 0; numberSection < expectedDumbChunkData.getData().length; numberSection++) { + final DumbChunkData.DumbChunkSection expectedDumbChunkSection = expectedDumbChunkData.getData()[numberSection]; + final DumbChunkData.DumbChunkSection actualDumbChunkSection = actualDumbChunkData.getData()[numberSection]; + + // Palette + assertEquals(expectedDumbChunkSection.getBitsPerBlock(), actualDumbChunkSection.getBitsPerBlock()); + + if (expectedDumbChunkSection.getPalette().size() > actualDumbChunkSection.getPalette().size()) { + for (int j = 0; j < actualDumbChunkSection.getPalette().size(); j++) { + assertTrue(expectedDumbChunkSection.getPalette().contains( + actualDumbChunkSection.getPalette().get(j) + ), String.format("[%d] Palette not contains %s", numberSection, actualDumbChunkSection.getPalette().get(j))); + } + } else { + for (int j = 0; j < expectedDumbChunkSection.getPalette().size(); j++) { + assertTrue(actualDumbChunkSection.getPalette().contains( + expectedDumbChunkSection.getPalette().get(j) + ), String.format("[%d] Palette not contains %s", numberSection, actualDumbChunkSection.getPalette().get(j))); + } + } + + // Data + assertEquals(expectedDumbChunkSection.getData().size(), actualDumbChunkSection.getData().size()); + + for (int j = 0; j < expectedDumbChunkSection.getData().size(); j++) { + assertEquals( + expectedDumbChunkSection.getData().get(j), + actualDumbChunkSection.getData().get(j), + String.format("[%d] Data (blocks)", numberSection) + ); + } + + // Block light + assertArrayEquals(expectedDumbChunkSection.getBlockLight(), actualDumbChunkSection.getBlockLight(), + String.format("[%d] Block light", numberSection)); + + // Sky light + assertArrayEquals(expectedDumbChunkSection.getSkyLight(), actualDumbChunkSection.getSkyLight(), + String.format("[%d] Sky light", numberSection)); + } } } 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 new file mode 100644 index 0000000..129c3d5 --- /dev/null +++ b/proto_1.12.2/src/test/java/mc/core/network/proto_1_12_2/packets/DumbChunkData.java @@ -0,0 +1,112 @@ +package mc.core.network.proto_1_12_2.packets; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import mc.core.world.block.BlockType; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.List; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +class DumbChunkData { + private int x; + private int z; + private boolean initChunk; + private int bitMask; + + private int sizeOfData; + private DumbChunkSection[] data; + private byte[] biomes; + + private int numberNBT; + + private static BlockType deserializeBlockState(int blockState) { + return BlockType.getByIdMeta((blockState & 0xF0) >> 4, blockState & 0x0F); + } + + static DumbChunkData ReadFromNetInputStream(byte[] bytes) { + ByteArrayInputNetStream netStream = new ByteArrayInputNetStream(bytes); + + DumbChunkData dumbChunkData = new DumbChunkData(); + + dumbChunkData.x = netStream.readInt(); + dumbChunkData.z = netStream.readInt(); + dumbChunkData.initChunk = netStream.readBoolean(); + + dumbChunkData.bitMask = netStream.readVarInt(); + int countOfSections = 0; + for (int shift = 0; shift < 8; shift++) { + countOfSections += ((dumbChunkData.bitMask >> shift) & 0x01) > 0 ? 1 : 0; + } + + dumbChunkData.sizeOfData = netStream.readVarInt(); + + dumbChunkData.data = new DumbChunkSection[countOfSections]; + for (int c = 0; c < countOfSections; c++) { + DumbChunkSection dumbChunkSection = new DumbChunkSection(); + + dumbChunkSection.bitsPerBlock = netStream.readUnsignedByte(); + int sizePalette = netStream.readVarInt(); + dumbChunkSection.palette = new ArrayList<>(sizePalette); + for (int i = 0; i < sizePalette; i++) { + dumbChunkSection.palette.add(deserializeBlockState(netStream.readVarInt())); + } + + final byte[] rawData = new byte[netStream.readVarInt() * 8]; + netStream.readBytes(rawData); + LongBuffer data = ByteBuffer.wrap(rawData).asLongBuffer(); + + final int bitMask = (1 << dumbChunkSection.bitsPerBlock) - 1; + dumbChunkSection.data = new ArrayList<>(4096); + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + final int blockNumber = (((y << 4) + z) << 4) + x; + final int startLong = ( blockNumber * dumbChunkSection.bitsPerBlock ) / 64; + final int startOffset = ( blockNumber * dumbChunkSection.bitsPerBlock ) % 64; + final int endLong = ((blockNumber + 1) * dumbChunkSection.bitsPerBlock - 1) / 64; + + int idxBlock; + if (startLong == endLong) { + idxBlock = (int)(data.get(startLong) >> startOffset); + } else { + int endOffset = 64 - startOffset; + idxBlock = (int)(data.get(startLong) >> startOffset | data.get(endLong) << endOffset); + } + + dumbChunkSection.data.add(dumbChunkSection.palette.get(idxBlock & bitMask)); + } + } + } + + dumbChunkSection.blockLight = new byte[2048]; + netStream.readBytes(dumbChunkSection.blockLight); + dumbChunkSection.skyLight = new byte[2048]; + netStream.readBytes(dumbChunkSection.skyLight); + + dumbChunkData.data[c] = dumbChunkSection; + } + + dumbChunkData.biomes = new byte[256]; + netStream.readBytes(dumbChunkData.biomes); + + dumbChunkData.numberNBT = netStream.readVarInt(); + + return dumbChunkData; + } + + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @Getter + static class DumbChunkSection { + private int bitsPerBlock; + private List palette; + + private List data; + private byte[] blockLight; + private byte[] skyLight; + } +} diff --git a/simple_world/src/main/java/mc/world/simple/SimpleChunkSection.java b/simple_world/src/main/java/mc/world/simple/SimpleChunkSection.java index a7fb197..5fa72d6 100644 --- a/simple_world/src/main/java/mc/world/simple/SimpleChunkSection.java +++ b/simple_world/src/main/java/mc/world/simple/SimpleChunkSection.java @@ -1,6 +1,5 @@ package mc.world.simple; -import mc.core.world.Biome; import mc.core.world.block.Block; import mc.core.world.block.BlockFactory; import mc.core.world.block.BlockType; @@ -35,11 +34,6 @@ public class SimpleChunkSection implements ChunkSection { public void setAddition(int x, int y, int z, int value) { } - @Override - public Biome getBiomeLocal(int x, int z) { - return Biome.PLAINS; - } - @Override public int getX() { return 0; diff --git a/simple_world/src/test/java/mc/world/simple/SimpleChunkSectionTest.java b/simple_world/src/test/java/mc/world/simple/SimpleChunkSectionTest.java index 4804190..f9745b1 100644 --- a/simple_world/src/test/java/mc/world/simple/SimpleChunkSectionTest.java +++ b/simple_world/src/test/java/mc/world/simple/SimpleChunkSectionTest.java @@ -4,6 +4,7 @@ import com.google.common.collect.Lists; import mc.core.world.block.Block; import mc.core.world.block.BlockType; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.List; @@ -27,6 +28,7 @@ class SimpleChunkSectionTest { } @Test + @Disabled void getBlock() { for (int y = 15; y >= 0; y--) { for (int x = 0; x < 16; x++) {