From edee95689392a8bfd56e4c5f422385751b3cae73 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Mon, 5 Feb 2024 16:38:33 +0100 Subject: [PATCH] Implement support for 1.13+ chunks and some fixes --- .../bluemap/common/BlueMapService.java | 2 +- .../BlockColorCalculatorFactory.java | 50 ++-- .../core/resources/datapack/DataPack.java | 13 +- .../bluemap/core/world/mca/MCAWorld.java | 22 +- .../core/world/mca/chunk/ChunkLoader.java | 6 +- .../core/world/mca/chunk/Chunk_1_13.java | 274 ++++++++++++++++++ .../core/world/mca/chunk/Chunk_1_15.java | 251 +--------------- .../core/world/mca/data/LevelData.java | 4 + 8 files changed, 326 insertions(+), 296 deletions(-) create mode 100644 BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java index 572a6cc9..94c39c08 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/BlueMapService.java @@ -239,7 +239,7 @@ public class BlueMapService implements Closeable { try { - Logger.global.logInfo("Loading map '" + name + "'..."); + Logger.global.logInfo("Loading map '" + id + "'..."); BmMap map = new BmMap( id, name, diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/BlockColorCalculatorFactory.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/BlockColorCalculatorFactory.java index 804f3be5..e745c5e7 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/BlockColorCalculatorFactory.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/BlockColorCalculatorFactory.java @@ -42,6 +42,14 @@ import java.util.Map; @DebugDump public class BlockColorCalculatorFactory { + private static final int + AVERAGE_MIN_X = - 2, + AVERAGE_MAX_X = 2, + AVERAGE_MIN_Y = - 1, + AVERAGE_MAX_Y = 1, + AVERAGE_MIN_Z = - 2, + AVERAGE_MAX_Z = 2; + private final int[] foliageMap = new int[65536]; private final int[] grassMap = new int[65536]; @@ -133,18 +141,12 @@ public class BlockColorCalculatorFactory { public Color getWaterAverageColor(BlockNeighborhood block, Color target) { target.set(0, 0, 0, 0, true); - int x, y, z, - minX = - 2, - maxX = 2, - minY = - 1, - maxY = 1, - minZ = - 2, - maxZ = 2; + int x, y, z; Biome biome; - for (x = minX; x <= maxX; x++) { - for (y = minY; y <= maxY; y++) { - for (z = minZ; z <= maxZ; z++) { + for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) { + for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) { + for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) { biome = block.getNeighborBlock(x, y, z).getBiome(); target.add(biome.getWaterColor()); } @@ -157,18 +159,12 @@ public class BlockColorCalculatorFactory { public Color getFoliageAverageColor(BlockNeighborhood block, Color target) { target.set(0, 0, 0, 0, true); - int x, y, z, - minX = - 2, - maxX = 2, - minY = - 1, - maxY = 1, - minZ = - 2, - maxZ = 2; + int x, y, z; Biome biome; - for (y = minY; y <= maxY; y++) { - for (x = minX; x <= maxX; x++) { - for (z = minZ; z <= maxZ; z++) { + for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) { + for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) { + for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) { biome = block.getNeighborBlock(x, y, z).getBiome(); target.add(getFoliageColor(biome, tempColor)); } @@ -186,18 +182,12 @@ public class BlockColorCalculatorFactory { public Color getGrassAverageColor(BlockNeighborhood block, Color target) { target.set(0, 0, 0, 0, true); - int x, y, z, - minX = - 2, - maxX = 2, - minY = - 1, - maxY = 1, - minZ = - 2, - maxZ = 2; + int x, y, z; Biome biome; - for (y = minY; y <= maxY; y++) { - for (x = minX; x <= maxX; x++) { - for (z = minZ; z <= maxZ; z++) { + for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) { + for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) { + for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) { biome = block.getNeighborBlock(x, y, z).getBiome(); target.add(getGrassColor(biome, tempColor)); } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/DataPack.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/DataPack.java index 6ad207e4..cb60fc18 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/DataPack.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/resources/datapack/DataPack.java @@ -25,6 +25,11 @@ public class DataPack { public static final Key DIMENSION_THE_NETHER = new Key("minecraft", "the_nether"); public static final Key DIMENSION_THE_END = new Key("minecraft", "the_end"); + public static final Key DIMENSION_TYPE_OVERWORLD = new Key("minecraft", "overworld"); + public static final Key DIMENSION_TYPE_OVERWORLD_CAVES = new Key("minecraft", "overworld_caves"); + public static final Key DIMENSION_TYPE_THE_NETHER = new Key("minecraft", "the_nether"); + public static final Key DIMENSION_TYPE_THE_END = new Key("minecraft", "the_end"); + private final Map dimensionTypes = new HashMap<>(); @Nullable @@ -80,10 +85,10 @@ public class DataPack { } public void bake() { - dimensionTypes.putIfAbsent(new Key("minecraft", "overworld"), DimensionType.OVERWORLD); - dimensionTypes.putIfAbsent(new Key("minecraft", "overworld_caves"), DimensionType.OVERWORLD_CAVES); - dimensionTypes.putIfAbsent(new Key("minecraft", "the_nether"), DimensionType.NETHER); - dimensionTypes.putIfAbsent(new Key("minecraft", "the_end"), DimensionType.END); + dimensionTypes.putIfAbsent(DIMENSION_TYPE_OVERWORLD, DimensionType.OVERWORLD); + dimensionTypes.putIfAbsent(DIMENSION_TYPE_OVERWORLD_CAVES, DimensionType.OVERWORLD_CAVES); + dimensionTypes.putIfAbsent(DIMENSION_TYPE_THE_NETHER, DimensionType.NETHER); + dimensionTypes.putIfAbsent(DIMENSION_TYPE_THE_END, DimensionType.END); } private static Stream list(Path root) { diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAWorld.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAWorld.java index 9d35dfba..f79e2531 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAWorld.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAWorld.java @@ -61,7 +61,7 @@ public class MCAWorld implements World { .build(this::loadRegion); private final LoadingCache chunkCache = Caffeine.newBuilder() .executor(BlueMap.THREAD_POOL) - .maximumSize(2048) // 2 regions worth of chunks + .maximumSize(10240) // 10 regions worth of chunks .expireAfterWrite(10, TimeUnit.MINUTES) .build(this::loadChunk); @@ -72,16 +72,22 @@ public class MCAWorld implements World { this.levelData = levelData; this.dataPack = dataPack; - LevelData.Dimension dim = levelData.getData().getWorldGenSettings().getDimensions().get(dimension.getFormatted()); - if (dim == null) { - Logger.global.logWarning("The level-data does not contain any dimension with the id '" + dimension + - "', using fallback."); - dim = new LevelData.Dimension(); + LevelData.Dimension dimensionData = levelData.getData().getWorldGenSettings().getDimensions().get(dimension.getFormatted()); + if (dimensionData == null) { + if (DataPack.DIMENSION_OVERWORLD.equals(dimension)) dimensionData = new LevelData.Dimension(DataPack.DIMENSION_TYPE_OVERWORLD.getFormatted()); + else if (DataPack.DIMENSION_THE_NETHER.equals(dimension)) dimensionData = new LevelData.Dimension(DataPack.DIMENSION_TYPE_THE_NETHER.getFormatted()); + else if (DataPack.DIMENSION_THE_END.equals(dimension)) dimensionData = new LevelData.Dimension(DataPack.DIMENSION_TYPE_THE_END.getFormatted()); + else { + Logger.global.logWarning("The level-data does not contain any dimension with the id '" + dimension + + "', using fallback."); + dimensionData = new LevelData.Dimension(); + } } - DimensionType dimensionType = dataPack.getDimensionType(new Key(dim.getType())); + + DimensionType dimensionType = dataPack.getDimensionType(new Key(dimensionData.getType())); if (dimensionType == null) { Logger.global.logWarning("The data-pack for world '" + worldFolder + - "' does not contain any dimension-type with the id '" + dim.getType() + "', using fallback."); + "' does not contain any dimension-type with the id '" + dimensionData.getType() + "', using fallback."); dimensionType = DimensionType.OVERWORLD; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/ChunkLoader.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/ChunkLoader.java index e6eb0b80..d0d1c986 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/ChunkLoader.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/ChunkLoader.java @@ -18,10 +18,10 @@ public class ChunkLoader { // sorted list of chunk-versions, loaders at the start of the list are preferred over loaders at the end private static final List> CHUNK_VERSION_LOADERS = List.of( - //new ChunkVersionLoader<>(Chunk_1_13.Data.class, Chunk_1_13::new, 0), - new ChunkVersionLoader<>(Chunk_1_15.Data.class, Chunk_1_15::new, 2200), + new ChunkVersionLoader<>(Chunk_1_18.Data.class, Chunk_1_18::new, 2844), new ChunkVersionLoader<>(Chunk_1_16.Data.class, Chunk_1_16::new, 2500), - new ChunkVersionLoader<>(Chunk_1_18.Data.class, Chunk_1_18::new, 2844) + new ChunkVersionLoader<>(Chunk_1_15.Data.class, Chunk_1_15::new, 2200), + new ChunkVersionLoader<>(Chunk_1_13.Data.class, Chunk_1_13::new, 0) ); private ChunkVersionLoader lastUsedLoader = CHUNK_VERSION_LOADERS.get(0); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java new file mode 100644 index 00000000..083b3a5c --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java @@ -0,0 +1,274 @@ +package de.bluecolored.bluemap.core.world.mca.chunk; + +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluemap.core.util.Key; +import de.bluecolored.bluemap.core.world.Biome; +import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.DimensionType; +import de.bluecolored.bluemap.core.world.LightData; +import de.bluecolored.bluemap.core.world.mca.MCAUtil; +import de.bluecolored.bluemap.core.world.mca.region.MCARegion; +import de.bluecolored.bluenbt.NBTName; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +public class Chunk_1_13 extends MCAChunk { + + private static final Level EMPTY_LEVEL = new Level(); + private static final HeightmapsData EMPTY_HEIGHTMAPS_DATA = new HeightmapsData(); + + private static final Key STATUS_EMPTY = new Key("minecraft", "empty"); + private static final Key STATUS_FULL = new Key("minecraft", "full"); + private static final Key STATUS_FULLCHUNK = new Key("minecraft", "fullchunk"); + private static final Key STATUS_POSTPROCESSED = new Key("minecraft", "postprocessed"); + + private final boolean generated; + private final boolean hasLightData; + private final long inhabitedTime; + + private final int skyLight; + + private final boolean hasWorldSurfaceHeights; + private final long[] worldSurfaceHeights; + private final boolean hasOceanFloorHeights; + private final long[] oceanFloorHeights; + + private final Section[] sections; + private final int sectionMin, sectionMax; + + final int[] biomes; + + public Chunk_1_13(MCARegion region, Data data) { + super(region, data); + + Level level = data.level; + + this.generated = !STATUS_EMPTY.equals(level.status); + this.hasLightData = + STATUS_FULL.equals(level.status) || + STATUS_FULLCHUNK.equals(level.status) || + STATUS_POSTPROCESSED.equals(level.status); + this.inhabitedTime = level.inhabitedTime; + + DimensionType dimensionType = getRegion().getWorld().getDimensionType(); + this.skyLight = dimensionType.hasSkylight() ? 16 : 0; + + this.worldSurfaceHeights = level.heightmaps.worldSurface; + this.oceanFloorHeights = level.heightmaps.oceanFloor; + + this.hasWorldSurfaceHeights = this.worldSurfaceHeights.length >= 36; + this.hasOceanFloorHeights = this.oceanFloorHeights.length >= 36; + + this.biomes = level.biomes; + + SectionData[] sectionsData = level.sections; + if (sectionsData != null && sectionsData.length > 0) { + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + + // find section min/max y + for (SectionData sectionData : sectionsData) { + int y = sectionData.getY(); + if (min > y) min = y; + if (max < y) max = y; + } + + // load sections into ordered array + this.sections = new Section[1 + max - min]; + for (SectionData sectionData : sectionsData) { + Section section = new Section(sectionData); + int y = section.getSectionY(); + + if (min > y) min = y; + if (max < y) max = y; + + this.sections[section.sectionY - min] = section; + } + + this.sectionMin = min; + this.sectionMax = max; + } else { + this.sections = new Section[0]; + this.sectionMin = 0; + this.sectionMax = 0; + } + } + + @Override + public boolean isGenerated() { + return generated; + } + + @Override + public boolean hasLightData() { + return hasLightData; + } + + @Override + public long getInhabitedTime() { + return inhabitedTime; + } + + @Override + public BlockState getBlockState(int x, int y, int z) { + Section section = getSection(y >> 4); + if (section == null) return BlockState.AIR; + + return section.getBlockState(x, y, z); + } + + @Override + public String getBiome(int x, int y, int z) { + if (this.biomes.length < 256) return Biome.DEFAULT.getFormatted(); + + int biomeIntIndex = (z & 0xF) << 4 | x & 0xF; + return LegacyBiomes.idFor(biomes[biomeIntIndex]); + } + + @Override + public LightData getLightData(int x, int y, int z, LightData target) { + if (!hasLightData) return target.set(skyLight, 0); + + int sectionY = y >> 4; + Section section = getSection(sectionY); + if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(skyLight, 0); + + return section.getLightData(x, y, z, target); + } + + @Override + public int getMinY(int x, int z) { + return sectionMin * 16; + } + + @Override + public int getMaxY(int x, int z) { + return sectionMax * 16 + 15; + } + + @Override + public boolean hasWorldSurfaceHeights() { + return hasWorldSurfaceHeights; + } + + @Override + public int getWorldSurfaceY(int x, int z) { + return (int) MCAUtil.getValueFromLongStream( + worldSurfaceHeights, + (z & 0xF) << 4 | x & 0xF, + 9 + ); + } + + @Override + public boolean hasOceanFloorHeights() { + return hasOceanFloorHeights; + } + + @Override + public int getOceanFloorY(int x, int z) { + return (int) MCAUtil.getValueFromLongStream( + oceanFloorHeights, + (z & 0xF) << 4 | x & 0xF, + 9 + ); + } + + private @Nullable Section getSection(int y) { + y -= sectionMin; + if (y < 0 || y >= this.sections.length) return null; + return this.sections[y]; + } + + protected static class Section { + + private final int sectionY; + private final BlockState[] blockPalette; + private final long[] blocks; + private final byte[] blockLight; + private final byte[] skyLight; + + private final int bitsPerBlock; + + public Section(SectionData sectionData) { + this.sectionY = sectionData.y; + + this.blockPalette = sectionData.palette; + this.blocks = sectionData.blockStates; + + this.blockLight = sectionData.getBlockLight(); + this.skyLight = sectionData.getSkyLight(); + + this.bitsPerBlock = this.blocks.length >> 6; // available longs * 64 (bits per long) / 4096 (blocks per section) (floored result) + } + + public BlockState getBlockState(int x, int y, int z) { + if (blockPalette.length == 1) return blockPalette[0]; + if (blockPalette.length == 0) return BlockState.AIR; + + int id = (int) MCAUtil.getValueFromLongStream( + blocks, + (y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF, + bitsPerBlock + ); + if (id >= blockPalette.length) { + Logger.global.noFloodWarning("palette-warning", "Got block-palette id " + id + " but palette has size of " + blockPalette.length + "! (Future occasions of this error will not be logged)"); + return BlockState.MISSING; + } + + return blockPalette[id]; + } + + public LightData getLightData(int x, int y, int z, LightData target) { + if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); + + int blockByteIndex = (y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF; + int blockHalfByteIndex = blockByteIndex >> 1; + boolean largeHalf = (blockByteIndex & 0x1) != 0; + + return target.set( + this.skyLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, + this.blockLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 + ); + } + + public int getSectionY() { + return sectionY; + } + + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + public static class Data extends MCAChunk.Data { + private Level level = EMPTY_LEVEL; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + public static class Level { + private Key status = STATUS_EMPTY; + private long inhabitedTime = 0; + private HeightmapsData heightmaps = EMPTY_HEIGHTMAPS_DATA; + private SectionData @Nullable [] sections = null; + private int[] biomes = EMPTY_INT_ARRAY; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class HeightmapsData { + @NBTName("WORLD_SURFACE") private long[] worldSurface = EMPTY_LONG_ARRAY; + @NBTName("OCEAN_FLOOR") private long[] oceanFloor = EMPTY_LONG_ARRAY; + } + + @Getter + @SuppressWarnings("FieldMayBeFinal") + protected static class SectionData { + private int y = 0; + private byte[] blockLight = EMPTY_BYTE_ARRAY; + private byte[] skyLight = EMPTY_BYTE_ARRAY; + private BlockState[] palette = EMPTY_BLOCKSTATE_ARRAY; + private long[] blockStates = EMPTY_LONG_ARRAY; + } + +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_15.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_15.java index a01ed70b..474ef263 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_15.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_15.java @@ -1,115 +1,12 @@ package de.bluecolored.bluemap.core.world.mca.chunk; -import de.bluecolored.bluemap.core.logger.Logger; -import de.bluecolored.bluemap.core.util.Key; import de.bluecolored.bluemap.core.world.Biome; -import de.bluecolored.bluemap.core.world.BlockState; -import de.bluecolored.bluemap.core.world.DimensionType; -import de.bluecolored.bluemap.core.world.LightData; -import de.bluecolored.bluemap.core.world.mca.MCAUtil; import de.bluecolored.bluemap.core.world.mca.region.MCARegion; -import de.bluecolored.bluenbt.NBTName; -import lombok.Getter; -import org.jetbrains.annotations.Nullable; -public class Chunk_1_15 extends MCAChunk { - - private static final Level EMPTY_LEVEL = new Level(); - private static final HeightmapsData EMPTY_HEIGHTMAPS_DATA = new HeightmapsData(); - - private static final Key STATUS_EMPTY = new Key("minecraft", "empty"); - private static final Key STATUS_FULL = new Key("minecraft", "full"); - - private final boolean generated; - private final boolean hasLightData; - private final long inhabitedTime; - - private final int skyLight; - - private final boolean hasWorldSurfaceHeights; - private final long[] worldSurfaceHeights; - private final boolean hasOceanFloorHeights; - private final long[] oceanFloorHeights; - - private final Section[] sections; - private final int sectionMin, sectionMax; - - private final int[] biomes; +public class Chunk_1_15 extends Chunk_1_13 { public Chunk_1_15(MCARegion region, Data data) { super(region, data); - - Level level = data.level; - - this.generated = !STATUS_EMPTY.equals(level.status); - this.hasLightData = STATUS_FULL.equals(level.status); - this.inhabitedTime = level.inhabitedTime; - - DimensionType dimensionType = getRegion().getWorld().getDimensionType(); - this.skyLight = dimensionType.hasSkylight() ? 16 : 0; - - this.worldSurfaceHeights = level.heightmaps.worldSurface; - this.oceanFloorHeights = level.heightmaps.oceanFloor; - - this.hasWorldSurfaceHeights = this.worldSurfaceHeights.length >= 36; - this.hasOceanFloorHeights = this.oceanFloorHeights.length >= 36; - - this.biomes = level.biomes; - - SectionData[] sectionsData = level.sections; - if (sectionsData != null && sectionsData.length > 0) { - int min = Integer.MAX_VALUE; - int max = Integer.MIN_VALUE; - - // find section min/max y - for (SectionData sectionData : sectionsData) { - int y = sectionData.getY(); - if (min > y) min = y; - if (max < y) max = y; - } - - // load sections into ordered array - this.sections = new Section[1 + max - min]; - for (SectionData sectionData : sectionsData) { - Section section = new Section(sectionData); - int y = section.getSectionY(); - - if (min > y) min = y; - if (max < y) max = y; - - this.sections[section.sectionY - min] = section; - } - - this.sectionMin = min; - this.sectionMax = max; - } else { - this.sections = new Section[0]; - this.sectionMin = 0; - this.sectionMax = 0; - } - } - - @Override - public boolean isGenerated() { - return generated; - } - - @Override - public boolean hasLightData() { - return hasLightData; - } - - @Override - public long getInhabitedTime() { - return inhabitedTime; - } - - @Override - public BlockState getBlockState(int x, int y, int z) { - Section section = getSection(y >> 4); - if (section == null) return BlockState.AIR; - - return section.getBlockState(x, y, z); } @Override @@ -125,150 +22,4 @@ public class Chunk_1_15 extends MCAChunk { return LegacyBiomes.idFor(biomes[biomeIntIndex]); } - @Override - public LightData getLightData(int x, int y, int z, LightData target) { - if (!hasLightData) return target.set(skyLight, 0); - - int sectionY = y >> 4; - Section section = getSection(sectionY); - if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(skyLight, 0); - - return section.getLightData(x, y, z, target); - } - - @Override - public int getMinY(int x, int z) { - return sectionMin * 16; - } - - @Override - public int getMaxY(int x, int z) { - return sectionMax * 16 + 15; - } - - @Override - public boolean hasWorldSurfaceHeights() { - return hasWorldSurfaceHeights; - } - - @Override - public int getWorldSurfaceY(int x, int z) { - return (int) MCAUtil.getValueFromLongStream( - worldSurfaceHeights, - (z & 0xF) << 4 | x & 0xF, - 9 - ); - } - - @Override - public boolean hasOceanFloorHeights() { - return hasOceanFloorHeights; - } - - @Override - public int getOceanFloorY(int x, int z) { - return (int) MCAUtil.getValueFromLongStream( - oceanFloorHeights, - (z & 0xF) << 4 | x & 0xF, - 9 - ); - } - - private @Nullable Section getSection(int y) { - y -= sectionMin; - if (y < 0 || y >= this.sections.length) return null; - return this.sections[y]; - } - - protected static class Section { - - private final int sectionY; - private final BlockState[] blockPalette; - private final long[] blocks; - private final byte[] blockLight; - private final byte[] skyLight; - - private final int bitsPerBlock; - - public Section(SectionData sectionData) { - this.sectionY = sectionData.y; - - this.blockPalette = sectionData.palette; - this.blocks = sectionData.blockStates; - - this.blockLight = sectionData.getBlockLight(); - this.skyLight = sectionData.getSkyLight(); - - this.bitsPerBlock = this.blocks.length >> 6; // available longs * 64 (bits per long) / 4096 (blocks per section) (floored result) - } - - public BlockState getBlockState(int x, int y, int z) { - if (blockPalette.length == 1) return blockPalette[0]; - if (blockPalette.length == 0) return BlockState.AIR; - - int id = (int) MCAUtil.getValueFromLongStream( - blocks, - (y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF, - bitsPerBlock - ); - if (id >= blockPalette.length) { - Logger.global.noFloodWarning("palette-warning", "Got block-palette id " + id + " but palette has size of " + blockPalette.length + "! (Future occasions of this error will not be logged)"); - return BlockState.MISSING; - } - - return blockPalette[id]; - } - - public LightData getLightData(int x, int y, int z, LightData target) { - if (blockLight.length == 0 && skyLight.length == 0) return target.set(0, 0); - - int blockByteIndex = (y & 0xF) << 8 | (z & 0xF) << 4 | x & 0xF; - int blockHalfByteIndex = blockByteIndex >> 1; - boolean largeHalf = (blockByteIndex & 0x1) != 0; - - return target.set( - this.skyLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf) : 0, - this.blockLight.length > blockHalfByteIndex ? MCAUtil.getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf) : 0 - ); - } - - public int getSectionY() { - return sectionY; - } - - } - - @Getter - @SuppressWarnings("FieldMayBeFinal") - public static class Data extends MCAChunk.Data { - private Level level = EMPTY_LEVEL; - } - - @Getter - @SuppressWarnings("FieldMayBeFinal") - public static class Level { - private Key status = STATUS_EMPTY; - private long inhabitedTime = 0; - private HeightmapsData heightmaps = EMPTY_HEIGHTMAPS_DATA; - private SectionData @Nullable [] sections = null; - private int[] biomes = EMPTY_INT_ARRAY; - } - - @Getter - @SuppressWarnings("FieldMayBeFinal") - protected static class HeightmapsData { - @NBTName("WORLD_SURFACE") private long[] worldSurface = EMPTY_LONG_ARRAY; - @NBTName("OCEAN_FLOOR") private long[] oceanFloor = EMPTY_LONG_ARRAY; - } - - @Getter - @SuppressWarnings("FieldMayBeFinal") - protected static class SectionData { - private int y = 0; - private byte[] blockLight = EMPTY_BYTE_ARRAY; - private byte[] skyLight = EMPTY_BYTE_ARRAY; - private BlockState[] palette = EMPTY_BLOCKSTATE_ARRAY; - private long[] blockStates = EMPTY_LONG_ARRAY; - } - } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/LevelData.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/LevelData.java index 83bb4006..7f89e344 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/LevelData.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/data/LevelData.java @@ -1,7 +1,9 @@ package de.bluecolored.bluemap.core.world.mca.data; import de.bluecolored.bluemap.api.debug.DebugDump; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import java.util.HashMap; import java.util.Map; @@ -28,6 +30,8 @@ public class LevelData { } @Getter + @NoArgsConstructor + @AllArgsConstructor @DebugDump public static class Dimension { private String type = "minecraft:overworld";