]> git.lizzy.rs Git - BoundingBoxOutlineReloaded.git/blob - src/main/java/com/irtimaled/bbor/client/interop/NBTStructureLoader.java
b0c76ec3582e7760650a5fc097fb5e9b17c03423
[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.*;
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;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.util.*;
30 import java.util.function.BiFunction;
31
32 class NBTStructureLoader {
33     private final DimensionId dimensionId;
34     private final Set<String> loadedChunks = new HashSet<>();
35
36     private FeatureUpdater legacyStructureDataUtil = null;
37     private LevelStorage.Session saveHandler = null;
38     private File chunkSaveLocation = null;
39     private ChunkLoader chunkLoader;
40
41     NBTStructureLoader(DimensionId dimensionId, LevelStorage.Session saveHandler, File worldDirectory) {
42         this.dimensionId = dimensionId;
43         this.configure(saveHandler, worldDirectory);
44     }
45
46     void clear() {
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;
53     }
54
55     private void close(AutoCloseable... closeables) {
56         for (AutoCloseable closeable : closeables) {
57             if(closeable == null) continue;
58             try {
59                 closeable.close();
60             } catch (Exception ignored) {
61             }
62         }
63     }
64
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);
70         }
71     }
72
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()));
78         }
79         return this.legacyStructureDataUtil;
80     }
81
82     private NbtCompound loadStructureStarts(int chunkX, int chunkZ) {
83         try {
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);
90                 }
91             }
92             return compound.getCompound("Level").getCompound("Structures").getCompound("Starts");
93         } catch (IOException ignored) {
94         }
95         return null;
96     }
97
98     void loadStructures(int chunkX, int chunkZ) {
99         if (saveHandler == null) return;
100
101         if (!loadedChunks.add(String.format("%s,%s", chunkX, chunkZ))) return;
102
103         NbtCompound structureStarts = loadStructureStarts(chunkX, chunkZ);
104         if (structureStarts == null || structureStarts.getSize() == 0) return;
105
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));
111             }
112         }
113
114         EventBus.publish(new StructuresLoaded(structureStartMap, dimensionId));
115     }
116
117     private static class SimpleStructureStart extends StructureStart<FeatureConfig> {
118         SimpleStructureStart(NbtCompound compound) {
119             super(null,
120                     null,
121                     0, 0);
122
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));
127             }
128         }
129
130         @Override
131         public void init(DynamicRegistryManager registryManager, ChunkGenerator chunkGenerator, StructureManager manager, ChunkPos pos, Biome biome, FeatureConfig config, HeightLimitView world) {
132
133         }
134     }
135
136     private static class SimpleStructurePiece extends StructurePiece {
137         SimpleStructurePiece(NbtCompound compound) {
138             super(null, compound);
139         }
140
141         @Override
142         protected void writeNbt(ServerWorld world, NbtCompound nbt) {
143
144         }
145
146         @Override
147         public boolean generate(StructureWorldAccess structureWorldAccess, StructureAccessor structureAccessor, ChunkGenerator chunkGenerator, Random random, BlockBox blockBox, ChunkPos chunkPos, BlockPos blockPos) {
148             return false;
149         }
150     }
151
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);
155
156         private final RegionBasedStorage regionFileCache;
157
158         public ChunkLoader(File file) {
159             this.regionFileCache = creator.apply(file, false);
160         }
161
162         public NbtCompound readChunk(int chunkX, int chunkZ) throws IOException {
163             if (regionFileCache == null) return null;
164             return regionFileCache.getTagAt(new ChunkPos(chunkX, chunkZ));
165         }
166
167         public void close() throws IOException {
168             if (regionFileCache == null) return;
169             regionFileCache.close();
170         }
171     }
172 }