mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2025-01-23 00:31:23 +01:00
Add support for reading 1.16 region-files
This commit is contained in:
parent
54364c40ee
commit
67cb42fa40
@ -39,9 +39,13 @@ public abstract class Chunk {
|
||||
private final MCAWorld world;
|
||||
private final Vector2i chunkPos;
|
||||
|
||||
private final int dataVersion;
|
||||
|
||||
protected Chunk(MCAWorld world, Vector2i chunkPos) {
|
||||
this.world = world;
|
||||
this.chunkPos = chunkPos;
|
||||
|
||||
this.dataVersion = -1;
|
||||
}
|
||||
|
||||
protected Chunk(MCAWorld world, CompoundTag chunkTag) {
|
||||
@ -53,6 +57,8 @@ protected Chunk(MCAWorld world, CompoundTag chunkTag) {
|
||||
levelData.getInt("xPos"),
|
||||
levelData.getInt("zPos")
|
||||
);
|
||||
|
||||
dataVersion = chunkTag.getInt("DataVersion");
|
||||
}
|
||||
|
||||
public abstract boolean isGenerated();
|
||||
@ -65,6 +71,10 @@ public MCAWorld getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
public int getDataVersion() {
|
||||
return dataVersion;
|
||||
}
|
||||
|
||||
public abstract BlockState getBlockState(Vector3i pos);
|
||||
|
||||
public abstract LightData getLightData(Vector3i pos);
|
||||
@ -76,7 +86,8 @@ public static Chunk create(MCAWorld world, CompoundTag chunkTag, boolean ignoreM
|
||||
|
||||
if (version <= 1343) return new ChunkAnvil112(world, chunkTag, ignoreMissingLightData);
|
||||
if (version <= 1976) return new ChunkAnvil113(world, chunkTag, ignoreMissingLightData);
|
||||
return new ChunkAnvil115(world, chunkTag, ignoreMissingLightData);
|
||||
if (version < 2534) return new ChunkAnvil115(world, chunkTag, ignoreMissingLightData);
|
||||
return new ChunkAnvil116(world, chunkTag, ignoreMissingLightData);
|
||||
}
|
||||
|
||||
public static Chunk empty(MCAWorld world, Vector2i chunkPos) {
|
||||
|
@ -139,6 +139,8 @@ private class Section {
|
||||
private long[] blocks;
|
||||
private BlockState[] palette;
|
||||
|
||||
private int bitsPerBlock;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Section(CompoundTag sectionData) {
|
||||
this.sectionY = sectionData.getByte("Y");
|
||||
@ -175,6 +177,8 @@ public Section(CompoundTag sectionData) {
|
||||
} else {
|
||||
this.palette = new BlockState[0];
|
||||
}
|
||||
|
||||
this.bitsPerBlock = blocks.length * 64 / 4096; //64 bits per long and 4096 blocks per section
|
||||
}
|
||||
|
||||
public int getSectionY() {
|
||||
@ -188,7 +192,6 @@ public BlockState getBlockState(Vector3i pos) {
|
||||
int y = pos.getY() & 0xF;
|
||||
int z = pos.getZ() & 0xF;
|
||||
int blockIndex = y * 256 + z * 16 + x;
|
||||
int bitsPerBlock = blocks.length * 64 / 4096; //64 bits per long and 4096 blocks per section
|
||||
int index = blockIndex * bitsPerBlock;
|
||||
int firstLong = index >> 6; // index / 64
|
||||
int bitoffset = index & 0x3F; // Math.floorMod(index, 64)
|
||||
|
@ -0,0 +1,242 @@
|
||||
/*
|
||||
* This file is part of BlueMap, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.mca;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.flowpowered.math.vector.Vector3i;
|
||||
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
import de.bluecolored.bluemap.core.mca.mapping.BiomeMapper;
|
||||
import de.bluecolored.bluemap.core.world.Biome;
|
||||
import de.bluecolored.bluemap.core.world.BlockState;
|
||||
import de.bluecolored.bluemap.core.world.LightData;
|
||||
import net.querz.nbt.ByteArrayTag;
|
||||
import net.querz.nbt.CompoundTag;
|
||||
import net.querz.nbt.IntArrayTag;
|
||||
import net.querz.nbt.ListTag;
|
||||
import net.querz.nbt.StringTag;
|
||||
import net.querz.nbt.Tag;
|
||||
import net.querz.nbt.mca.MCAUtil;
|
||||
|
||||
public class ChunkAnvil116 extends Chunk {
|
||||
private BiomeMapper biomeIdMapper;
|
||||
|
||||
private boolean isGenerated;
|
||||
private boolean hasLight;
|
||||
private Section[] sections;
|
||||
private int[] biomes;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ChunkAnvil116(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) {
|
||||
super(world, chunkTag);
|
||||
|
||||
biomeIdMapper = getWorld().getBiomeIdMapper();
|
||||
|
||||
CompoundTag levelData = chunkTag.getCompoundTag("Level");
|
||||
|
||||
String status = levelData.getString("Status");
|
||||
isGenerated = status.equals("full") || status.equals("spawn"); // full is normal fully generated and spawn seems to be converted from old format but not yet loaded if you optimized your world
|
||||
hasLight = isGenerated;
|
||||
|
||||
if (!isGenerated && ignoreMissingLightData) {
|
||||
isGenerated = !status.equals("empty");
|
||||
}
|
||||
|
||||
sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe?
|
||||
if (levelData.containsKey("Sections")) {
|
||||
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
|
||||
Section section = new Section(sectionTag);
|
||||
if (section.getSectionY() >= 0) sections[section.getSectionY()] = section;
|
||||
}
|
||||
}
|
||||
|
||||
Tag<?> tag = levelData.get("Biomes"); //tag can be byte-array or int-array
|
||||
if (tag instanceof ByteArrayTag) {
|
||||
byte[] bs = ((ByteArrayTag) tag).getValue();
|
||||
biomes = new int[bs.length];
|
||||
|
||||
for (int i = 0; i < bs.length; i++) {
|
||||
biomes[i] = bs[i] & 0xFF;
|
||||
}
|
||||
}
|
||||
else if (tag instanceof IntArrayTag) {
|
||||
biomes = ((IntArrayTag) tag).getValue();
|
||||
}
|
||||
|
||||
if (biomes == null || biomes.length == 0) {
|
||||
biomes = new int[2048];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenerated() {
|
||||
return isGenerated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(Vector3i pos) {
|
||||
int sectionY = MCAUtil.blockToChunk(pos.getY());
|
||||
|
||||
Section section = this.sections[sectionY];
|
||||
if (section == null) return BlockState.AIR;
|
||||
|
||||
return section.getBlockState(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LightData getLightData(Vector3i pos) {
|
||||
if (!hasLight) return LightData.SKY;
|
||||
|
||||
int sectionY = MCAUtil.blockToChunk(pos.getY());
|
||||
|
||||
Section section = this.sections[sectionY];
|
||||
if (section == null) return LightData.SKY;
|
||||
|
||||
return section.getLightData(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome getBiome(Vector3i pos) {
|
||||
int x = (pos.getX() & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
|
||||
int z = (pos.getZ() & 0xF) / 4;
|
||||
int y = pos.getY() / 4;
|
||||
int biomeIntIndex = y * 16 + z * 4 + x;
|
||||
|
||||
return biomeIdMapper.get(biomes[biomeIntIndex]);
|
||||
}
|
||||
|
||||
private class Section {
|
||||
private static final String AIR_ID = "minecraft:air";
|
||||
|
||||
private int sectionY;
|
||||
private byte[] blockLight;
|
||||
private byte[] skyLight;
|
||||
private long[] blocks;
|
||||
private BlockState[] palette;
|
||||
|
||||
private int bitsPerBlock;
|
||||
private int blocksPerLong;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Section(CompoundTag sectionData) {
|
||||
this.sectionY = sectionData.getByte("Y");
|
||||
this.blockLight = sectionData.getByteArray("BlockLight");
|
||||
if (blockLight.length == 0) blockLight = new byte[2048];
|
||||
this.skyLight = sectionData.getByteArray("SkyLight");
|
||||
if (skyLight.length == 0) skyLight = new byte[2048];
|
||||
this.blocks = sectionData.getLongArray("BlockStates");
|
||||
|
||||
//read block palette
|
||||
ListTag<CompoundTag> paletteTag = (ListTag<CompoundTag>) sectionData.getListTag("Palette");
|
||||
if (paletteTag != null) {
|
||||
this.palette = new BlockState[paletteTag.size()];
|
||||
for (int i = 0; i < this.palette.length; i++) {
|
||||
CompoundTag stateTag = paletteTag.get(i);
|
||||
|
||||
String id = stateTag.getString("Name"); //shortcut to save time and memory
|
||||
if (id.equals(AIR_ID)) {
|
||||
palette[i] = BlockState.AIR;
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
|
||||
if (stateTag.containsKey("Properties")) {
|
||||
CompoundTag propertiesTag = stateTag.getCompoundTag("Properties");
|
||||
for (Entry<String, Tag<?>> property : propertiesTag) {
|
||||
properties.put(property.getKey().toLowerCase(), ((StringTag) property.getValue()).getValue().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
palette[i] = new BlockState(id, properties);
|
||||
}
|
||||
} else {
|
||||
this.palette = new BlockState[0];
|
||||
}
|
||||
|
||||
|
||||
this.bitsPerBlock = 32 - Integer.numberOfLeadingZeros(palette.length - 1);
|
||||
if (this.bitsPerBlock < 4) this.bitsPerBlock = 4;
|
||||
this.blocksPerLong = 64 / bitsPerBlock;
|
||||
}
|
||||
|
||||
public int getSectionY() {
|
||||
return sectionY;
|
||||
}
|
||||
|
||||
public BlockState getBlockState(Vector3i pos) {
|
||||
if (blocks.length == 0) return BlockState.AIR;
|
||||
|
||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||
int y = pos.getY() & 0xF;
|
||||
int z = pos.getZ() & 0xF;
|
||||
int blockIndex = y * 256 + z * 16 + x;
|
||||
int longIndex = blockIndex / blocksPerLong;
|
||||
int bitIndex = (blockIndex % blocksPerLong) * bitsPerBlock;
|
||||
|
||||
long value = blocks[longIndex] >>> bitIndex;
|
||||
|
||||
value = value & (0xFFFFFFFFFFFFFFFFL >>> -bitsPerBlock);
|
||||
|
||||
if (value >= palette.length) {
|
||||
Logger.global.noFloodWarning("palettewarning", "Got palette value " + value + " but palette has size of " + palette.length + "! (Future occasions of this error will not be logged)");
|
||||
return BlockState.MISSING;
|
||||
}
|
||||
|
||||
return palette[(int) value];
|
||||
}
|
||||
|
||||
public LightData getLightData(Vector3i pos) {
|
||||
int x = pos.getX() & 0xF; // Math.floorMod(pos.getX(), 16)
|
||||
int y = pos.getY() & 0xF;
|
||||
int z = pos.getZ() & 0xF;
|
||||
int blockByteIndex = y * 256 + z * 16 + x;
|
||||
int blockHalfByteIndex = blockByteIndex >> 1; // blockByteIndex / 2
|
||||
boolean largeHalf = (blockByteIndex & 0x1) != 0; // (blockByteIndex % 2) == 0
|
||||
|
||||
int blockLight = getByteHalf(this.blockLight[blockHalfByteIndex], largeHalf);
|
||||
int skyLight = getByteHalf(this.skyLight[blockHalfByteIndex], largeHalf);
|
||||
|
||||
return new LightData(skyLight, blockLight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the 4 bits of the left (largeHalf = <code>true</code>) or the right (largeHalf = <code>false</code>) side of the byte stored in <code>value</code>.<br>
|
||||
* The value is treated as an unsigned byte.
|
||||
*/
|
||||
private int getByteHalf(int value, boolean largeHalf) {
|
||||
value = value & 0xFF;
|
||||
if (largeHalf) {
|
||||
value = value >> 4;
|
||||
}
|
||||
value = value & 0xF;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user