From 8d1201924652baf28464995bc366deb91a8c35fe Mon Sep 17 00:00:00 2001 From: piexel Date: Tue, 20 Oct 2026 12:25:42 +0200 Subject: [PATCH] List procedural snippets in viewer Reads definitions from snippet folders. --- .../procedural/ProceduralObjectCatalog.java | 83 +++++++++++++++---- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/volmit/iris/engine/object/procedural/ProceduralObjectCatalog.java b/core/src/main/java/com/volmit/iris/engine/object/procedural/ProceduralObjectCatalog.java index 58bea97bf..cc97fdde8 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/procedural/ProceduralObjectCatalog.java +++ b/core/src/main/java/com/volmit/iris/engine/object/procedural/ProceduralObjectCatalog.java @@ -18,28 +18,42 @@ package com.volmit.iris.engine.object.procedural; import com.google.gson.Gson; +import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.engine.object.IrisObject; +import com.volmit.iris.engine.object.annotations.Snippet; +import com.volmit.iris.engine.object.procedural.coral.Coral; import com.volmit.iris.engine.object.procedural.coral.ResolvedCoral; +import com.volmit.iris.engine.object.procedural.crystal.Crystal; import com.volmit.iris.engine.object.procedural.crystal.ResolvedCrystal; +import com.volmit.iris.engine.object.procedural.formation.Formation; import com.volmit.iris.engine.object.procedural.formation.ResolvedFormation; +import com.volmit.iris.engine.object.procedural.fungi.Fungus; import com.volmit.iris.engine.object.procedural.fungi.ResolvedFungus; import com.volmit.iris.engine.object.procedural.ruin.ResolvedRuin; +import com.volmit.iris.engine.object.procedural.ruin.Ruin; import com.volmit.iris.engine.object.procedural.tree.ResolvedTree; +import com.volmit.iris.engine.object.procedural.tree.Tree; import com.volmit.iris.util.collection.KMap; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import java.util.function.Function; +import java.util.stream.Stream; /** - * The procedural objects of one pack. A definition several biomes share, such as one snippet, is baked once, and every - * variant can be found again by its key. + * The procedural objects of one pack: those its regions and biomes declare, and those saved on their own as snippets, + * such as {@code snippet/procedural-tree/oak.json}. A definition several places share is baked once, and every variant + * can be found again by its key. */ public final class ProceduralObjectCatalog { private static final Gson DEFINITION_JSON = new Gson(); + private static final List> DEFINITION_TYPES = List.of(Tree.class, Ruin.class, Formation.class, Coral.class, Fungus.class, Crystal.class); private final IrisData data; private final KMap byDefinition = new KMap<>(); @@ -52,20 +66,27 @@ public final class ProceduralObjectCatalog { /** The placements {@code settings} declare, shared with every other declaration of the same definitions. */ public List resolve(ProceduralObjectSettings settings) { List all = new ArrayList<>(); - addAll(all, settings.trees(), tree -> new ResolvedTree(tree, data)); - addAll(all, settings.ruins(), ruin -> new ResolvedRuin(ruin, data)); - addAll(all, settings.formations(), formation -> new ResolvedFormation(formation, data)); - addAll(all, settings.coral(), coral -> new ResolvedCoral(coral, data)); - addAll(all, settings.fungi(), fungus -> new ResolvedFungus(fungus, data)); - addAll(all, settings.crystals(), crystal -> new ResolvedCrystal(crystal, data)); + Stream.of(settings.trees(), settings.ruins(), settings.formations(), settings.coral(), settings.fungi(), settings.crystals()) + .flatMap(List::stream) + .forEach(definition -> all.add(placementOf(definition))); return List.copyOf(all); } - private void addAll(List all, List definitions, Function resolve) { - for (S definition : definitions) { - String identity = definition.getClass().getName() + DEFINITION_JSON.toJson(definition); - all.add(byDefinition.computeIfAbsent(identity, ignored -> register(resolve.apply(definition)))); - } + private ResolvedProceduralPlacement placementOf(Object definition) { + String identity = definition.getClass().getName() + DEFINITION_JSON.toJson(definition); + return byDefinition.computeIfAbsent(identity, ignored -> register(resolvedFor(definition))); + } + + private ResolvedProceduralPlacement resolvedFor(Object definition) { + return switch (definition) { + case Tree tree -> new ResolvedTree(tree, data); + case Ruin ruin -> new ResolvedRuin(ruin, data); + case Formation formation -> new ResolvedFormation(formation, data); + case Coral coral -> new ResolvedCoral(coral, data); + case Fungus fungus -> new ResolvedFungus(fungus, data); + case Crystal crystal -> new ResolvedCrystal(crystal, data); + default -> throw new IllegalArgumentException("Not a procedural object: " + definition.getClass().getName()); + }; } private ResolvedProceduralPlacement register(ResolvedProceduralPlacement placement) { @@ -75,14 +96,46 @@ public final class ProceduralObjectCatalog { return placement; } - /** The key of every variant the pack's regions and biomes declare, whether or not its dimensions reach them. */ + /** The key of every variant the pack defines, in snippets or in regions and biomes, whether or not its dimensions reach them. */ public List variantKeys() { Set keys = new LinkedHashSet<>(); + for (Class type : DEFINITION_TYPES) { + snippetDefinitions(type).forEach(definition -> keys.addAll(placementOf(definition).variantKeys())); + } data.getRegionLoader().streamAll().forEach(region -> addVariantKeys(keys, region.getProceduralObjects())); data.getBiomeLoader().streamAll().forEach(biome -> addVariantKeys(keys, biome.getProceduralObjects())); return List.copyOf(keys); } + private List snippetDefinitions(Class type) { + Path folder = data.getDataFolder().toPath().resolve("snippet").resolve(type.getAnnotation(Snippet.class).value()); + if (!Files.isDirectory(folder)) { + return List.of(); + } + try (Stream files = Files.walk(folder)) { + List definitions = new ArrayList<>(); + files.filter(file -> file.toString().endsWith(".json")).sorted().forEach(file -> { + Object definition = readDefinition(file, type); + if (definition != null) { + definitions.add(definition); + } + }); + return definitions; + } catch (IOException error) { + Iris.warn("Could not list the procedural objects in " + folder + ": " + error.getMessage()); + return List.of(); + } + } + + private Object readDefinition(Path file, Class type) { + try (Reader reader = Files.newBufferedReader(file)) { + return data.getGson().fromJson(reader, type); + } catch (IOException | RuntimeException error) { + Iris.warn("Could not read the procedural object " + file + ": " + error.getMessage()); + return null; + } + } + private void addVariantKeys(Set keys, ProceduralObjectSettings settings) { if (settings == null) { return;