Capped adoptions per template

- Each pack biome now serves as the template for at most five adopted land biomes and three adopted sea, shore, or cave biomes
- A mod biome whose best template is full falls back to the next best template under the cap
- A mod biome that finds every compatible template full is skipped with a reason naming the cap instead of being dropped
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-15 09:13:55 -04:00
1 parent 0ca33068f3
commit 523cc7e7be
3 files changed
+177 -8

No files matched your search

@@ -0,0 +1,151 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.pack.ingest;
import art.arcane.iris.pack.ingest.catalog.CatalogBiome;
import art.arcane.iris.pack.ingest.catalog.CatalogDimensionType;
import art.arcane.iris.pack.ingest.catalog.CatalogNamespace;
import art.arcane.iris.pack.ingest.catalog.WorldgenCatalog;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class IngestTemplateCapTest {
private static final String MOD = "modcaps";
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Test
public void caveTemplatesHostAtMostThreeModBiomesAndOverflowMovesToTheNextBestTemplate() throws Exception {
ModIngestReport report = ingest(7, 0);
Map<String, Integer> templates = templateUses(report);
assertEquals(Integer.valueOf(3), templates.get("caves/lush"));
for (Map.Entry<String, Integer> template : templates.entrySet()) {
assertTrue(template.toString(), template.getValue() <= 3);
}
for (int index = 0; index < 7; index++) {
assertEquals(ModIngestTier.ADOPTED, ModIngestFixture.item(report, MOD + ":grotto_" + index).tier());
}
}
@Test
public void landTemplatesHostAtMostFiveModBiomesAndOverflowMovesToTheNextBestTemplate() throws Exception {
ModIngestReport report = ingest(0, 7);
Map<String, Integer> templates = templateUses(report);
assertEquals(Integer.valueOf(5), templates.get("wet/marsh"));
assertEquals(Integer.valueOf(2), templates.get("dry/plains"));
}
@Test
public void modBiomeIsSkippedWithAReasonWhenEveryCompatibleTemplateIsFull() throws Exception {
ModIngestReport report = ingest(10, 0);
int adopted = 0;
List<ModIngestItem> skipped = new ArrayList<>();
for (int index = 0; index < 10; index++) {
ModIngestItem item = ModIngestFixture.item(report, MOD + ":grotto_" + index);
if (item.tier() == ModIngestTier.ADOPTED) {
adopted++;
} else {
skipped.add(item);
}
}
assertEquals(9, adopted);
assertEquals(1, skipped.size());
assertEquals(ModIngestTier.SKIPPED, skipped.getFirst().tier());
assertTrue(skipped.getFirst().reason(), skipped.getFirst().reason().contains("already hosts 3 adopted biomes"));
}
private ModIngestReport ingest(int caves, int marshes) throws IOException {
Path root = folder.newFolder().toPath();
Path source = root.resolve("source");
ModIngestFixture.write(source, "dimensions/overworld.json", """
{"name": "Overworld", "environment": "NORMAL", "regions": ["wet", "dry"], "dimensionHeight": {"min": -64, "max": 320}}
""");
ModIngestFixture.write(source, "regions/wet.json", """
{"name": "Wet", "landBiomes": ["wet/marsh"], "caveBiomes": ["caves/lush"]}
""");
ModIngestFixture.write(source, "regions/dry.json", """
{"name": "Dry", "landBiomes": ["dry/plains"], "caveBiomes": ["caves/drip", "caves/sand"]}
""");
biome(source, "wet/marsh", "minecraft:swamp");
biome(source, "dry/plains", "minecraft:plains");
biome(source, "caves/lush", "minecraft:lush_caves");
biome(source, "caves/drip", "minecraft:dripstone_caves");
biome(source, "caves/sand", "minecraft:dripstone_caves");
Path target = root.resolve("packs").resolve("capped");
return ModIngester.ingest(new ModIngestRequest(source, target, "overworld", catalog(caves, marshes), Set.of(MOD), Set.of(), false));
}
private static Map<String, Integer> templateUses(ModIngestReport report) {
Map<String, Integer> uses = new TreeMap<>();
for (ModIngestItem item : report.items()) {
if (item.kind().equals("biome") && item.tier() == ModIngestTier.ADOPTED) {
String reason = item.reason();
uses.merge(reason.substring(reason.indexOf("from template ") + "from template ".length()), 1, Integer::sum);
}
}
return uses;
}
private static void biome(Path pack, String key, String derivative) throws IOException {
ModIngestFixture.write(pack, "biomes/" + key + ".json", "{\"name\": \"" + key + "\", \"derivative\": \"" + derivative + "\"}");
}
private static WorldgenCatalog catalog(int caves, int marshes) {
List<CatalogBiome> biomes = new ArrayList<>();
biomes.add(biome("minecraft:swamp", 0.8F, 0.9F, List.of("c:is_swamp")));
biomes.add(biome("minecraft:plains", 0.8F, 0.4F, List.of("c:is_plains")));
biomes.add(biome("minecraft:lush_caves", 0.5F, 0.5F, List.of("c:is_cave", "c:is_lush")));
biomes.add(biome("minecraft:dripstone_caves", 2.0F, 0.0F, List.of("c:is_cave")));
for (int index = 0; index < caves; index++) {
biomes.add(biome(MOD + ":grotto_" + index, 0.5F, 0.5F, List.of("c:is_cave", "c:is_lush")));
}
for (int index = 0; index < marshes; index++) {
biomes.add(biome(MOD + ":marsh_" + index, 0.8F, 0.9F, List.of("c:is_swamp")));
}
return new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 1000L, "fingerprint-caps",
List.of(new CatalogNamespace(MOD, "Mod Caps", "1.0.0")), biomes, List.of(), List.of(),
List.of(new CatalogDimensionType("minecraft:overworld", -64, 384, 384, null)), List.of(), Map.of());
}
private static CatalogBiome biome(String key, float temperature, float downfall, List<String> categoryTags) {
List<List<String>> steps = new ArrayList<>(11);
for (int index = 0; index < 11; index++) {
steps.add(List.of());
}
List<String> tags = new ArrayList<>(categoryTags);
tags.add("minecraft:is_overworld");
return new CatalogBiome(key, temperature, downfall, true, tags, steps, List.of(), null);
}
}