3232import aztech .modern_industrialization .machines .models .MachineCasings ;
3333import aztech .modern_industrialization .machines .multiblocks .ShapeTemplate ;
3434import aztech .modern_industrialization .machines .multiblocks .structure .member .StructureMember ;
35- import java .io .FileInputStream ;
36- import java .io .FileOutputStream ;
3735import java .io .IOException ;
3836import java .io .InputStream ;
3937import java .io .OutputStream ;
38+ import java .nio .file .DirectoryStream ;
4039import java .nio .file .Files ;
4140import java .nio .file .Path ;
4241import java .util .ArrayList ;
42+ import java .util .HashMap ;
4343import java .util .List ;
44+ import java .util .Map ;
4445import java .util .Objects ;
46+ import java .util .function .BiConsumer ;
4547import net .minecraft .FileUtil ;
4648import net .minecraft .core .BlockPos ;
4749import net .minecraft .core .Direction ;
5860import net .minecraft .world .level .block .entity .BlockEntity ;
5961import net .minecraft .world .level .block .state .BlockState ;
6062import net .minecraft .world .level .levelgen .structure .BoundingBox ;
63+ import net .neoforged .fml .ModList ;
6164import net .neoforged .fml .loading .FMLPaths ;
65+ import net .neoforged .neoforgespi .language .IModFileInfo ;
6266import org .jetbrains .annotations .Nullable ;
6367
6468public final class MIStructureTemplateManager {
69+ private static Map <ResourceLocation , ShapeTemplate > STRUCTURE_TEMPLATES ;
70+
71+ private static void assertLoaded () {
72+ if (STRUCTURE_TEMPLATES == null ) {
73+ throw new IllegalStateException ("Structure templates have not yet been loaded" );
74+ }
75+ }
76+
77+ public static boolean exists (ResourceLocation id ) {
78+ assertLoaded ();
79+ return STRUCTURE_TEMPLATES .containsKey (id );
80+ }
81+
82+ public static ShapeTemplate get (ResourceLocation id ) {
83+ assertLoaded ();
84+ ShapeTemplate template = STRUCTURE_TEMPLATES .get (id );
85+ if (template != null ) {
86+ return template ;
87+ } else {
88+ throw new IllegalArgumentException ("Structure shape template \" " + id .toString () + "\" does not exist." );
89+ }
90+ }
91+
6592 public static StructureResult fromWorld (ResourceLocation id , Level level ,
6693 BlockPos controllerPos , Direction controllerDirection ,
6794 MachineCasing hatchCasing , StructureControllerBounds bounds ) {
@@ -160,7 +187,7 @@ public static StructureResult fromWorld(ResourceLocation id, Level level,
160187 }
161188
162189 @ Nullable
163- public static ShapeTemplate deserialize (CompoundTag tag ) {
190+ private static ShapeTemplate deserialize (CompoundTag tag ) {
164191 Objects .requireNonNull (tag );
165192
166193 ResourceLocation hatchCasingId = ResourceLocation .tryParse (tag .getString ("hatch_casing" ));
@@ -189,12 +216,15 @@ public static ShapeTemplate deserialize(CompoundTag tag) {
189216 return builder .build ();
190217 }
191218
219+ private static Path structuresPath () {
220+ return FMLPaths .GAMEDIR .get ()
221+ .resolve (MI .ID )
222+ .resolve ("structures" );
223+ }
224+
192225 private static Path path (ResourceLocation id ) throws IOException {
193226 Objects .requireNonNull (id );
194- var miFolder = FMLPaths .GAMEDIR .get ().resolve (MI .ID );
195- var structuresFolder = miFolder
196- .resolve ("structures" )
197- .resolve (id .getNamespace ());
227+ var structuresFolder = structuresPath ().resolve (id .getNamespace ());
198228 Files .createDirectories (structuresFolder );
199229 return FileUtil .createPathToResource (structuresFolder , id .getPath (), ".nbt" );
200230 }
@@ -203,39 +233,97 @@ public static boolean save(ResourceLocation id, CompoundTag tag) {
203233 Objects .requireNonNull (id );
204234 Objects .requireNonNull (tag );
205235 try {
206- try (OutputStream output = new FileOutputStream (path (id ). toFile ( ))) {
236+ try (OutputStream output = Files . newOutputStream (path (id ))) {
207237 NbtIo .writeCompressed (tag , output );
208238 return true ;
209239 } catch (Exception ex ) {
210- MI .LOGGER .error ("Failed to save structure '{}' " , id , ex );
240+ MI .LOGGER .error ("Failed to save structure \" {} \" " , id , ex );
211241 }
212242 } catch (Exception ex ) {
213- MI .LOGGER .error ("Failed to save structure '{}' " , id , ex );
243+ MI .LOGGER .error ("Failed to save structure \" {} \" " , id , ex );
214244 }
215245 return false ;
216246 }
217247
218248 @ Nullable
219- public static CompoundTag load (ResourceLocation id ) {
220- Objects .requireNonNull (id );
249+ private static CompoundTag load (Path path ) {
250+ Objects .requireNonNull (path );
221251 try {
222- Path path = path (id );
223252 if (Files .exists (path )) {
224- try (InputStream input = new FileInputStream ( path . toFile () );
253+ try (InputStream input = Files . newInputStream ( path );
225254 InputStream fastInput = new FastBufferedInputStream (input )) {
226255 return NbtIo .readCompressed (fastInput , NbtAccounter .unlimitedHeap ());
227256 } catch (Exception ex ) {
228- MI .LOGGER .error ("Failed to load structure '{}'" , id , ex );
257+ MI .LOGGER .error ("Failed to load structure at \" {} \" " , path , ex );
229258 return null ;
230259 }
231260 }
261+ MI .LOGGER .error ("Could not find structure at \" {}\" " , path );
232262 return null ;
233263 } catch (Exception ex ) {
234- MI .LOGGER .error ("Failed to load structure '{}'" , id , ex );
264+ MI .LOGGER .error ("Failed to load structure at \" {} \" " , path , ex );
235265 return null ;
236266 }
237267 }
238268
269+ private static void iterateStructureFiles (Path origin , BiConsumer <ResourceLocation , Path > action ) throws IOException {
270+ try (DirectoryStream <Path > subdirectories = Files .newDirectoryStream (origin , Files ::isDirectory )) {
271+ for (Path subdirectory : subdirectories ) {
272+ String namespace = subdirectory .getFileName ().toString ();
273+ try (DirectoryStream <Path > files = Files .newDirectoryStream (subdirectory )) {
274+ for (Path file : files ) {
275+ if (file .toString ().endsWith (".nbt" )) {
276+ String rawFileName = file .getFileName ().toString ();
277+ String path = rawFileName .substring (0 , rawFileName .lastIndexOf ('.' ));
278+ ResourceLocation id = ResourceLocation .fromNamespaceAndPath (namespace , path );
279+ action .accept (id , file );
280+ }
281+ }
282+ }
283+ }
284+ }
285+ }
286+
287+ private static void register (ResourceLocation id , Path path ) {
288+ CompoundTag structureTag = load (path );
289+ if (structureTag != null ) {
290+ ShapeTemplate structure = deserialize (structureTag );
291+ if (structure != null ) {
292+ STRUCTURE_TEMPLATES .put (id , structure );
293+ } else {
294+ MI .LOGGER .error ("Failed to load structure with id \" {}\" " , id );
295+ }
296+ }
297+ }
298+
299+ public static void init () {
300+ if (STRUCTURE_TEMPLATES != null ) {
301+ throw new IllegalStateException ("Structures have already been loaded" );
302+ }
303+
304+ STRUCTURE_TEMPLATES = new HashMap <>();
305+
306+ try {
307+ var structuresPath = structuresPath ();
308+ Files .createDirectories (structuresPath );
309+ iterateStructureFiles (structuresPath , MIStructureTemplateManager ::register );
310+
311+ for (IModFileInfo modFile : ModList .get ().getModFiles ()) {
312+ Path modStructuresPath = modFile .getFile ().findResource ("mi_structures" );
313+ iterateStructureFiles (modStructuresPath , (id , path ) -> {
314+ if (STRUCTURE_TEMPLATES .containsKey (id )) {
315+ return ;
316+ }
317+ register (id , path );
318+ });
319+ }
320+
321+ MI .LOGGER .info ("Loaded {} structures." , STRUCTURE_TEMPLATES .size ());
322+ } catch (IOException ex ) {
323+ throw new RuntimeException (ex );
324+ }
325+ }
326+
239327 private MIStructureTemplateManager () {
240328 }
241329}
0 commit comments