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