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 7770eb7..255ba79 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/AnvilBlock.java +++ b/anvil-loader/src/main/java/mc/world/anvil/AnvilBlock.java @@ -1,12 +1,14 @@ package mc.world.anvil; import com.flowpowered.nbt.Tag; +import lombok.extern.slf4j.Slf4j; import mc.core.world.block.Block; import mc.core.world.block.BlockLocation; import mc.core.world.block.BlockType; import java.util.stream.Stream; +@Slf4j public class AnvilBlock implements Block { private final AnvilChunkSection chunkSection; private final BlockLocation location; @@ -18,7 +20,9 @@ public class AnvilBlock implements Block { @Override public int getLight() { - return chunkSection.getBlockLight().get(((16 * location.getZ()) * location.getY()) + location.getX()); + final int idx = (location.getY() << 8 | location.getZ() << 4 | location.getX()) >> 1; + final int value = chunkSection.getBlockLight().get(idx); + return (idx & 1) == 0 ? value & 15 : value >> 4 & 15; } @Override @@ -28,7 +32,7 @@ public class AnvilBlock implements Block { @Override public BlockType getBlockType() { - byte id = chunkSection.getBlocks().get(((16 * location.getZ()) * location.getY()) + location.getX()); + byte id = chunkSection.getBlocks().get((location.getY() * 256) + (location.getZ() * 16) + location.getX()); return BlockType.getByIdMeta(id, 0/*FIXME*/); } 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 caa04a5..69664ec 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/AnvilChunk.java +++ b/anvil-loader/src/main/java/mc/world/anvil/AnvilChunk.java @@ -22,7 +22,6 @@ public class AnvilChunk implements Chunk { @SuppressWarnings("unchecked") AnvilChunk(CompoundTag chunkTag) { - log.info(chunkTag.toString()); CompoundMap levelTagMap = ((CompoundTag) chunkTag.getValue().get("Level")).getValue(); this.x = ((IntTag) levelTagMap.get("xPos")).getValue(); @@ -50,6 +49,7 @@ public class AnvilChunk implements Chunk { @Override public ChunkSection getChunkSection(int height) { + if (height > sections.size()-1) return null; return sections.get(height); } diff --git a/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkProvider.java b/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkProvider.java index 6b59e6e..6496485 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkProvider.java +++ b/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkProvider.java @@ -1,19 +1,27 @@ package mc.world.anvil; +import lombok.NoArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import mc.core.world.chunk.Chunk; import mc.core.world.chunk.ChunkProvider; +import org.springframework.stereotype.Component; + +import java.nio.file.Paths; @Slf4j +@Component +@NoArgsConstructor @Setter public class AnvilChunkProvider implements ChunkProvider { private RegionManager regionManager; + public AnvilChunkProvider(String mapPath) { + this.setRegionManager(new RegionManager(Paths.get(mapPath).resolve("region"))); + } + @Override public Chunk getChunk(int x, int z) { - log.info("r.{}.{}", x/32, z/32); - Region region = regionManager.getRegion(x / 32, z / 32); if (region == null) return null; 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 315e8ee..cdeb281 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkSection.java +++ b/anvil-loader/src/main/java/mc/world/anvil/AnvilChunkSection.java @@ -42,7 +42,9 @@ public class AnvilChunkSection implements ChunkSection { @Override public int getSkyLight(int x, int y, int z) { - return skyLight.get(((16 * z) * y) + x); + final int idx = (y << 8 | z << 4 | x) >> 1; + final int value = skyLight.get(idx); + return (idx & 1) == 0 ? value & 15 : value >> 4 & 15; } @Override diff --git a/anvil-loader/src/main/java/mc/world/anvil/Main.java b/anvil-loader/src/main/java/mc/world/anvil/Main.java deleted file mode 100644 index 7a475d6..0000000 --- a/anvil-loader/src/main/java/mc/world/anvil/Main.java +++ /dev/null @@ -1,41 +0,0 @@ -package mc.world.anvil; - -import com.flowpowered.nbt.CompoundTag; -import com.flowpowered.nbt.Tag; -import com.flowpowered.nbt.stream.NBTInputStream; -import lombok.extern.slf4j.Slf4j; -import mc.core.world.chunk.Chunk; -import mc.core.world.chunk.ChunkProvider; - -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -@Slf4j -public class Main { - private static LevelInfo buildLevelInfo(Path levelDatPath) throws IOException { - try (NBTInputStream nbtInputStream = new NBTInputStream(new FileInputStream(levelDatPath.toFile()))) { - Tag rootTag = nbtInputStream.readTag(); - return new LevelInfo((CompoundTag) rootTag); - } - } - - public static void main(String[] args) throws IOException { - final Path worldPath = Paths.get(args[0]); - - // level.dat - LevelInfo levelInfo = buildLevelInfo(worldPath.resolve("level.dat")); - log.info(levelInfo.toString()); - - // regions - RegionManager regionManager = new RegionManager(worldPath.resolve("region")); - AnvilChunkProvider chunkProvider = new AnvilChunkProvider(); - chunkProvider.setRegionManager(regionManager); - - Chunk chunk = chunkProvider.getChunk(0, 0); - log.info("{}", chunk.getBiome(0,0)); - log.info("{}", chunk.getChunkSection(0).getBiome(0,0)); - log.info("{}", chunk.getChunkSection(0).getBlock(0,0,0).getBlockType()); - } -} diff --git a/anvil-loader/src/main/java/mc/world/anvil/Region.java b/anvil-loader/src/main/java/mc/world/anvil/Region.java index 38c30f9..d8bbf33 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/Region.java +++ b/anvil-loader/src/main/java/mc/world/anvil/Region.java @@ -16,31 +16,14 @@ import java.util.zip.InflaterInputStream; class Region implements Closeable { private static final byte BYTE_TRUE = 1, BYTE_FALSE = 0; - private static final byte[] EMPTY_SECTOR = new byte[4096]; private RandomAccessFile file; private TByteList sectorFree; private final int[] offsets = new int[1024]; - private void correctingSizeRegionFile() throws IOException { - if (this.file.length() < 4096L) { - this.file.write(EMPTY_SECTOR); - this.file.write(EMPTY_SECTOR); - } - - if ((this.file.length() & 4095L) != 0L) { - for (int i = 0; i < (this.file.length() & 4095L); i++) { - this.file.write(0); - } - } - } - Region(File file) throws IOException { this.file = new RandomAccessFile(file, "rw"); - //TODO ??? - correctingSizeRegionFile(); - int sizeOfSectorFree = (int)this.file.length() / 4096; sectorFree = new TByteArrayList(sizeOfSectorFree); @@ -50,8 +33,6 @@ class Region implements Closeable { sectorFree.add(BYTE_TRUE); } - this.file.seek(0L); - for (int i = 0; i < offsets.length; ++i) { int read = this.file.readInt(); offsets[i] = read; @@ -68,7 +49,12 @@ class Region implements Closeable { @Nullable Chunk getChunk(int x, int z) { - int offset = getOffset(x, z); + int offset; + try { + offset = getOffset(x, z); + } catch (Exception e) { + return null; + } if (offset == 0) return null; @@ -85,10 +71,8 @@ class Region implements Closeable { boolean gzippedData = (file.readByte() == 0x01); if (gzippedData) { - log.info("GZipped"); + log.warn("GZipped"); } else { - log.info("Inflaten"); - byte[] buffer = new byte[read - 1]; file.read(buffer); InflaterInputStream inputStream = new InflaterInputStream(new ByteArrayInputStream(buffer)); diff --git a/anvil-loader/src/main/java/mc/world/anvil/RegionManager.java b/anvil-loader/src/main/java/mc/world/anvil/RegionManager.java index dfd1843..b0cdb50 100644 --- a/anvil-loader/src/main/java/mc/world/anvil/RegionManager.java +++ b/anvil-loader/src/main/java/mc/world/anvil/RegionManager.java @@ -9,13 +9,18 @@ import org.springframework.lang.Nullable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; @Slf4j -class RegionManager { +public class RegionManager { private final Path regionFilesPath; private final TIntObjectMap regions = new TIntObjectHashMap<>(); - RegionManager(Path regionFilesPath) { + public RegionManager(String regionFilesPath) { + this(Paths.get(regionFilesPath)); + } + + public RegionManager(Path regionFilesPath) { this.regionFilesPath = regionFilesPath; } diff --git a/anvil-loader/src/test/java/mc/world/anvil/RegionTest.java b/anvil-loader/src/test/java/mc/world/anvil/RegionTest.java new file mode 100644 index 0000000..4a37172 --- /dev/null +++ b/anvil-loader/src/test/java/mc/world/anvil/RegionTest.java @@ -0,0 +1,60 @@ +package mc.world.anvil; + +import com.google.common.collect.Lists; +import mc.core.world.block.Block; +import mc.core.world.block.BlockType; +import mc.core.world.chunk.Chunk; +import mc.core.world.chunk.ChunkSection; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class RegionTest { + private static Region region; + private static List layersBlock; + + @BeforeAll + static void before() throws URISyntaxException, IOException { + region = new Region(Paths.get(RegionTest.class.getResource("/region/r.0.0.mca").toURI()).toFile()); + + layersBlock = Lists.newArrayList( + BlockType.BEDROCK, + BlockType.DIRT, + BlockType.DIRT, + BlockType.GRASS + ); + } + + @Test + void getChunk() { + for (int cZ = 0; cZ < 32; cZ++) { + for (int cX = 0; cX < 32; cX++) { + Chunk chunk = region.getChunk(cX, cZ); + assertNotNull(chunk); + ChunkSection chunkSection = chunk.getChunkSection(0); + assertNotNull(chunkSection); + + for (int y = 0; y < 16; y++) { + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + Block block = chunkSection.getBlock(x, y, z); + if (y > layersBlock.size()-1) { + assertEquals(BlockType.AIR, block.getBlockType(), String.format("coords: %d %d %d", x+(cX*16), y, z+(cZ*16))); + } else { + assertEquals(layersBlock.get(y), block.getBlockType(), String.format("coords: %d %d %d", x+(cX*16), y, z+(cZ*16))); + } + } + } + } + } + } + } + +} diff --git a/anvil-loader/src/test/resources/region/r.0.0.mca b/anvil-loader/src/test/resources/region/r.0.0.mca new file mode 100644 index 0000000..b696b94 Binary files /dev/null and b/anvil-loader/src/test/resources/region/r.0.0.mca differ