Implement support for 1.13+ chunks and some fixes
This commit is contained in:
parent
f3488f34f1
commit
edee956893
|
@ -239,7 +239,7 @@ public class BlueMapService implements Closeable {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Logger.global.logInfo("Loading map '" + name + "'...");
|
Logger.global.logInfo("Loading map '" + id + "'...");
|
||||||
BmMap map = new BmMap(
|
BmMap map = new BmMap(
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -42,6 +42,14 @@ import java.util.Map;
|
||||||
@DebugDump
|
@DebugDump
|
||||||
public class BlockColorCalculatorFactory {
|
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[] foliageMap = new int[65536];
|
||||||
private final int[] grassMap = new int[65536];
|
private final int[] grassMap = new int[65536];
|
||||||
|
|
||||||
|
@ -133,18 +141,12 @@ public class BlockColorCalculatorFactory {
|
||||||
public Color getWaterAverageColor(BlockNeighborhood<?> block, Color target) {
|
public Color getWaterAverageColor(BlockNeighborhood<?> block, Color target) {
|
||||||
target.set(0, 0, 0, 0, true);
|
target.set(0, 0, 0, 0, true);
|
||||||
|
|
||||||
int x, y, z,
|
int x, y, z;
|
||||||
minX = - 2,
|
|
||||||
maxX = 2,
|
|
||||||
minY = - 1,
|
|
||||||
maxY = 1,
|
|
||||||
minZ = - 2,
|
|
||||||
maxZ = 2;
|
|
||||||
|
|
||||||
Biome biome;
|
Biome biome;
|
||||||
for (x = minX; x <= maxX; x++) {
|
for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) {
|
||||||
for (y = minY; y <= maxY; y++) {
|
for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) {
|
||||||
for (z = minZ; z <= maxZ; z++) {
|
for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) {
|
||||||
biome = block.getNeighborBlock(x, y, z).getBiome();
|
biome = block.getNeighborBlock(x, y, z).getBiome();
|
||||||
target.add(biome.getWaterColor());
|
target.add(biome.getWaterColor());
|
||||||
}
|
}
|
||||||
|
@ -157,18 +159,12 @@ public class BlockColorCalculatorFactory {
|
||||||
public Color getFoliageAverageColor(BlockNeighborhood<?> block, Color target) {
|
public Color getFoliageAverageColor(BlockNeighborhood<?> block, Color target) {
|
||||||
target.set(0, 0, 0, 0, true);
|
target.set(0, 0, 0, 0, true);
|
||||||
|
|
||||||
int x, y, z,
|
int x, y, z;
|
||||||
minX = - 2,
|
|
||||||
maxX = 2,
|
|
||||||
minY = - 1,
|
|
||||||
maxY = 1,
|
|
||||||
minZ = - 2,
|
|
||||||
maxZ = 2;
|
|
||||||
|
|
||||||
Biome biome;
|
Biome biome;
|
||||||
for (y = minY; y <= maxY; y++) {
|
for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) {
|
||||||
for (x = minX; x <= maxX; x++) {
|
for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) {
|
||||||
for (z = minZ; z <= maxZ; z++) {
|
for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) {
|
||||||
biome = block.getNeighborBlock(x, y, z).getBiome();
|
biome = block.getNeighborBlock(x, y, z).getBiome();
|
||||||
target.add(getFoliageColor(biome, tempColor));
|
target.add(getFoliageColor(biome, tempColor));
|
||||||
}
|
}
|
||||||
|
@ -186,18 +182,12 @@ public class BlockColorCalculatorFactory {
|
||||||
public Color getGrassAverageColor(BlockNeighborhood<?> block, Color target) {
|
public Color getGrassAverageColor(BlockNeighborhood<?> block, Color target) {
|
||||||
target.set(0, 0, 0, 0, true);
|
target.set(0, 0, 0, 0, true);
|
||||||
|
|
||||||
int x, y, z,
|
int x, y, z;
|
||||||
minX = - 2,
|
|
||||||
maxX = 2,
|
|
||||||
minY = - 1,
|
|
||||||
maxY = 1,
|
|
||||||
minZ = - 2,
|
|
||||||
maxZ = 2;
|
|
||||||
|
|
||||||
Biome biome;
|
Biome biome;
|
||||||
for (y = minY; y <= maxY; y++) {
|
for (y = AVERAGE_MIN_Y; y <= AVERAGE_MAX_Y; y++) {
|
||||||
for (x = minX; x <= maxX; x++) {
|
for (x = AVERAGE_MIN_X; x <= AVERAGE_MAX_X; x++) {
|
||||||
for (z = minZ; z <= maxZ; z++) {
|
for (z = AVERAGE_MIN_Z; z <= AVERAGE_MAX_Z; z++) {
|
||||||
biome = block.getNeighborBlock(x, y, z).getBiome();
|
biome = block.getNeighborBlock(x, y, z).getBiome();
|
||||||
target.add(getGrassColor(biome, tempColor));
|
target.add(getGrassColor(biome, tempColor));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_NETHER = new Key("minecraft", "the_nether");
|
||||||
public static final Key DIMENSION_THE_END = new Key("minecraft", "the_end");
|
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<Key, DimensionType> dimensionTypes = new HashMap<>();
|
private final Map<Key, DimensionType> dimensionTypes = new HashMap<>();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -80,10 +85,10 @@ public class DataPack {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bake() {
|
public void bake() {
|
||||||
dimensionTypes.putIfAbsent(new Key("minecraft", "overworld"), DimensionType.OVERWORLD);
|
dimensionTypes.putIfAbsent(DIMENSION_TYPE_OVERWORLD, DimensionType.OVERWORLD);
|
||||||
dimensionTypes.putIfAbsent(new Key("minecraft", "overworld_caves"), DimensionType.OVERWORLD_CAVES);
|
dimensionTypes.putIfAbsent(DIMENSION_TYPE_OVERWORLD_CAVES, DimensionType.OVERWORLD_CAVES);
|
||||||
dimensionTypes.putIfAbsent(new Key("minecraft", "the_nether"), DimensionType.NETHER);
|
dimensionTypes.putIfAbsent(DIMENSION_TYPE_THE_NETHER, DimensionType.NETHER);
|
||||||
dimensionTypes.putIfAbsent(new Key("minecraft", "the_end"), DimensionType.END);
|
dimensionTypes.putIfAbsent(DIMENSION_TYPE_THE_END, DimensionType.END);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream<Path> list(Path root) {
|
private static Stream<Path> list(Path root) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class MCAWorld implements World {
|
||||||
.build(this::loadRegion);
|
.build(this::loadRegion);
|
||||||
private final LoadingCache<Vector2i, Chunk> chunkCache = Caffeine.newBuilder()
|
private final LoadingCache<Vector2i, Chunk> chunkCache = Caffeine.newBuilder()
|
||||||
.executor(BlueMap.THREAD_POOL)
|
.executor(BlueMap.THREAD_POOL)
|
||||||
.maximumSize(2048) // 2 regions worth of chunks
|
.maximumSize(10240) // 10 regions worth of chunks
|
||||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||||
.build(this::loadChunk);
|
.build(this::loadChunk);
|
||||||
|
|
||||||
|
@ -72,16 +72,22 @@ public class MCAWorld implements World {
|
||||||
this.levelData = levelData;
|
this.levelData = levelData;
|
||||||
this.dataPack = dataPack;
|
this.dataPack = dataPack;
|
||||||
|
|
||||||
LevelData.Dimension dim = levelData.getData().getWorldGenSettings().getDimensions().get(dimension.getFormatted());
|
LevelData.Dimension dimensionData = levelData.getData().getWorldGenSettings().getDimensions().get(dimension.getFormatted());
|
||||||
if (dim == null) {
|
if (dimensionData == null) {
|
||||||
Logger.global.logWarning("The level-data does not contain any dimension with the id '" + dimension +
|
if (DataPack.DIMENSION_OVERWORLD.equals(dimension)) dimensionData = new LevelData.Dimension(DataPack.DIMENSION_TYPE_OVERWORLD.getFormatted());
|
||||||
"', using fallback.");
|
else if (DataPack.DIMENSION_THE_NETHER.equals(dimension)) dimensionData = new LevelData.Dimension(DataPack.DIMENSION_TYPE_THE_NETHER.getFormatted());
|
||||||
dim = new LevelData.Dimension();
|
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) {
|
if (dimensionType == null) {
|
||||||
Logger.global.logWarning("The data-pack for world '" + worldFolder +
|
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;
|
dimensionType = DimensionType.OVERWORLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
// sorted list of chunk-versions, loaders at the start of the list are preferred over loaders at the end
|
||||||
private static final List<ChunkVersionLoader<?>> CHUNK_VERSION_LOADERS = List.of(
|
private static final List<ChunkVersionLoader<?>> CHUNK_VERSION_LOADERS = List.of(
|
||||||
//new ChunkVersionLoader<>(Chunk_1_13.Data.class, Chunk_1_13::new, 0),
|
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_16.Data.class, Chunk_1_16::new, 2500),
|
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);
|
private ChunkVersionLoader<?> lastUsedLoader = CHUNK_VERSION_LOADERS.get(0);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,115 +1,12 @@
|
||||||
package de.bluecolored.bluemap.core.world.mca.chunk;
|
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.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.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 {
|
public class Chunk_1_15 extends Chunk_1_13 {
|
||||||
|
|
||||||
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 Chunk_1_15(MCARegion region, Data data) {
|
public Chunk_1_15(MCARegion region, Data data) {
|
||||||
super(region, 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
|
@Override
|
||||||
|
@ -125,150 +22,4 @@ public class Chunk_1_15 extends MCAChunk {
|
||||||
return LegacyBiomes.idFor(biomes[biomeIntIndex]);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package de.bluecolored.bluemap.core.world.mca.data;
|
package de.bluecolored.bluemap.core.world.mca.data;
|
||||||
|
|
||||||
import de.bluecolored.bluemap.api.debug.DebugDump;
|
import de.bluecolored.bluemap.api.debug.DebugDump;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -28,6 +30,8 @@ public class LevelData {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
@DebugDump
|
@DebugDump
|
||||||
public static class Dimension {
|
public static class Dimension {
|
||||||
private String type = "minecraft:overworld";
|
private String type = "minecraft:overworld";
|
||||||
|
|
Loading…
Reference in New Issue