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.client.MinecraftClient;
8 import net.minecraft.nbt.NbtCompound;
9 import net.minecraft.nbt.NbtList;
10 import net.minecraft.server.world.ServerWorld;
11 import net.minecraft.structure.StructureManager;
12 import net.minecraft.structure.StructurePiece;
13 import net.minecraft.structure.StructureStart;
14 import net.minecraft.util.math.BlockBox;
15 import net.minecraft.util.math.BlockPos;
16 import net.minecraft.util.math.ChunkPos;
17 import net.minecraft.util.registry.DynamicRegistryManager;
18 import net.minecraft.world.*;
19 import net.minecraft.world.biome.Biome;
20 import net.minecraft.world.dimension.DimensionType;
21 import net.minecraft.world.gen.StructureAccessor;
22 import net.minecraft.world.gen.chunk.ChunkGenerator;
23 import net.minecraft.world.gen.feature.FeatureConfig;
24 import net.minecraft.world.level.storage.LevelStorage;
25 import net.minecraft.world.storage.RegionBasedStorage;
28 import java.io.IOException;
30 import java.util.function.BiFunction;
32 class NBTStructureLoader {
33 private final DimensionId dimensionId;
34 private final Set<String> loadedChunks = new HashSet<>();
36 private FeatureUpdater legacyStructureDataUtil = null;
37 private LevelStorage.Session saveHandler = null;
38 private File chunkSaveLocation = null;
39 private ChunkLoader chunkLoader;
41 NBTStructureLoader(DimensionId dimensionId, LevelStorage.Session saveHandler, File worldDirectory) {
42 this.dimensionId = dimensionId;
43 this.configure(saveHandler, worldDirectory);
47 this.legacyStructureDataUtil = null;
48 this.chunkSaveLocation = null;
49 this.loadedChunks.clear();
50 close(this.saveHandler, this.chunkLoader);
51 this.saveHandler = null;
52 this.chunkLoader = null;
55 private void close(AutoCloseable... closeables) {
56 for (AutoCloseable closeable : closeables) {
57 if(closeable == null) continue;
60 } catch (Exception ignored) {
65 void configure(LevelStorage.Session saveHandler, File worldDirectory) {
66 this.saveHandler = saveHandler;
67 if (worldDirectory != null) {
68 this.chunkSaveLocation = new File(DimensionType.getSaveDirectory(this.dimensionId.getDimensionType(), worldDirectory), "region");
69 this.chunkLoader = new ChunkLoader(this.chunkSaveLocation);
73 private FeatureUpdater getLegacyStructureDataUtil() {
74 if (this.legacyStructureDataUtil == null) {
75 File dataFolder = new File(this.saveHandler.getWorldDirectory(World.OVERWORLD), "data");
76 this.legacyStructureDataUtil = FeatureUpdater.create(dimensionId.getDimensionType(),
77 new PersistentStateManager(dataFolder, MinecraftClient.getInstance().getDataFixer()));
79 return this.legacyStructureDataUtil;
82 private NbtCompound loadStructureStarts(int chunkX, int chunkZ) {
84 NbtCompound compound = this.chunkLoader.readChunk(chunkX, chunkZ);
85 if (compound == null) return null;
86 int dataVersion = compound.contains("DataVersion", 99) ? compound.getInt("DataVersion") : -1;
87 if (dataVersion < 1493) {
88 if (compound.getCompound("Level").getBoolean("hasLegacyStructureData")) {
89 compound = getLegacyStructureDataUtil().getUpdatedReferences(compound);
92 return compound.getCompound("Level").getCompound("Structures").getCompound("Starts");
93 } catch (IOException ignored) {
98 void loadStructures(int chunkX, int chunkZ) {
99 if (saveHandler == null) return;
101 if (!loadedChunks.add(String.format("%s,%s", chunkX, chunkZ))) return;
103 NbtCompound structureStarts = loadStructureStarts(chunkX, chunkZ);
104 if (structureStarts == null || structureStarts.getSize() == 0) return;
106 Map<String, StructureStart<?>> structureStartMap = new HashMap<>();
107 for (String key : structureStarts.getKeys()) {
108 NbtCompound compound = structureStarts.getCompound(key);
109 if (compound.contains("BB")) {
110 structureStartMap.put(key, new SimpleStructureStart(compound));
114 EventBus.publish(new StructuresLoaded(structureStartMap, dimensionId));
117 private static class SimpleStructureStart extends StructureStart<FeatureConfig> {
118 SimpleStructureStart(NbtCompound compound) {
123 NbtList children = compound.getList("Children", 10);
124 for (int index = 0; index < children.size(); ++index) {
125 NbtCompound child = children.getCompound(index);
126 if (child.contains("BB")) this.children.add(new SimpleStructurePiece(child));
131 public void init(DynamicRegistryManager registryManager, ChunkGenerator chunkGenerator, StructureManager manager, ChunkPos pos, Biome biome, FeatureConfig config, HeightLimitView world) {
136 private static class SimpleStructurePiece extends StructurePiece {
137 SimpleStructurePiece(NbtCompound compound) {
138 super(null, compound);
142 protected void writeNbt(ServerWorld world, NbtCompound nbt) {
147 public boolean generate(StructureWorldAccess structureWorldAccess, StructureAccessor structureAccessor, ChunkGenerator chunkGenerator, Random random, BlockBox blockBox, ChunkPos chunkPos, BlockPos blockPos) {
152 private static class ChunkLoader implements AutoCloseable {
153 private static final BiFunction<File, Boolean, RegionBasedStorage> creator =
154 ReflectionHelper.getPrivateInstanceBuilder(RegionBasedStorage.class, File.class, boolean.class);
156 private final RegionBasedStorage regionFileCache;
158 public ChunkLoader(File file) {
159 this.regionFileCache = creator.apply(file, false);
162 public NbtCompound readChunk(int chunkX, int chunkZ) throws IOException {
163 if (regionFileCache == null) return null;
164 return regionFileCache.getTagAt(new ChunkPos(chunkX, chunkZ));
167 public void close() throws IOException {
168 if (regionFileCache == null) return;
169 regionFileCache.close();