1 package com.irtimaled.bbor.client.interop;
3 import com.irtimaled.bbor.common.EventBus;
4 import com.irtimaled.bbor.common.ReflectionHelper;
5 import com.irtimaled.bbor.common.events.StructuresLoaded;
6 import com.irtimaled.bbor.common.models.DimensionId;
7 import net.minecraft.nbt.CompoundTag;
8 import net.minecraft.nbt.ListTag;
9 import net.minecraft.structure.StructureManager;
10 import net.minecraft.structure.StructurePiece;
11 import net.minecraft.structure.StructureStart;
12 import net.minecraft.util.math.BlockBox;
13 import net.minecraft.util.math.ChunkPos;
14 import net.minecraft.world.FeatureUpdater;
15 import net.minecraft.world.IWorld;
16 import net.minecraft.world.PersistentStateManager;
17 import net.minecraft.world.WorldSaveHandler;
18 import net.minecraft.world.biome.Biome;
19 import net.minecraft.world.dimension.DimensionType;
20 import net.minecraft.world.gen.chunk.ChunkGenerator;
21 import net.minecraft.world.storage.RegionBasedStorage;
24 import java.io.IOException;
26 import java.util.function.Function;
28 class NBTStructureLoader {
29 private final DimensionId dimensionId;
30 private final Set<String> loadedChunks = new HashSet<>();
32 private FeatureUpdater legacyStructureDataUtil = null;
33 private WorldSaveHandler saveHandler = null;
34 private File chunkSaveLocation = null;
35 private ChunkLoader chunkLoader;
37 NBTStructureLoader(DimensionId dimensionId, WorldSaveHandler saveHandler, File worldDirectory) {
38 this.dimensionId = dimensionId;
39 this.configure(saveHandler, worldDirectory);
43 this.legacyStructureDataUtil = null;
44 this.saveHandler = null;
45 this.chunkSaveLocation = null;
46 this.loadedChunks.clear();
48 if (this.chunkLoader == null) return;
50 this.chunkLoader.close();
51 } catch (IOException ignored) {
53 this.chunkLoader = null;
56 void configure(WorldSaveHandler saveHandler, File worldDirectory) {
57 this.saveHandler = saveHandler;
58 if (worldDirectory != null) {
59 this.chunkSaveLocation = new File(dimensionId.getDimensionType().getSaveDirectory(worldDirectory), "region");
60 this.chunkLoader = new ChunkLoader(this.chunkSaveLocation);
64 private FeatureUpdater getLegacyStructureDataUtil() {
65 if (this.legacyStructureDataUtil == null) {
66 File dataFolder = new File(DimensionType.OVERWORLD.getSaveDirectory(this.saveHandler.getWorldDir()), "data");
67 this.legacyStructureDataUtil = FeatureUpdater.create(dimensionId.getDimensionType(),
68 new PersistentStateManager(dataFolder, this.saveHandler.getDataFixer()));
70 return this.legacyStructureDataUtil;
73 private CompoundTag loadStructureStarts(int chunkX, int chunkZ) {
75 CompoundTag compound = this.chunkLoader.readChunk(chunkX, chunkZ);
76 if (compound == null) return null;
77 int dataVersion = compound.contains("DataVersion", 99) ? compound.getInt("DataVersion") : -1;
78 if (dataVersion < 1493) {
79 if (compound.getCompound("Level").getBoolean("hasLegacyStructureData")) {
80 compound = getLegacyStructureDataUtil().getUpdatedReferences(compound);
83 return compound.getCompound("Level").getCompound("Structures").getCompound("Starts");
84 } catch (IOException ignored) {
89 void loadStructures(int chunkX, int chunkZ) {
90 if (saveHandler == null) return;
92 if (!loadedChunks.add(String.format("%s,%s", chunkX, chunkZ))) return;
94 CompoundTag structureStarts = loadStructureStarts(chunkX, chunkZ);
95 if (structureStarts == null || structureStarts.getSize() == 0) return;
97 Map<String, StructureStart> structureStartMap = new HashMap<>();
98 for (String key : structureStarts.getKeys()) {
99 CompoundTag compound = structureStarts.getCompound(key);
100 if (compound.contains("BB")) {
101 structureStartMap.put(key, new SimpleStructureStart(compound));
105 EventBus.publish(new StructuresLoaded(structureStartMap, dimensionId));
108 private static class SimpleStructureStart extends StructureStart {
109 SimpleStructureStart(CompoundTag compound) {
113 new BlockBox(compound.getIntArray("BB")),
117 ListTag children = compound.getList("Children", 10);
118 for (int index = 0; index < children.size(); ++index) {
119 CompoundTag child = children.getCompound(index);
120 if (child.contains("BB")) this.children.add(new SimpleStructurePiece(child));
125 public void initialize(ChunkGenerator<?> chunkGenerator, StructureManager structureManager, int i, int i1, Biome biome) {
130 private static class SimpleStructurePiece extends StructurePiece {
131 SimpleStructurePiece(CompoundTag compound) {
132 super(null, compound);
136 protected void toNbt(CompoundTag compoundTag) {
141 public boolean generate(IWorld iWorld, ChunkGenerator<?> chunkGenerator, Random random, BlockBox blockBox, ChunkPos chunkPos) {
146 private static class ChunkLoader {
147 private static final Function<File, RegionBasedStorage> creator =
148 ReflectionHelper.getPrivateInstanceBuilder(RegionBasedStorage.class, File.class);
150 private final RegionBasedStorage regionFileCache;
152 public ChunkLoader(File file) {
153 this.regionFileCache = creator.apply(file);
156 public CompoundTag readChunk(int chunkX, int chunkZ) throws IOException {
157 if (regionFileCache == null) return null;
158 return regionFileCache.getTagAt(new ChunkPos(chunkX, chunkZ));
161 public void close() throws IOException {
162 if (regionFileCache == null) return;
163 regionFileCache.close();