]> git.lizzy.rs Git - BoundingBoxOutlineReloaded.git/blob - src/main/java/com/irtimaled/bbor/client/interop/NBTStructureLoader.java
Setup for 1.16.3 Fabric
[BoundingBoxOutlineReloaded.git] / src / main / java / com / irtimaled / bbor / client / interop / NBTStructureLoader.java
1 package com.irtimaled.bbor.client.interop;
2
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.CompoundTag;
9 import net.minecraft.nbt.ListTag;
10 import net.minecraft.structure.StructureManager;
11 import net.minecraft.structure.StructurePiece;
12 import net.minecraft.structure.StructureStart;
13 import net.minecraft.util.math.BlockBox;
14 import net.minecraft.util.math.BlockPos;
15 import net.minecraft.util.math.ChunkPos;
16 import net.minecraft.util.registry.DynamicRegistryManager;
17 import net.minecraft.world.FeatureUpdater;
18 import net.minecraft.world.PersistentStateManager;
19 import net.minecraft.world.StructureWorldAccess;
20 import net.minecraft.world.World;
21 import net.minecraft.world.biome.Biome;
22 import net.minecraft.world.dimension.DimensionType;
23 import net.minecraft.world.gen.StructureAccessor;
24 import net.minecraft.world.gen.chunk.ChunkGenerator;
25 import net.minecraft.world.gen.feature.FeatureConfig;
26 import net.minecraft.world.level.storage.LevelStorage;
27 import net.minecraft.world.storage.RegionBasedStorage;
28
29 import java.io.File;
30 import java.io.IOException;
31 import java.util.*;
32 import java.util.function.BiFunction;
33
34 class NBTStructureLoader {
35     private final DimensionId dimensionId;
36     private final Set<String> loadedChunks = new HashSet<>();
37
38     private FeatureUpdater legacyStructureDataUtil = null;
39     private LevelStorage.Session saveHandler = null;
40     private File chunkSaveLocation = null;
41     private ChunkLoader chunkLoader;
42
43     NBTStructureLoader(DimensionId dimensionId, LevelStorage.Session saveHandler, File worldDirectory) {
44         this.dimensionId = dimensionId;
45         this.configure(saveHandler, worldDirectory);
46     }
47
48     void clear() {
49         this.legacyStructureDataUtil = null;
50         this.chunkSaveLocation = null;
51         this.loadedChunks.clear();
52         close(this.saveHandler, this.chunkLoader);
53         this.saveHandler = null;
54         this.chunkLoader = null;
55     }
56
57     private void close(AutoCloseable... closeables) {
58         for (AutoCloseable closeable : closeables) {
59             if(closeable == null) continue;
60             try {
61                 closeable.close();
62             } catch (Exception ignored) {
63             }
64         }
65     }
66
67     void configure(LevelStorage.Session saveHandler, File worldDirectory) {
68         this.saveHandler = saveHandler;
69         if (worldDirectory != null) {
70             this.chunkSaveLocation = new File(DimensionType.getSaveDirectory(this.dimensionId.getDimensionType(), worldDirectory), "region");
71             this.chunkLoader = new ChunkLoader(this.chunkSaveLocation);
72         }
73     }
74
75     private FeatureUpdater getLegacyStructureDataUtil() {
76         if (this.legacyStructureDataUtil == null) {
77             File dataFolder = new File(this.saveHandler.getWorldDirectory(World.OVERWORLD), "data");
78             this.legacyStructureDataUtil = FeatureUpdater.create(dimensionId.getDimensionType(),
79                     new PersistentStateManager(dataFolder, MinecraftClient.getInstance().getDataFixer()));
80         }
81         return this.legacyStructureDataUtil;
82     }
83
84     private CompoundTag loadStructureStarts(int chunkX, int chunkZ) {
85         try {
86             CompoundTag compound = this.chunkLoader.readChunk(chunkX, chunkZ);
87             if (compound == null) return null;
88             int dataVersion = compound.contains("DataVersion", 99) ? compound.getInt("DataVersion") : -1;
89             if (dataVersion < 1493) {
90                 if (compound.getCompound("Level").getBoolean("hasLegacyStructureData")) {
91                     compound = getLegacyStructureDataUtil().getUpdatedReferences(compound);
92                 }
93             }
94             return compound.getCompound("Level").getCompound("Structures").getCompound("Starts");
95         } catch (IOException ignored) {
96         }
97         return null;
98     }
99
100     void loadStructures(int chunkX, int chunkZ) {
101         if (saveHandler == null) return;
102
103         if (!loadedChunks.add(String.format("%s,%s", chunkX, chunkZ))) return;
104
105         CompoundTag structureStarts = loadStructureStarts(chunkX, chunkZ);
106         if (structureStarts == null || structureStarts.getSize() == 0) return;
107
108         Map<String, StructureStart<?>> structureStartMap = new HashMap<>();
109         for (String key : structureStarts.getKeys()) {
110             CompoundTag compound = structureStarts.getCompound(key);
111             if (compound.contains("BB")) {
112                 structureStartMap.put(key, new SimpleStructureStart(compound));
113             }
114         }
115
116         EventBus.publish(new StructuresLoaded(structureStartMap, dimensionId));
117     }
118
119     private static class SimpleStructureStart extends StructureStart<FeatureConfig> {
120         SimpleStructureStart(CompoundTag compound) {
121             super(null,
122                     0,
123                     0,
124                     new BlockBox(compound.getIntArray("BB")),
125                     0,
126                     0);
127
128             ListTag children = compound.getList("Children", 10);
129             for (int index = 0; index < children.size(); ++index) {
130                 CompoundTag child = children.getCompound(index);
131                 if (child.contains("BB")) this.children.add(new SimpleStructurePiece(child));
132             }
133         }
134
135         @Override
136         public void init(DynamicRegistryManager dynamicRegistryManager, ChunkGenerator chunkGenerator, StructureManager structureManager, int i, int j, Biome biome, FeatureConfig featureConfig) {
137         }
138     }
139
140     private static class SimpleStructurePiece extends StructurePiece {
141         SimpleStructurePiece(CompoundTag compound) {
142             super(null, compound);
143         }
144
145         @Override
146         protected void toNbt(CompoundTag compoundTag) {
147
148         }
149
150         @Override
151         public boolean generate(StructureWorldAccess structureWorldAccess, StructureAccessor structureAccessor, ChunkGenerator chunkGenerator, Random random, BlockBox blockBox, ChunkPos chunkPos, BlockPos blockPos) {
152             return false;
153         }
154     }
155
156     private static class ChunkLoader implements AutoCloseable {
157         private static final BiFunction<File, Boolean, RegionBasedStorage> creator =
158                 ReflectionHelper.getPrivateInstanceBuilder(RegionBasedStorage.class, File.class, boolean.class);
159
160         private final RegionBasedStorage regionFileCache;
161
162         public ChunkLoader(File file) {
163             this.regionFileCache = creator.apply(file, false);
164         }
165
166         public CompoundTag readChunk(int chunkX, int chunkZ) throws IOException {
167             if (regionFileCache == null) return null;
168             return regionFileCache.getTagAt(new ChunkPos(chunkX, chunkZ));
169         }
170
171         public void close() throws IOException {
172             if (regionFileCache == null) return;
173             regionFileCache.close();
174         }
175     }
176 }