minecraft-src/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
2025-07-04 03:45:38 +03:00

617 lines
25 KiB
Java

package net.minecraft.world.level.chunk.storage;
import com.google.common.collect.Maps;
import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import it.unimi.dsi.fastutil.shorts.ShortList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Map.Entry;
import net.minecraft.Optionull;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.SectionPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.LongArrayTag;
import net.minecraft.nbt.NbtException;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.ShortTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.CarvingMask;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.DataLayer;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.PalettedContainerRO;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
import net.minecraft.world.level.levelgen.BelowZeroRetrogen;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.blending.BlendingData;
import net.minecraft.world.level.levelgen.blending.BlendingData.Packed;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.ticks.LevelChunkTicks;
import net.minecraft.world.ticks.ProtoChunkTicks;
import net.minecraft.world.ticks.SavedTick;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
public record SerializableChunkData(
Registry<Biome> biomeRegistry,
ChunkPos chunkPos,
int minSectionY,
long lastUpdateTime,
long inhabitedTime,
ChunkStatus chunkStatus,
@Nullable Packed blendingData,
@Nullable BelowZeroRetrogen belowZeroRetrogen,
UpgradeData upgradeData,
@Nullable long[] carvingMask,
Map<Heightmap.Types, long[]> heightmaps,
ChunkAccess.PackedTicks packedTicks,
ShortList[] postProcessingSections,
boolean lightCorrect,
List<SerializableChunkData.SectionData> sectionData,
List<CompoundTag> entities,
List<CompoundTag> blockEntities,
CompoundTag structureData
) {
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(
Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState()
);
private static final Codec<List<SavedTick<Block>>> BLOCK_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.BLOCK.byNameCodec()).listOf();
private static final Codec<List<SavedTick<Fluid>>> FLUID_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.FLUID.byNameCodec()).listOf();
private static final Logger LOGGER = LogUtils.getLogger();
private static final String TAG_UPGRADE_DATA = "UpgradeData";
private static final String BLOCK_TICKS_TAG = "block_ticks";
private static final String FLUID_TICKS_TAG = "fluid_ticks";
public static final String X_POS_TAG = "xPos";
public static final String Z_POS_TAG = "zPos";
public static final String HEIGHTMAPS_TAG = "Heightmaps";
public static final String IS_LIGHT_ON_TAG = "isLightOn";
public static final String SECTIONS_TAG = "sections";
public static final String BLOCK_LIGHT_TAG = "BlockLight";
public static final String SKY_LIGHT_TAG = "SkyLight";
@Nullable
public static SerializableChunkData parse(LevelHeightAccessor levelHeightAccessor, RegistryAccess registries, CompoundTag tag) {
if (tag.getString("Status").isEmpty()) {
return null;
} else {
ChunkPos chunkPos = new ChunkPos(tag.getIntOr("xPos", 0), tag.getIntOr("zPos", 0));
long l = tag.getLongOr("LastUpdate", 0L);
long m = tag.getLongOr("InhabitedTime", 0L);
ChunkStatus chunkStatus = (ChunkStatus)tag.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY);
UpgradeData upgradeData = (UpgradeData)tag.getCompound("UpgradeData")
.map(compoundTagx -> new UpgradeData(compoundTagx, levelHeightAccessor))
.orElse(UpgradeData.EMPTY);
boolean bl = tag.getBooleanOr("isLightOn", false);
Packed packed = (Packed)tag.read("blending_data", Packed.CODEC).orElse(null);
BelowZeroRetrogen belowZeroRetrogen = (BelowZeroRetrogen)tag.read("below_zero_retrogen", BelowZeroRetrogen.CODEC).orElse(null);
long[] ls = (long[])tag.getLongArray("carving_mask").orElse(null);
Map<Heightmap.Types, long[]> map = new EnumMap(Heightmap.Types.class);
tag.getCompound("Heightmaps").ifPresent(compoundTagx -> {
for (Heightmap.Types types : chunkStatus.heightmapsAfter()) {
compoundTagx.getLongArray(types.getSerializationKey()).ifPresent(lsx -> map.put(types, lsx));
}
});
List<SavedTick<Block>> list = SavedTick.filterTickListForChunk(
(List<SavedTick<Block>>)tag.read("block_ticks", BLOCK_TICKS_CODEC).orElse(List.of()), chunkPos
);
List<SavedTick<Fluid>> list2 = SavedTick.filterTickListForChunk(
(List<SavedTick<Fluid>>)tag.read("fluid_ticks", FLUID_TICKS_CODEC).orElse(List.of()), chunkPos
);
ChunkAccess.PackedTicks packedTicks = new ChunkAccess.PackedTicks(list, list2);
ListTag listTag = tag.getListOrEmpty("PostProcessing");
ShortList[] shortLists = new ShortList[listTag.size()];
for (int i = 0; i < listTag.size(); i++) {
ListTag listTag2 = listTag.getListOrEmpty(i);
ShortList shortList = new ShortArrayList(listTag2.size());
for (int j = 0; j < listTag2.size(); j++) {
shortList.add(listTag2.getShortOr(j, (short)0));
}
shortLists[i] = shortList;
}
List<CompoundTag> list3 = tag.getList("entities").stream().flatMap(ListTag::compoundStream).toList();
List<CompoundTag> list4 = tag.getList("block_entities").stream().flatMap(ListTag::compoundStream).toList();
CompoundTag compoundTag = tag.getCompoundOrEmpty("structures");
ListTag listTag3 = tag.getListOrEmpty("sections");
List<SerializableChunkData.SectionData> list5 = new ArrayList(listTag3.size());
Registry<Biome> registry = registries.lookupOrThrow(Registries.BIOME);
Codec<PalettedContainerRO<Holder<Biome>>> codec = makeBiomeCodec(registry);
for (int k = 0; k < listTag3.size(); k++) {
Optional<CompoundTag> optional = listTag3.getCompound(k);
if (!optional.isEmpty()) {
CompoundTag compoundTag2 = (CompoundTag)optional.get();
int n = compoundTag2.getByteOr("Y", (byte)0);
LevelChunkSection levelChunkSection;
if (n >= levelHeightAccessor.getMinSectionY() && n <= levelHeightAccessor.getMaxSectionY()) {
PalettedContainer<BlockState> palettedContainer = (PalettedContainer<BlockState>)compoundTag2.getCompound("block_states")
.map(
compoundTagx -> BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, compoundTagx)
.promotePartial(string -> logErrors(chunkPos, n, string))
.getOrThrow(SerializableChunkData.ChunkReadException::new)
)
.orElseGet(() -> new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES));
PalettedContainerRO<Holder<Biome>> palettedContainerRO = (PalettedContainerRO<Holder<Biome>>)compoundTag2.getCompound("biomes")
.map(
compoundTagx -> codec.parse(NbtOps.INSTANCE, compoundTagx)
.promotePartial(string -> logErrors(chunkPos, n, string))
.getOrThrow(SerializableChunkData.ChunkReadException::new)
)
.orElseGet(() -> new PalettedContainer<>(registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES));
levelChunkSection = new LevelChunkSection(palettedContainer, palettedContainerRO);
} else {
levelChunkSection = null;
}
DataLayer dataLayer = (DataLayer)compoundTag2.getByteArray("BlockLight").map(DataLayer::new).orElse(null);
DataLayer dataLayer2 = (DataLayer)compoundTag2.getByteArray("SkyLight").map(DataLayer::new).orElse(null);
list5.add(new SerializableChunkData.SectionData(n, levelChunkSection, dataLayer, dataLayer2));
}
}
return new SerializableChunkData(
registry,
chunkPos,
levelHeightAccessor.getMinSectionY(),
l,
m,
chunkStatus,
packed,
belowZeroRetrogen,
upgradeData,
ls,
map,
packedTicks,
shortLists,
bl,
list5,
list3,
list4,
compoundTag
);
}
}
public ProtoChunk read(ServerLevel level, PoiManager poiManager, RegionStorageInfo regionStorageInfo, ChunkPos pos) {
if (!Objects.equals(pos, this.chunkPos)) {
LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", pos, pos, this.chunkPos);
level.getServer().reportMisplacedChunk(this.chunkPos, pos, regionStorageInfo);
}
int i = level.getSectionsCount();
LevelChunkSection[] levelChunkSections = new LevelChunkSection[i];
boolean bl = level.dimensionType().hasSkyLight();
ChunkSource chunkSource = level.getChunkSource();
LevelLightEngine levelLightEngine = chunkSource.getLightEngine();
Registry<Biome> registry = level.registryAccess().lookupOrThrow(Registries.BIOME);
boolean bl2 = false;
for (SerializableChunkData.SectionData sectionData : this.sectionData) {
SectionPos sectionPos = SectionPos.of(pos, sectionData.y);
if (sectionData.chunkSection != null) {
levelChunkSections[level.getSectionIndexFromSectionY(sectionData.y)] = sectionData.chunkSection;
poiManager.checkConsistencyWithBlocks(sectionPos, sectionData.chunkSection);
}
boolean bl3 = sectionData.blockLight != null;
boolean bl4 = bl && sectionData.skyLight != null;
if (bl3 || bl4) {
if (!bl2) {
levelLightEngine.retainData(pos, true);
bl2 = true;
}
if (bl3) {
levelLightEngine.queueSectionData(LightLayer.BLOCK, sectionPos, sectionData.blockLight);
}
if (bl4) {
levelLightEngine.queueSectionData(LightLayer.SKY, sectionPos, sectionData.skyLight);
}
}
}
ChunkType chunkType = this.chunkStatus.getChunkType();
ChunkAccess chunkAccess;
if (chunkType == ChunkType.LEVELCHUNK) {
LevelChunkTicks<Block> levelChunkTicks = new LevelChunkTicks<>(this.packedTicks.blocks());
LevelChunkTicks<Fluid> levelChunkTicks2 = new LevelChunkTicks<>(this.packedTicks.fluids());
chunkAccess = new LevelChunk(
level.getLevel(),
pos,
this.upgradeData,
levelChunkTicks,
levelChunkTicks2,
this.inhabitedTime,
levelChunkSections,
postLoadChunk(level, this.entities, this.blockEntities),
BlendingData.unpack(this.blendingData)
);
} else {
ProtoChunkTicks<Block> protoChunkTicks = ProtoChunkTicks.load(this.packedTicks.blocks());
ProtoChunkTicks<Fluid> protoChunkTicks2 = ProtoChunkTicks.load(this.packedTicks.fluids());
ProtoChunk protoChunk = new ProtoChunk(
pos, this.upgradeData, levelChunkSections, protoChunkTicks, protoChunkTicks2, level, registry, BlendingData.unpack(this.blendingData)
);
chunkAccess = protoChunk;
protoChunk.setInhabitedTime(this.inhabitedTime);
if (this.belowZeroRetrogen != null) {
protoChunk.setBelowZeroRetrogen(this.belowZeroRetrogen);
}
protoChunk.setPersistedStatus(this.chunkStatus);
if (this.chunkStatus.isOrAfter(ChunkStatus.INITIALIZE_LIGHT)) {
protoChunk.setLightEngine(levelLightEngine);
}
}
chunkAccess.setLightCorrect(this.lightCorrect);
EnumSet<Heightmap.Types> enumSet = EnumSet.noneOf(Heightmap.Types.class);
for (Heightmap.Types types : chunkAccess.getPersistedStatus().heightmapsAfter()) {
long[] ls = (long[])this.heightmaps.get(types);
if (ls != null) {
chunkAccess.setHeightmap(types, ls);
} else {
enumSet.add(types);
}
}
Heightmap.primeHeightmaps(chunkAccess, enumSet);
chunkAccess.setAllStarts(unpackStructureStart(StructurePieceSerializationContext.fromLevel(level), this.structureData, level.getSeed()));
chunkAccess.setAllReferences(unpackStructureReferences(level.registryAccess(), pos, this.structureData));
for (int j = 0; j < this.postProcessingSections.length; j++) {
chunkAccess.addPackedPostProcess(this.postProcessingSections[j], j);
}
if (chunkType == ChunkType.LEVELCHUNK) {
return new ImposterProtoChunk((LevelChunk)chunkAccess, false);
} else {
ProtoChunk protoChunk2 = (ProtoChunk)chunkAccess;
for (CompoundTag compoundTag : this.entities) {
protoChunk2.addEntity(compoundTag);
}
for (CompoundTag compoundTag : this.blockEntities) {
protoChunk2.setBlockEntityNbt(compoundTag);
}
if (this.carvingMask != null) {
protoChunk2.setCarvingMask(new CarvingMask(this.carvingMask, chunkAccess.getMinY()));
}
return protoChunk2;
}
}
private static void logErrors(ChunkPos chunkPos, int sectionY, String error) {
LOGGER.error("Recoverable errors when loading section [{}, {}, {}]: {}", chunkPos.x, sectionY, chunkPos.z, error);
}
private static Codec<PalettedContainerRO<Holder<Biome>>> makeBiomeCodec(Registry<Biome> biomeRegistry) {
return PalettedContainer.codecRO(
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS)
);
}
public static SerializableChunkData copyOf(ServerLevel level, ChunkAccess chunk) {
if (!chunk.canBeSerialized()) {
throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
} else {
ChunkPos chunkPos = chunk.getPos();
List<SerializableChunkData.SectionData> list = new ArrayList();
LevelChunkSection[] levelChunkSections = chunk.getSections();
LevelLightEngine levelLightEngine = level.getChunkSource().getLightEngine();
for (int i = levelLightEngine.getMinLightSection(); i < levelLightEngine.getMaxLightSection(); i++) {
int j = chunk.getSectionIndexFromSectionY(i);
boolean bl = j >= 0 && j < levelChunkSections.length;
DataLayer dataLayer = levelLightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunkPos, i));
DataLayer dataLayer2 = levelLightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunkPos, i));
DataLayer dataLayer3 = dataLayer != null && !dataLayer.isEmpty() ? dataLayer.copy() : null;
DataLayer dataLayer4 = dataLayer2 != null && !dataLayer2.isEmpty() ? dataLayer2.copy() : null;
if (bl || dataLayer3 != null || dataLayer4 != null) {
LevelChunkSection levelChunkSection = bl ? levelChunkSections[j].copy() : null;
list.add(new SerializableChunkData.SectionData(i, levelChunkSection, dataLayer3, dataLayer4));
}
}
List<CompoundTag> list2 = new ArrayList(chunk.getBlockEntitiesPos().size());
for (BlockPos blockPos : chunk.getBlockEntitiesPos()) {
CompoundTag compoundTag = chunk.getBlockEntityNbtForSaving(blockPos, level.registryAccess());
if (compoundTag != null) {
list2.add(compoundTag);
}
}
List<CompoundTag> list3 = new ArrayList();
long[] ls = null;
if (chunk.getPersistedStatus().getChunkType() == ChunkType.PROTOCHUNK) {
ProtoChunk protoChunk = (ProtoChunk)chunk;
list3.addAll(protoChunk.getEntities());
CarvingMask carvingMask = protoChunk.getCarvingMask();
if (carvingMask != null) {
ls = carvingMask.toArray();
}
}
Map<Heightmap.Types, long[]> map = new EnumMap(Heightmap.Types.class);
for (Entry<Heightmap.Types, Heightmap> entry : chunk.getHeightmaps()) {
if (chunk.getPersistedStatus().heightmapsAfter().contains(entry.getKey())) {
long[] ms = ((Heightmap)entry.getValue()).getRawData();
map.put((Heightmap.Types)entry.getKey(), (long[])ms.clone());
}
}
ChunkAccess.PackedTicks packedTicks = chunk.getTicksForSerialization(level.getGameTime());
ShortList[] shortLists = (ShortList[])Arrays.stream(chunk.getPostProcessing())
.map(shortList -> shortList != null ? new ShortArrayList(shortList) : null)
.toArray(ShortList[]::new);
CompoundTag compoundTag2 = packStructureData(StructurePieceSerializationContext.fromLevel(level), chunkPos, chunk.getAllStarts(), chunk.getAllReferences());
return new SerializableChunkData(
level.registryAccess().lookupOrThrow(Registries.BIOME),
chunkPos,
chunk.getMinSectionY(),
level.getGameTime(),
chunk.getInhabitedTime(),
chunk.getPersistedStatus(),
Optionull.map(chunk.getBlendingData(), BlendingData::pack),
chunk.getBelowZeroRetrogen(),
chunk.getUpgradeData().copy(),
ls,
map,
packedTicks,
shortLists,
chunk.isLightCorrect(),
list,
list3,
list2,
compoundTag2
);
}
}
public CompoundTag write() {
CompoundTag compoundTag = NbtUtils.addCurrentDataVersion(new CompoundTag());
compoundTag.putInt("xPos", this.chunkPos.x);
compoundTag.putInt("yPos", this.minSectionY);
compoundTag.putInt("zPos", this.chunkPos.z);
compoundTag.putLong("LastUpdate", this.lastUpdateTime);
compoundTag.putLong("InhabitedTime", this.inhabitedTime);
compoundTag.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
compoundTag.storeNullable("blending_data", Packed.CODEC, this.blendingData);
compoundTag.storeNullable("below_zero_retrogen", BelowZeroRetrogen.CODEC, this.belowZeroRetrogen);
if (!this.upgradeData.isEmpty()) {
compoundTag.put("UpgradeData", this.upgradeData.write());
}
ListTag listTag = new ListTag();
Codec<PalettedContainerRO<Holder<Biome>>> codec = makeBiomeCodec(this.biomeRegistry);
for (SerializableChunkData.SectionData sectionData : this.sectionData) {
CompoundTag compoundTag2 = new CompoundTag();
LevelChunkSection levelChunkSection = sectionData.chunkSection;
if (levelChunkSection != null) {
compoundTag2.store("block_states", BLOCK_STATE_CODEC, levelChunkSection.getStates());
compoundTag2.store("biomes", codec, levelChunkSection.getBiomes());
}
if (sectionData.blockLight != null) {
compoundTag2.putByteArray("BlockLight", sectionData.blockLight.getData());
}
if (sectionData.skyLight != null) {
compoundTag2.putByteArray("SkyLight", sectionData.skyLight.getData());
}
if (!compoundTag2.isEmpty()) {
compoundTag2.putByte("Y", (byte)sectionData.y);
listTag.add(compoundTag2);
}
}
compoundTag.put("sections", listTag);
if (this.lightCorrect) {
compoundTag.putBoolean("isLightOn", true);
}
ListTag listTag2 = new ListTag();
listTag2.addAll(this.blockEntities);
compoundTag.put("block_entities", listTag2);
if (this.chunkStatus.getChunkType() == ChunkType.PROTOCHUNK) {
ListTag listTag3 = new ListTag();
listTag3.addAll(this.entities);
compoundTag.put("entities", listTag3);
if (this.carvingMask != null) {
compoundTag.putLongArray("carving_mask", this.carvingMask);
}
}
saveTicks(compoundTag, this.packedTicks);
compoundTag.put("PostProcessing", packOffsets(this.postProcessingSections));
CompoundTag compoundTag3 = new CompoundTag();
this.heightmaps.forEach((types, ls) -> compoundTag3.put(types.getSerializationKey(), new LongArrayTag(ls)));
compoundTag.put("Heightmaps", compoundTag3);
compoundTag.put("structures", this.structureData);
return compoundTag;
}
private static void saveTicks(CompoundTag tag, ChunkAccess.PackedTicks ticks) {
tag.store("block_ticks", BLOCK_TICKS_CODEC, ticks.blocks());
tag.store("fluid_ticks", FLUID_TICKS_CODEC, ticks.fluids());
}
public static ChunkStatus getChunkStatusFromTag(@Nullable CompoundTag tag) {
return tag != null ? (ChunkStatus)tag.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY) : ChunkStatus.EMPTY;
}
@Nullable
private static LevelChunk.PostLoadProcessor postLoadChunk(ServerLevel level, List<CompoundTag> entities, List<CompoundTag> blockEntities) {
return entities.isEmpty() && blockEntities.isEmpty() ? null : levelChunk -> {
if (!entities.isEmpty()) {
level.addLegacyChunkEntities(EntityType.loadEntitiesRecursive(entities, level, EntitySpawnReason.LOAD));
}
for (CompoundTag compoundTag : blockEntities) {
boolean bl = compoundTag.getBooleanOr("keepPacked", false);
if (bl) {
levelChunk.setBlockEntityNbt(compoundTag);
} else {
BlockPos blockPos = BlockEntity.getPosFromTag(levelChunk.getPos(), compoundTag);
BlockEntity blockEntity = BlockEntity.loadStatic(blockPos, levelChunk.getBlockState(blockPos), compoundTag, level.registryAccess());
if (blockEntity != null) {
levelChunk.setBlockEntity(blockEntity);
}
}
}
};
}
private static CompoundTag packStructureData(
StructurePieceSerializationContext context, ChunkPos pos, Map<Structure, StructureStart> structureStarts, Map<Structure, LongSet> references
) {
CompoundTag compoundTag = new CompoundTag();
CompoundTag compoundTag2 = new CompoundTag();
Registry<Structure> registry = context.registryAccess().lookupOrThrow(Registries.STRUCTURE);
for (Entry<Structure, StructureStart> entry : structureStarts.entrySet()) {
ResourceLocation resourceLocation = registry.getKey((Structure)entry.getKey());
compoundTag2.put(resourceLocation.toString(), ((StructureStart)entry.getValue()).createTag(context, pos));
}
compoundTag.put("starts", compoundTag2);
CompoundTag compoundTag3 = new CompoundTag();
for (Entry<Structure, LongSet> entry2 : references.entrySet()) {
if (!((LongSet)entry2.getValue()).isEmpty()) {
ResourceLocation resourceLocation2 = registry.getKey((Structure)entry2.getKey());
compoundTag3.putLongArray(resourceLocation2.toString(), ((LongSet)entry2.getValue()).toLongArray());
}
}
compoundTag.put("References", compoundTag3);
return compoundTag;
}
private static Map<Structure, StructureStart> unpackStructureStart(StructurePieceSerializationContext context, CompoundTag tag, long seed) {
Map<Structure, StructureStart> map = Maps.<Structure, StructureStart>newHashMap();
Registry<Structure> registry = context.registryAccess().lookupOrThrow(Registries.STRUCTURE);
CompoundTag compoundTag = tag.getCompoundOrEmpty("starts");
for (String string : compoundTag.keySet()) {
ResourceLocation resourceLocation = ResourceLocation.tryParse(string);
Structure structure = registry.getValue(resourceLocation);
if (structure == null) {
LOGGER.error("Unknown structure start: {}", resourceLocation);
} else {
StructureStart structureStart = StructureStart.loadStaticStart(context, compoundTag.getCompoundOrEmpty(string), seed);
if (structureStart != null) {
map.put(structure, structureStart);
}
}
}
return map;
}
private static Map<Structure, LongSet> unpackStructureReferences(RegistryAccess registries, ChunkPos pos, CompoundTag tag) {
Map<Structure, LongSet> map = Maps.<Structure, LongSet>newHashMap();
Registry<Structure> registry = registries.lookupOrThrow(Registries.STRUCTURE);
CompoundTag compoundTag = tag.getCompoundOrEmpty("References");
compoundTag.forEach((string, tagx) -> {
ResourceLocation resourceLocation = ResourceLocation.tryParse(string);
Structure structure = registry.getValue(resourceLocation);
if (structure == null) {
LOGGER.warn("Found reference to unknown structure '{}' in chunk {}, discarding", resourceLocation, pos);
} else {
Optional<long[]> optional = tagx.asLongArray();
if (!optional.isEmpty()) {
map.put(structure, new LongOpenHashSet(Arrays.stream((long[])optional.get()).filter(l -> {
ChunkPos chunkPos2 = new ChunkPos(l);
if (chunkPos2.getChessboardDistance(pos) > 8) {
LOGGER.warn("Found invalid structure reference [ {} @ {} ] for chunk {}.", resourceLocation, chunkPos2, pos);
return false;
} else {
return true;
}
}).toArray()));
}
}
});
return map;
}
private static ListTag packOffsets(ShortList[] offsets) {
ListTag listTag = new ListTag();
for (ShortList shortList : offsets) {
ListTag listTag2 = new ListTag();
if (shortList != null) {
for (int i = 0; i < shortList.size(); i++) {
listTag2.add(ShortTag.valueOf(shortList.getShort(i)));
}
}
listTag.add(listTag2);
}
return listTag;
}
public static class ChunkReadException extends NbtException {
public ChunkReadException(String string) {
super(string);
}
}
public record SectionData(int y, @Nullable LevelChunkSection chunkSection, @Nullable DataLayer blockLight, @Nullable DataLayer skyLight) {
}
}