diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestItem.java b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestItem.java
new file mode 100644
index 000000000..35e5001b6
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestItem.java
@@ -0,0 +1,33 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest;
+
+import java.util.Objects;
+
+public record ModIngestItem(String namespace, String sourceKey, String kind, ModIngestTier tier, String target, String reason) {
+ public ModIngestItem {
+ Objects.requireNonNull(namespace, "namespace");
+ Objects.requireNonNull(sourceKey, "sourceKey");
+ Objects.requireNonNull(kind, "kind");
+ Objects.requireNonNull(tier, "tier");
+ Objects.requireNonNull(target, "target");
+ Objects.requireNonNull(reason, "reason");
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestReport.java b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestReport.java
new file mode 100644
index 000000000..1ae2ddabc
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestReport.java
@@ -0,0 +1,32 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Objects;
+
+public record ModIngestReport(Path targetPack, List items, List summaryLines) {
+ public ModIngestReport {
+ Objects.requireNonNull(targetPack, "targetPack");
+ items = List.copyOf(Objects.requireNonNull(items, "items"));
+ summaryLines = List.copyOf(Objects.requireNonNull(summaryLines, "summaryLines"));
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestRequest.java b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestRequest.java
new file mode 100644
index 000000000..5849b6565
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestRequest.java
@@ -0,0 +1,36 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest;
+
+import art.arcane.iris.pack.ingest.catalog.WorldgenCatalog;
+
+import java.nio.file.Path;
+import java.util.Objects;
+import java.util.Set;
+
+public record ModIngestRequest(Path sourcePack, Path targetPack, String dimensionKey, WorldgenCatalog catalog, Set namespaces, boolean replaceTarget) {
+ public ModIngestRequest {
+ Objects.requireNonNull(sourcePack, "sourcePack");
+ Objects.requireNonNull(targetPack, "targetPack");
+ Objects.requireNonNull(dimensionKey, "dimensionKey");
+ Objects.requireNonNull(catalog, "catalog");
+ namespaces = Set.copyOf(Objects.requireNonNull(namespaces, "namespaces"));
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestTier.java b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestTier.java
new file mode 100644
index 000000000..bddb2444d
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngestTier.java
@@ -0,0 +1,27 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest;
+
+public enum ModIngestTier {
+ NATIVE,
+ ADOPTED,
+ PASSTHROUGH,
+ SKIPPED
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/ModIngester.java b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngester.java
new file mode 100644
index 000000000..5483dce24
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/ModIngester.java
@@ -0,0 +1,31 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest;
+
+import java.io.IOException;
+
+public final class ModIngester {
+ private ModIngester() {
+ }
+
+ public static ModIngestReport ingest(ModIngestRequest request) throws IOException {
+ throw new UnsupportedOperationException("Mod ingest is not implemented yet");
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogBiome.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogBiome.java
new file mode 100644
index 000000000..3ab33c1be
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogBiome.java
@@ -0,0 +1,42 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import com.google.gson.JsonElement;
+
+import java.util.List;
+import java.util.Objects;
+
+public record CatalogBiome(
+ String key,
+ float temperature,
+ float downfall,
+ boolean hasPrecipitation,
+ List tags,
+ List> featureSteps,
+ List spawns,
+ JsonElement definition) {
+ public CatalogBiome {
+ Objects.requireNonNull(key, "key");
+ tags = List.copyOf(Objects.requireNonNull(tags, "tags"));
+ featureSteps = Objects.requireNonNull(featureSteps, "featureSteps").stream().map(List::copyOf).toList();
+ spawns = List.copyOf(Objects.requireNonNull(spawns, "spawns"));
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogDimensionType.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogDimensionType.java
new file mode 100644
index 000000000..dc92d4fdd
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogDimensionType.java
@@ -0,0 +1,30 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import com.google.gson.JsonElement;
+
+import java.util.Objects;
+
+public record CatalogDimensionType(String key, int minY, int height, int logicalHeight, JsonElement definition) {
+ public CatalogDimensionType {
+ Objects.requireNonNull(key, "key");
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogLevelStem.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogLevelStem.java
new file mode 100644
index 000000000..ff1f77ed0
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogLevelStem.java
@@ -0,0 +1,38 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import com.google.gson.JsonElement;
+
+import java.util.Objects;
+
+public record CatalogLevelStem(
+ String key,
+ String dimensionType,
+ String generatorType,
+ String biomeSourceType,
+ JsonElement generator) {
+ public CatalogLevelStem {
+ Objects.requireNonNull(key, "key");
+ Objects.requireNonNull(dimensionType, "dimensionType");
+ Objects.requireNonNull(generatorType, "generatorType");
+ Objects.requireNonNull(biomeSourceType, "biomeSourceType");
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogNamespace.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogNamespace.java
new file mode 100644
index 000000000..49980ac62
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogNamespace.java
@@ -0,0 +1,30 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import java.util.Objects;
+
+public record CatalogNamespace(String namespace, String displayName, String version) {
+ public CatalogNamespace {
+ Objects.requireNonNull(namespace, "namespace");
+ Objects.requireNonNull(displayName, "displayName");
+ Objects.requireNonNull(version, "version");
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogPlacedFeature.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogPlacedFeature.java
new file mode 100644
index 000000000..2302ab779
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogPlacedFeature.java
@@ -0,0 +1,37 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import com.google.gson.JsonElement;
+
+import java.util.Objects;
+
+public record CatalogPlacedFeature(
+ String key,
+ String configuredFeature,
+ String featureType,
+ JsonElement config,
+ JsonElement placement) {
+ public CatalogPlacedFeature {
+ Objects.requireNonNull(key, "key");
+ Objects.requireNonNull(configuredFeature, "configuredFeature");
+ Objects.requireNonNull(featureType, "featureType");
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogSpawn.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogSpawn.java
new file mode 100644
index 000000000..c6c4b4ab1
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogSpawn.java
@@ -0,0 +1,29 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import java.util.Objects;
+
+public record CatalogSpawn(String category, String entity, int weight, int minCount, int maxCount) {
+ public CatalogSpawn {
+ Objects.requireNonNull(category, "category");
+ Objects.requireNonNull(entity, "entity");
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogStructure.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogStructure.java
new file mode 100644
index 000000000..890ef9580
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/CatalogStructure.java
@@ -0,0 +1,42 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import java.util.List;
+import java.util.Objects;
+
+public record CatalogStructure(
+ String key,
+ String type,
+ String step,
+ String terrainAdaptation,
+ String biomeTag,
+ List biomes,
+ List structureSets) {
+ public CatalogStructure {
+ Objects.requireNonNull(key, "key");
+ Objects.requireNonNull(type, "type");
+ Objects.requireNonNull(step, "step");
+ Objects.requireNonNull(terrainAdaptation, "terrainAdaptation");
+ Objects.requireNonNull(biomeTag, "biomeTag");
+ biomes = List.copyOf(Objects.requireNonNull(biomes, "biomes"));
+ structureSets = List.copyOf(Objects.requireNonNull(structureSets, "structureSets"));
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalog.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalog.java
new file mode 100644
index 000000000..ca04f42d5
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalog.java
@@ -0,0 +1,50 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import java.util.List;
+import java.util.Objects;
+
+public record WorldgenCatalog(
+ int format,
+ String minecraftVersion,
+ String platform,
+ long capturedAtEpochMillis,
+ String contentFingerprint,
+ List namespaces,
+ List biomes,
+ List placedFeatures,
+ List structures,
+ List dimensionTypes,
+ List levelStems) {
+ public static final int FORMAT = 1;
+
+ public WorldgenCatalog {
+ Objects.requireNonNull(minecraftVersion, "minecraftVersion");
+ Objects.requireNonNull(platform, "platform");
+ Objects.requireNonNull(contentFingerprint, "contentFingerprint");
+ namespaces = List.copyOf(Objects.requireNonNull(namespaces, "namespaces"));
+ biomes = List.copyOf(Objects.requireNonNull(biomes, "biomes"));
+ placedFeatures = List.copyOf(Objects.requireNonNull(placedFeatures, "placedFeatures"));
+ structures = List.copyOf(Objects.requireNonNull(structures, "structures"));
+ dimensionTypes = List.copyOf(Objects.requireNonNull(dimensionTypes, "dimensionTypes"));
+ levelStems = List.copyOf(Objects.requireNonNull(levelStems, "levelStems"));
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogIndex.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogIndex.java
new file mode 100644
index 000000000..c198818e3
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogIndex.java
@@ -0,0 +1,111 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+
+public final class WorldgenCatalogIndex {
+ private final WorldgenCatalog catalog;
+ private final Map biomes;
+ private final Map placedFeatures;
+ private final Map structures;
+ private final Map dimensionTypes;
+ private final Map levelStems;
+
+ private WorldgenCatalogIndex(WorldgenCatalog catalog) {
+ this.catalog = catalog;
+ this.biomes = index(catalog.biomes(), CatalogBiome::key);
+ this.placedFeatures = index(catalog.placedFeatures(), CatalogPlacedFeature::key);
+ this.structures = index(catalog.structures(), CatalogStructure::key);
+ this.dimensionTypes = index(catalog.dimensionTypes(), CatalogDimensionType::key);
+ this.levelStems = index(catalog.levelStems(), CatalogLevelStem::key);
+ }
+
+ public static WorldgenCatalogIndex of(WorldgenCatalog catalog) {
+ return new WorldgenCatalogIndex(Objects.requireNonNull(catalog, "catalog"));
+ }
+
+ public static String namespaceOf(String key) {
+ int colon = key.indexOf(':');
+ return colon < 0 ? "minecraft" : key.substring(0, colon);
+ }
+
+ public WorldgenCatalog catalog() {
+ return catalog;
+ }
+
+ public Optional biome(String key) {
+ return Optional.ofNullable(biomes.get(key));
+ }
+
+ public Optional placedFeature(String key) {
+ return Optional.ofNullable(placedFeatures.get(key));
+ }
+
+ public Optional structure(String key) {
+ return Optional.ofNullable(structures.get(key));
+ }
+
+ public Optional dimensionType(String key) {
+ return Optional.ofNullable(dimensionTypes.get(key));
+ }
+
+ public Optional levelStem(String key) {
+ return Optional.ofNullable(levelStems.get(key));
+ }
+
+ public List biomesIn(String namespace) {
+ List result = new ArrayList<>();
+ for (CatalogBiome biome : catalog.biomes()) {
+ if (namespaceOf(biome.key()).equals(namespace)) {
+ result.add(biome);
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ public List biomesDeclaring(String placedFeatureKey) {
+ List result = new ArrayList<>();
+ for (CatalogBiome biome : catalog.biomes()) {
+ for (List step : biome.featureSteps()) {
+ if (step.contains(placedFeatureKey)) {
+ result.add(biome);
+ break;
+ }
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ private static Map index(List values, Function key) {
+ Map result = new LinkedHashMap<>(Math.max(16, values.size() * 2));
+ for (T value : values) {
+ result.put(key.apply(value), value);
+ }
+ return Collections.unmodifiableMap(result);
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogStore.java b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogStore.java
new file mode 100644
index 000000000..72659f820
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogStore.java
@@ -0,0 +1,70 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Objects;
+
+public final class WorldgenCatalogStore {
+ public static final String FILE_NAME = "worldgen-catalog.json";
+ private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
+
+ private WorldgenCatalogStore() {
+ }
+
+ public static WorldgenCatalog read(Path file) throws IOException {
+ Objects.requireNonNull(file, "file");
+ try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
+ WorldgenCatalog catalog = GSON.fromJson(reader, WorldgenCatalog.class);
+ if (catalog == null) {
+ throw new IOException("Worldgen catalog is empty: " + file);
+ }
+ if (catalog.format() != WorldgenCatalog.FORMAT) {
+ throw new IOException("Worldgen catalog " + file + " has format " + catalog.format() + ", expected " + WorldgenCatalog.FORMAT);
+ }
+ return catalog;
+ }
+ }
+
+ public static void write(Path file, WorldgenCatalog catalog) throws IOException {
+ Objects.requireNonNull(file, "file");
+ Objects.requireNonNull(catalog, "catalog");
+ Path parent = file.toAbsolutePath().getParent();
+ Files.createDirectories(parent);
+ Path temp = Files.createTempFile(parent, FILE_NAME, ".tmp");
+ try {
+ try (Writer writer = Files.newBufferedWriter(temp, StandardCharsets.UTF_8)) {
+ GSON.toJson(catalog, writer);
+ }
+ Files.move(temp, file, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+ } finally {
+ Files.deleteIfExists(temp);
+ }
+ }
+}
diff --git a/core/src/test/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogStoreTest.java b/core/src/test/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogStoreTest.java
new file mode 100644
index 000000000..64fd6fba4
--- /dev/null
+++ b/core/src/test/java/art/arcane/iris/pack/ingest/catalog/WorldgenCatalogStoreTest.java
@@ -0,0 +1,83 @@
+/*
+ * Iris is a World Generator for Minecraft Bukkit Servers
+ * Copyright (c) 2022 Arcane Arts (Volmit Software)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+package art.arcane.iris.pack.ingest.catalog;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class WorldgenCatalogStoreTest {
+ @Rule
+ public TemporaryFolder folder = new TemporaryFolder();
+
+ @Test
+ public void writeThenReadPreservesEveryRecord() throws IOException {
+ JsonObject config = new JsonObject();
+ config.add("size", new JsonPrimitive(9));
+ WorldgenCatalog catalog = new WorldgenCatalog(
+ WorldgenCatalog.FORMAT,
+ "26.3",
+ "neoforge",
+ 1234L,
+ "abc",
+ List.of(new CatalogNamespace("examplemod", "Example Mod", "1.0.0")),
+ List.of(new CatalogBiome("examplemod:glade", 0.7F, 0.8F, true, List.of("minecraft:is_overworld"),
+ List.of(List.of(), List.of("examplemod:ore_glimmer")),
+ List.of(new CatalogSpawn("creature", "minecraft:sheep", 12, 2, 4)), new JsonObject())),
+ List.of(new CatalogPlacedFeature("examplemod:ore_glimmer", "examplemod:ore_glimmer", "minecraft:ore", config, null)),
+ List.of(new CatalogStructure("examplemod:tower", "minecraft:jigsaw", "surface_structures", "none",
+ "examplemod:has_tower", List.of("examplemod:glade"), List.of("examplemod:towers"))),
+ List.of(new CatalogDimensionType("examplemod:sky", 0, 256, 256, new JsonObject())),
+ List.of(new CatalogLevelStem("examplemod:sky", "examplemod:sky", "minecraft:noise", "minecraft:multi_noise", null)));
+ Path file = folder.getRoot().toPath().resolve("nested").resolve(WorldgenCatalogStore.FILE_NAME);
+
+ WorldgenCatalogStore.write(file, catalog);
+ WorldgenCatalog read = WorldgenCatalogStore.read(file);
+
+ assertEquals(catalog, read);
+ assertTrue(Files.list(file.getParent()).allMatch(path -> path.equals(file)));
+ }
+
+ @Test
+ public void indexResolvesNamespacesAndFeatureOwners() {
+ WorldgenCatalog catalog = new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 0L, "",
+ List.of(),
+ List.of(new CatalogBiome("examplemod:glade", 0.7F, 0.8F, true, List.of(), List.of(List.of("examplemod:flowers")), List.of(), null),
+ new CatalogBiome("minecraft:forest", 0.7F, 0.8F, true, List.of(), List.of(List.of("examplemod:flowers")), List.of(), null),
+ new CatalogBiome("minecraft:desert", 2.0F, 0.0F, false, List.of(), List.of(), List.of(), null)),
+ List.of(), List.of(), List.of(), List.of());
+ WorldgenCatalogIndex index = WorldgenCatalogIndex.of(catalog);
+
+ assertEquals(1, index.biomesIn("examplemod").size());
+ assertEquals(2, index.biomesDeclaring("examplemod:flowers").size());
+ assertEquals("minecraft", WorldgenCatalogIndex.namespaceOf("plains"));
+ assertTrue(index.biome("minecraft:desert").isPresent());
+ }
+}