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