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 biomeRegistry, ChunkPos chunkPos, int minSectionY, long lastUpdateTime, long inhabitedTime, ChunkStatus chunkStatus, @Nullable Packed blendingData, @Nullable BelowZeroRetrogen belowZeroRetrogen, UpgradeData upgradeData, @Nullable long[] carvingMask, Map heightmaps, ChunkAccess.PackedTicks packedTicks, ShortList[] postProcessingSections, boolean lightCorrect, List sectionData, List entities, List blockEntities, CompoundTag structureData ) { private static final Codec> BLOCK_STATE_CODEC = PalettedContainer.codecRW( Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState() ); private static final Codec>> BLOCK_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.BLOCK.byNameCodec()).listOf(); private static final Codec>> 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 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> list = SavedTick.filterTickListForChunk( (List>)tag.read("block_ticks", BLOCK_TICKS_CODEC).orElse(List.of()), chunkPos ); List> list2 = SavedTick.filterTickListForChunk( (List>)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 list3 = tag.getList("entities").stream().flatMap(ListTag::compoundStream).toList(); List list4 = tag.getList("block_entities").stream().flatMap(ListTag::compoundStream).toList(); CompoundTag compoundTag = tag.getCompoundOrEmpty("structures"); ListTag listTag3 = tag.getListOrEmpty("sections"); List list5 = new ArrayList(listTag3.size()); Registry registry = registries.lookupOrThrow(Registries.BIOME); Codec>> codec = makeBiomeCodec(registry); for (int k = 0; k < listTag3.size(); k++) { Optional 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 palettedContainer = (PalettedContainer)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> palettedContainerRO = (PalettedContainerRO>)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 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 levelChunkTicks = new LevelChunkTicks<>(this.packedTicks.blocks()); LevelChunkTicks 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 protoChunkTicks = ProtoChunkTicks.load(this.packedTicks.blocks()); ProtoChunkTicks 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 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>> makeBiomeCodec(Registry 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 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 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 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 map = new EnumMap(Heightmap.Types.class); for (Entry 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>> 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 entities, List 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 structureStarts, Map references ) { CompoundTag compoundTag = new CompoundTag(); CompoundTag compoundTag2 = new CompoundTag(); Registry registry = context.registryAccess().lookupOrThrow(Registries.STRUCTURE); for (Entry 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 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 unpackStructureStart(StructurePieceSerializationContext context, CompoundTag tag, long seed) { Map map = Maps.newHashMap(); Registry 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 unpackStructureReferences(RegistryAccess registries, ChunkPos pos, CompoundTag tag) { Map map = Maps.newHashMap(); Registry 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 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) { } }