minecraft-source/src/net/minecraft/world/level/chunk/storage/ChunkSerializer.java

411 lines
19 KiB
Java

package net.minecraft.world.level.chunk.storage;
import java.util.AbstractList;
import org.apache.logging.log4j.LogManager;
import it.unimi.dsi.fastutil.shorts.ShortListIterator;
import net.minecraft.nbt.ShortTag;
import it.unimi.dsi.fastutil.shorts.ShortList;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.world.level.levelgen.structure.StructureFeatureIO;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.entity.EntityType;
import javax.annotation.Nullable;
import net.minecraft.nbt.LongArrayTag;
import java.util.Map;
import java.util.Collection;
import net.minecraft.world.entity.Entity;
import java.util.Arrays;
import net.minecraft.nbt.Tag;
import net.minecraft.SharedConstants;
import java.util.Iterator;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.TickList;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.nbt.ListTag;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.chunk.ChunkGenerator;
import java.util.BitSet;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import java.util.Set;
import java.util.EnumSet;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.ChunkTickList;
import net.minecraft.core.Registry;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.DataLayer;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.chunk.ProtoTickList;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
import java.util.Objects;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.Logger;
public class ChunkSerializer {
private static final Logger LOGGER;
public static ProtoChunk read(final ServerLevel xd, final StructureManager cml, final PoiManager aso, final ChunkPos bje, final CompoundTag jt) {
final ChunkGenerator<?> bzx6 = xd.getChunkSource().getGenerator();
final BiomeSource bkt7 = bzx6.getBiomeSource();
final CompoundTag jt2 = jt.getCompound("Level");
final ChunkPos bje2 = new ChunkPos(jt2.getInt("xPos"), jt2.getInt("zPos"));
if (!Objects.equals(bje, bje2)) {
ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", bje, bje, bje2);
}
final ChunkBiomeContainer bzw10 = new ChunkBiomeContainer(bje, bkt7, (int[])(jt2.contains("Biomes", 11) ? jt2.getIntArray("Biomes") : null));
final UpgradeData cas11 = jt2.contains("UpgradeData", 10) ? new UpgradeData(jt2.getCompound("UpgradeData")) : UpgradeData.EMPTY;
final ProtoTickList<Block> car12 = new ProtoTickList<Block>(bpe -> bpe == null || bpe.defaultBlockState().isAir(), bje, jt2.getList("ToBeTicked", 9));
final ProtoTickList<Fluid> car13 = new ProtoTickList<Fluid>(cof -> cof == null || cof == Fluids.EMPTY, bje, jt2.getList("LiquidsToBeTicked", 9));
final boolean boolean14 = jt2.getBoolean("isLightOn");
final ListTag jz15 = jt2.getList("Sections", 10);
final int integer16 = 16;
final LevelChunkSection[] arr17 = new LevelChunkSection[16];
final boolean boolean15 = xd.getDimension().isHasSkyLight();
final ChunkSource caa19 = xd.getChunkSource();
final LevelLightEngine cnx20 = caa19.getLightEngine();
if (boolean14) {
cnx20.retainData(bje, true);
}
for (int integer17 = 0; integer17 < jz15.size(); ++integer17) {
final CompoundTag jt3 = jz15.getCompound(integer17);
final int integer18 = jt3.getByte("Y");
if (jt3.contains("Palette", 9) && jt3.contains("BlockStates", 12)) {
final LevelChunkSection caj24 = new LevelChunkSection(integer18 << 4);
caj24.getStates().read(jt3.getList("Palette", 10), jt3.getLongArray("BlockStates"));
caj24.recalcBlockCounts();
if (!caj24.isEmpty()) {
arr17[integer18] = caj24;
}
aso.checkConsistencyWithBlocks(bje, caj24);
}
if (boolean14) {
if (jt3.contains("BlockLight", 7)) {
cnx20.queueSectionData(LightLayer.BLOCK, SectionPos.of(bje, integer18), new DataLayer(jt3.getByteArray("BlockLight")));
}
if (boolean15 && jt3.contains("SkyLight", 7)) {
cnx20.queueSectionData(LightLayer.SKY, SectionPos.of(bje, integer18), new DataLayer(jt3.getByteArray("SkyLight")));
}
}
}
final long long21 = jt2.getLong("InhabitedTime");
final ChunkStatus.ChunkType a23 = getChunkTypeFromTag(jt);
ChunkAccess bzv24;
if (a23 == ChunkStatus.ChunkType.LEVELCHUNK) {
TickList<Block> bki25;
if (jt2.contains("TileTicks", 9)) {
bki25 = ChunkTickList.create(jt2.getList("TileTicks", 10), Registry.BLOCK::getKey, Registry.BLOCK::get);
}
else {
bki25 = car12;
}
TickList<Fluid> bki26;
if (jt2.contains("LiquidTicks", 9)) {
bki26 = ChunkTickList.create(jt2.getList("LiquidTicks", 10), Registry.FLUID::getKey, Registry.FLUID::get);
}
else {
bki26 = car13;
}
bzv24 = new LevelChunk(xd.getLevel(), bje, bzw10, cas11, bki25, bki26, long21, arr17, cai -> postLoadChunk(jt2, cai));
}
else {
final ProtoChunk caq25 = new ProtoChunk(bje, cas11, arr17, car12, car13);
caq25.setBiomes(bzw10);
bzv24 = caq25;
bzv24.setInhabitedTime(long21);
caq25.setStatus(ChunkStatus.byName(jt2.getString("Status")));
if (bzv24.getStatus().isOrAfter(ChunkStatus.FEATURES)) {
caq25.setLightEngine(cnx20);
}
if (!boolean14 && bzv24.getStatus().isOrAfter(ChunkStatus.LIGHT)) {
for (final BlockPos fk27 : BlockPos.betweenClosed(bje.getMinBlockX(), 0, bje.getMinBlockZ(), bje.getMaxBlockX(), 255, bje.getMaxBlockZ())) {
if (bzv24.getBlockState(fk27).getLightEmission() != 0) {
caq25.addLight(fk27);
}
}
}
}
bzv24.setLightCorrect(boolean14);
final CompoundTag jt4 = jt2.getCompound("Heightmaps");
final EnumSet<Heightmap.Types> enumSet26 = EnumSet.<Heightmap.Types>noneOf(Heightmap.Types.class);
for (final Heightmap.Types a24 : bzv24.getStatus().heightmapsAfter()) {
final String string29 = a24.getSerializationKey();
if (jt4.contains(string29, 12)) {
bzv24.setHeightmap(a24, jt4.getLongArray(string29));
}
else {
enumSet26.add(a24);
}
}
Heightmap.primeHeightmaps(bzv24, enumSet26);
final CompoundTag jt5 = jt2.getCompound("Structures");
bzv24.setAllStarts(unpackStructureStart(bzx6, cml, jt5));
bzv24.setAllReferences(unpackStructureReferences(bje, jt5));
if (jt2.getBoolean("shouldSave")) {
bzv24.setUnsaved(true);
}
final ListTag jz16 = jt2.getList("PostProcessing", 9);
for (int integer19 = 0; integer19 < jz16.size(); ++integer19) {
final ListTag jz17 = jz16.getList(integer19);
for (int integer20 = 0; integer20 < jz17.size(); ++integer20) {
bzv24.addPackedPostProcess(jz17.getShort(integer20), integer19);
}
}
if (a23 == ChunkStatus.ChunkType.LEVELCHUNK) {
return new ImposterProtoChunk((LevelChunk)bzv24);
}
final ProtoChunk caq26 = (ProtoChunk)bzv24;
final ListTag jz17 = jt2.getList("Entities", 10);
for (int integer20 = 0; integer20 < jz17.size(); ++integer20) {
caq26.addEntity(jz17.getCompound(integer20));
}
final ListTag jz18 = jt2.getList("TileEntities", 10);
for (int integer21 = 0; integer21 < jz18.size(); ++integer21) {
final CompoundTag jt6 = jz18.getCompound(integer21);
bzv24.setBlockEntityNbt(jt6);
}
final ListTag jz19 = jt2.getList("Lights", 9);
for (int integer22 = 0; integer22 < jz19.size(); ++integer22) {
final ListTag jz20 = jz19.getList(integer22);
for (int integer23 = 0; integer23 < jz20.size(); ++integer23) {
caq26.addLight(jz20.getShort(integer23), integer22);
}
}
final CompoundTag jt6 = jt2.getCompound("CarvingMasks");
for (final String string30 : jt6.getAllKeys()) {
final GenerationStep.Carving a25 = GenerationStep.Carving.valueOf(string30);
caq26.setCarvingMask(a25, BitSet.valueOf(jt6.getByteArray(string30)));
}
return caq26;
}
public static CompoundTag write(final ServerLevel xd, final ChunkAccess bzv) {
final ChunkPos bje3 = bzv.getPos();
final CompoundTag jt4 = new CompoundTag();
final CompoundTag jt5 = new CompoundTag();
jt4.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
jt4.put("Level", jt5);
jt5.putInt("xPos", bje3.x);
jt5.putInt("zPos", bje3.z);
jt5.putLong("LastUpdate", xd.getGameTime());
jt5.putLong("InhabitedTime", bzv.getInhabitedTime());
jt5.putString("Status", bzv.getStatus().getName());
final UpgradeData cas6 = bzv.getUpgradeData();
if (!cas6.isEmpty()) {
jt5.put("UpgradeData", cas6.write());
}
final LevelChunkSection[] arr7 = bzv.getSections();
final ListTag jz8 = new ListTag();
final LevelLightEngine cnx9 = xd.getChunkSource().getLightEngine();
final boolean boolean10 = bzv.isLightCorrect();
for (int integer11 = -1; integer11 < 17; ++integer11) {
final int integer12 = integer11;
final int n;
final LevelChunkSection caj2 = Arrays.<LevelChunkSection>stream(arr7).filter(caj -> caj != null && caj.bottomBlockY() >> 4 == n).findFirst().orElse(LevelChunk.EMPTY_SECTION);
final DataLayer cac14 = cnx9.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(bje3, integer12));
final DataLayer cac15 = cnx9.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(bje3, integer12));
if (caj2 != LevelChunk.EMPTY_SECTION || cac14 != null || cac15 != null) {
final CompoundTag jt6 = new CompoundTag();
jt6.putByte("Y", (byte)(integer12 & 0xFF));
if (caj2 != LevelChunk.EMPTY_SECTION) {
caj2.getStates().write(jt6, "Palette", "BlockStates");
}
if (cac14 != null && !cac14.isEmpty()) {
jt6.putByteArray("BlockLight", cac14.getData());
}
if (cac15 != null && !cac15.isEmpty()) {
jt6.putByteArray("SkyLight", cac15.getData());
}
((AbstractList<CompoundTag>)jz8).add(jt6);
}
}
jt5.put("Sections", jz8);
if (boolean10) {
jt5.putBoolean("isLightOn", true);
}
final ChunkBiomeContainer bzw11 = bzv.getBiomes();
if (bzw11 != null) {
jt5.putIntArray("Biomes", bzw11.writeBiomes());
}
final ListTag jz9 = new ListTag();
for (final BlockPos fk14 : bzv.getBlockEntitiesPos()) {
final CompoundTag jt7 = bzv.getBlockEntityNbtForSaving(fk14);
if (jt7 != null) {
((AbstractList<CompoundTag>)jz9).add(jt7);
}
}
jt5.put("TileEntities", jz9);
final ListTag jz10 = new ListTag();
if (bzv.getStatus().getChunkType() == ChunkStatus.ChunkType.LEVELCHUNK) {
final LevelChunk cai14 = (LevelChunk)bzv;
cai14.setLastSaveHadEntities(false);
for (int integer13 = 0; integer13 < cai14.getEntitySections().length; ++integer13) {
for (final Entity akn17 : cai14.getEntitySections()[integer13]) {
final CompoundTag jt8 = new CompoundTag();
if (akn17.save(jt8)) {
cai14.setLastSaveHadEntities(true);
((AbstractList<CompoundTag>)jz10).add(jt8);
}
}
}
}
else {
final ProtoChunk caq14 = (ProtoChunk)bzv;
jz10.addAll(caq14.getEntities());
jt5.put("Lights", packOffsets(caq14.getPackedLights()));
final CompoundTag jt7 = new CompoundTag();
for (final GenerationStep.Carving a19 : GenerationStep.Carving.values()) {
jt7.putByteArray(a19.toString(), bzv.getCarvingMask(a19).toByteArray());
}
jt5.put("CarvingMasks", jt7);
}
jt5.put("Entities", jz10);
final TickList<Block> bki14 = bzv.getBlockTicks();
if (bki14 instanceof ProtoTickList) {
jt5.put("ToBeTicked", ((ProtoTickList)bki14).save());
}
else if (bki14 instanceof ChunkTickList) {
jt5.put("TileTicks", ((ChunkTickList)bki14).save(xd.getGameTime()));
}
else {
jt5.put("TileTicks", xd.getBlockTicks().save(bje3));
}
final TickList<Fluid> bki15 = bzv.getLiquidTicks();
if (bki15 instanceof ProtoTickList) {
jt5.put("LiquidsToBeTicked", ((ProtoTickList)bki15).save());
}
else if (bki15 instanceof ChunkTickList) {
jt5.put("LiquidTicks", ((ChunkTickList)bki15).save(xd.getGameTime()));
}
else {
jt5.put("LiquidTicks", xd.getLiquidTicks().save(bje3));
}
jt5.put("PostProcessing", packOffsets(bzv.getPostProcessing()));
final CompoundTag jt6 = new CompoundTag();
for (final Map.Entry<Heightmap.Types, Heightmap> entry18 : bzv.getHeightmaps()) {
if (bzv.getStatus().heightmapsAfter().contains(entry18.getKey())) {
jt6.put(entry18.getKey().getSerializationKey(), new LongArrayTag(entry18.getValue().getRawData()));
}
}
jt5.put("Heightmaps", jt6);
jt5.put("Structures", packStructureData(bje3, bzv.getAllStarts(), bzv.getAllReferences()));
return jt4;
}
public static ChunkStatus.ChunkType getChunkTypeFromTag(@Nullable final CompoundTag jt) {
if (jt != null) {
final ChunkStatus cab2 = ChunkStatus.byName(jt.getCompound("Level").getString("Status"));
if (cab2 != null) {
return cab2.getChunkType();
}
}
return ChunkStatus.ChunkType.PROTOCHUNK;
}
private static void postLoadChunk(final CompoundTag jt, final LevelChunk cai) {
final ListTag jz3 = jt.getList("Entities", 10);
final Level bjt4 = cai.getLevel();
for (int integer5 = 0; integer5 < jz3.size(); ++integer5) {
final CompoundTag jt2 = jz3.getCompound(integer5);
EntityType.loadEntityRecursive(jt2, bjt4, akn -> {
cai.addEntity(akn);
return akn;
});
cai.setLastSaveHadEntities(true);
}
final ListTag jz4 = jt.getList("TileEntities", 10);
for (int integer6 = 0; integer6 < jz4.size(); ++integer6) {
final CompoundTag jt3 = jz4.getCompound(integer6);
final boolean boolean8 = jt3.getBoolean("keepPacked");
if (boolean8) {
cai.setBlockEntityNbt(jt3);
}
else {
final BlockEntity bwi9 = BlockEntity.loadStatic(jt3);
if (bwi9 != null) {
cai.addBlockEntity(bwi9);
}
}
}
}
private static CompoundTag packStructureData(final ChunkPos bje, final Map<String, StructureStart> map2, final Map<String, LongSet> map3) {
final CompoundTag jt4 = new CompoundTag();
final CompoundTag jt5 = new CompoundTag();
for (final Map.Entry<String, StructureStart> entry7 : map2.entrySet()) {
jt5.put(entry7.getKey(), entry7.getValue().createTag(bje.x, bje.z));
}
jt4.put("Starts", jt5);
final CompoundTag jt6 = new CompoundTag();
for (final Map.Entry<String, LongSet> entry8 : map3.entrySet()) {
jt6.put(entry8.getKey(), new LongArrayTag(entry8.getValue()));
}
jt4.put("References", jt6);
return jt4;
}
private static Map<String, StructureStart> unpackStructureStart(final ChunkGenerator<?> bzx, final StructureManager cml, final CompoundTag jt) {
final Map<String, StructureStart> map4 = Maps.newHashMap();
final CompoundTag jt2 = jt.getCompound("Starts");
for (final String string7 : jt2.getAllKeys()) {
map4.put(string7, StructureFeatureIO.loadStaticStart(bzx, cml, jt2.getCompound(string7)));
}
return map4;
}
private static Map<String, LongSet> unpackStructureReferences(final ChunkPos bje, final CompoundTag jt) {
final Map<String, LongSet> map3 = Maps.newHashMap();
final CompoundTag jt2 = jt.getCompound("References");
for (final String string6 : jt2.getAllKeys()) {
final ChunkPos bje2;
final Object o;
map3.put(string6, (LongSet)new LongOpenHashSet(Arrays.stream(jt2.getLongArray(string6)).filter(long3 -> {
bje2 = new ChunkPos(long3);
if (bje2.getChessboardDistance(bje) > 8) {
ChunkSerializer.LOGGER.warn("Found invalid structure reference [ {} @ {} ] for chunk {}.", o, bje2, bje);
return false;
}
else {
return true;
}
}).toArray()));
}
return map3;
}
public static ListTag packOffsets(final ShortList[] arr) {
final ListTag jz2 = new ListTag();
for (final ShortList shortList6 : arr) {
final ListTag jz3 = new ListTag();
if (shortList6 != null) {
for (final Short short9 : shortList6) {
((AbstractList<ShortTag>)jz3).add(ShortTag.valueOf(short9));
}
}
((AbstractList<ListTag>)jz2).add(jz3);
}
return jz2;
}
static {
LOGGER = LogManager.getLogger();
}
}