package com.irtimaled.bbor.client.interop; import com.irtimaled.bbor.common.EventBus; import com.irtimaled.bbor.common.events.StructuresLoaded; import com.irtimaled.bbor.common.models.DimensionId; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.structure.StructureManager; import net.minecraft.structure.StructurePiece; import net.minecraft.structure.StructureStart; import net.minecraft.util.math.BlockBox; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.FeatureUpdater; import net.minecraft.world.IWorld; import net.minecraft.world.PersistentStateManager; import net.minecraft.world.WorldSaveHandler; import net.minecraft.world.biome.Biome; import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.gen.chunk.ChunkGenerator; import net.minecraft.world.storage.RegionBasedStorage; import java.io.File; import java.io.IOException; import java.util.*; class NBTStructureLoader { private final DimensionId dimensionId; private final Set loadedChunks = new HashSet<>(); private FeatureUpdater legacyStructureDataUtil = null; private WorldSaveHandler saveHandler = null; private File chunkSaveLocation = null; private ChunkLoader chunkLoader; NBTStructureLoader(DimensionId dimensionId, WorldSaveHandler saveHandler, File worldDirectory) { this.dimensionId = dimensionId; this.configure(saveHandler, worldDirectory); } void clear() { this.legacyStructureDataUtil = null; this.saveHandler = null; this.chunkSaveLocation = null; this.loadedChunks.clear(); if (this.chunkLoader == null) return; try { this.chunkLoader.close(); } catch (IOException ignored) { } this.chunkLoader = null; } void configure(WorldSaveHandler saveHandler, File worldDirectory) { this.saveHandler = saveHandler; if (worldDirectory != null) { this.chunkSaveLocation = new File(dimensionId.getDimensionType().getSaveDirectory(worldDirectory), "region"); this.chunkLoader = new ChunkLoader(this.chunkSaveLocation); } } private FeatureUpdater getLegacyStructureDataUtil() { if (this.legacyStructureDataUtil == null) { File dataFolder = new File(DimensionType.OVERWORLD.getSaveDirectory(this.saveHandler.getWorldDir()), "data"); this.legacyStructureDataUtil = FeatureUpdater.create(dimensionId.getDimensionType(), new PersistentStateManager(dataFolder, this.saveHandler.getDataFixer())); } return this.legacyStructureDataUtil; } private CompoundTag loadStructureStarts(int chunkX, int chunkZ) { try { CompoundTag compound = this.chunkLoader.readChunk(chunkX, chunkZ); if (compound == null) return null; int dataVersion = compound.contains("DataVersion", 99) ? compound.getInt("DataVersion") : -1; if (dataVersion < 1493) { if (compound.getCompound("Level").getBoolean("hasLegacyStructureData")) { compound = getLegacyStructureDataUtil().getUpdatedReferences(compound); } } return compound.getCompound("Level").getCompound("Structures").getCompound("Starts"); } catch (IOException ignored) { } return null; } void loadStructures(int chunkX, int chunkZ) { if (saveHandler == null) return; if (!loadedChunks.add(String.format("%s,%s", chunkX, chunkZ))) return; CompoundTag structureStarts = loadStructureStarts(chunkX, chunkZ); if (structureStarts == null || structureStarts.getSize() == 0) return; Map structureStartMap = new HashMap<>(); for (String key : structureStarts.getKeys()) { CompoundTag compound = structureStarts.getCompound(key); if (compound.contains("BB")) { structureStartMap.put(key, new SimpleStructureStart(compound)); } } EventBus.publish(new StructuresLoaded(structureStartMap, dimensionId)); } private static class SimpleStructureStart extends StructureStart { SimpleStructureStart(CompoundTag compound) { super(null, 0, 0, null, new BlockBox(compound.getIntArray("BB")), 0, 0); ListTag children = compound.getList("Children", 10); for (int index = 0; index < children.size(); ++index) { CompoundTag child = children.getCompound(index); if (child.contains("BB")) this.children.add(new SimpleStructurePiece(child)); } } @Override public void initialize(ChunkGenerator chunkGenerator, StructureManager structureManager, int i, int i1, Biome biome) { } } private static class SimpleStructurePiece extends StructurePiece { SimpleStructurePiece(CompoundTag compound) { super(null, compound); } @Override protected void toNbt(CompoundTag compoundTag) { } @Override public boolean generate(IWorld iWorld, Random random, BlockBox blockBox, ChunkPos chunkPos) { return false; } } private static class ChunkLoader { private final RegionBasedStorage regionFileCache; public ChunkLoader(File file) { this.regionFileCache = new RegionBasedStorage(file) { }; } public CompoundTag readChunk(int chunkX, int chunkZ) throws IOException { return regionFileCache.getTagAt(new ChunkPos(chunkX, chunkZ)); } public void close() throws IOException { regionFileCache.close(); } } }