mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-20 15:26:51 +00:00
Ordered converted ore hosts
- The worldgen catalog (format 2) records the members of every block tag referenced by a captured placed feature, so ore targets using mod block tags convert instead of passing through - Ore conversion supports noise_based_count and noise_threshold_count placement counts - Converted ores that produce another converted ore's host block are written ahead of it, so an ore whose host comes from a sibling ore finds that host during the deposit stage
This commit is contained in:
1 parent
d5796b275e
commit
965e19801d
20 files changed
+445
-45
No files matched your search
+47
-1
@@ -37,12 +37,17 @@ import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot.
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot.WorldgenSpawn;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot.WorldgenStructure;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public final class WorldgenCatalogAssembler {
|
||||
private static final String MINECRAFT = "minecraft";
|
||||
@@ -58,16 +63,57 @@ public final class WorldgenCatalogAssembler {
|
||||
List<CatalogStructure> structures = structures(snapshot.structures());
|
||||
List<CatalogDimensionType> dimensionTypes = dimensionTypes(snapshot.dimensionTypes());
|
||||
List<CatalogLevelStem> levelStems = levelStems(snapshot.levelStems());
|
||||
Map<String, List<String>> blockTags = referencedBlockTags(placedFeatures, snapshot.blockTags());
|
||||
Map<String, List<String>> registryKeys = new LinkedHashMap<>();
|
||||
registryKeys.put("biome", biomes.stream().map(CatalogBiome::key).toList());
|
||||
registryKeys.put("placed_feature", placedFeatures.stream().map(CatalogPlacedFeature::key).toList());
|
||||
registryKeys.put("structure", structures.stream().map(CatalogStructure::key).toList());
|
||||
registryKeys.put("dimension_type", dimensionTypes.stream().map(CatalogDimensionType::key).toList());
|
||||
registryKeys.put("level_stem", levelStems.stream().map(CatalogLevelStem::key).toList());
|
||||
registryKeys.put("block_tag", List.copyOf(blockTags.keySet()));
|
||||
List<CatalogNamespace> namespaces = namespaces(registryKeys, context);
|
||||
return new WorldgenCatalog(WorldgenCatalog.FORMAT, context.minecraftVersion(), context.platform(),
|
||||
context.capturedAtEpochMillis(), WorldgenCatalogFingerprint.of(namespaces, registryKeys), namespaces,
|
||||
biomes, placedFeatures, structures, dimensionTypes, levelStems);
|
||||
biomes, placedFeatures, structures, dimensionTypes, levelStems, blockTags);
|
||||
}
|
||||
|
||||
private static Map<String, List<String>> referencedBlockTags(List<CatalogPlacedFeature> features,
|
||||
Map<String, List<String>> available) {
|
||||
Set<String> referenced = new TreeSet<>();
|
||||
for (CatalogPlacedFeature feature : features) {
|
||||
collectTagMatches(feature.config(), referenced);
|
||||
}
|
||||
Map<String, List<String>> tags = new TreeMap<>();
|
||||
for (String tag : referenced) {
|
||||
List<String> members = available.get(tag);
|
||||
if (members != null) {
|
||||
tags.put(tag, members);
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
private static void collectTagMatches(JsonElement element, Set<String> tags) {
|
||||
if (element == null || element.isJsonNull() || element.isJsonPrimitive()) {
|
||||
return;
|
||||
}
|
||||
if (element.isJsonArray()) {
|
||||
for (JsonElement entry : element.getAsJsonArray()) {
|
||||
collectTagMatches(entry, tags);
|
||||
}
|
||||
return;
|
||||
}
|
||||
JsonObject object = element.getAsJsonObject();
|
||||
JsonElement predicate = object.get("predicate_type");
|
||||
JsonElement tag = object.get("tag");
|
||||
if (predicate != null && predicate.isJsonPrimitive() && predicate.getAsString().endsWith("tag_match")
|
||||
&& tag != null && tag.isJsonPrimitive()) {
|
||||
String key = tag.getAsString();
|
||||
tags.add(key.contains(":") ? key : MINECRAFT + ":" + key);
|
||||
}
|
||||
for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
|
||||
collectTagMatches(entry.getValue(), tags);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<CatalogNamespace> namespaces(Map<String, List<String>> registryKeys, CatalogCaptureContext context) {
|
||||
|
||||
+19
-3
@@ -23,6 +23,7 @@ import art.arcane.iris.pack.ingest.catalog.CatalogLevelStem;
|
||||
import art.arcane.iris.pack.ingest.catalog.CatalogNamespace;
|
||||
import art.arcane.iris.pack.ingest.catalog.CatalogPlacedFeature;
|
||||
import art.arcane.iris.pack.ingest.catalog.WorldgenCatalog;
|
||||
import art.arcane.iris.pack.ingest.catalog.WorldgenCatalogIndex;
|
||||
import art.arcane.iris.pack.ingest.catalog.WorldgenCatalogFingerprint;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModMetadata;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot;
|
||||
@@ -34,6 +35,7 @@ import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot.
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot.WorldgenSpawn;
|
||||
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldgenSnapshot.WorldgenStructure;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -104,15 +106,27 @@ public class WorldgenCatalogAssemblerTest {
|
||||
"placed_feature", List.of("examplemod:ore_glimmer"),
|
||||
"structure", List.of("examplemod:tower"),
|
||||
"dimension_type", List.of("minecraft:overworld"),
|
||||
"level_stem", List.of("minecraft:overworld")));
|
||||
"level_stem", List.of("minecraft:overworld"),
|
||||
"block_tag", List.of("examplemod:glimmer_hosts")));
|
||||
assertEquals(expected, catalog.contentFingerprint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyBlockTagsReferencedByPlacedFeaturesAreKept() {
|
||||
WorldgenCatalog catalog = WorldgenCatalogAssembler.assemble(snapshot(), CONTEXT);
|
||||
|
||||
assertEquals(Map.of("examplemod:glimmer_hosts", List.of("minecraft:dirt", "minecraft:grass_block")), catalog.blockTags());
|
||||
assertEquals(List.of("minecraft:dirt", "minecraft:grass_block"),
|
||||
WorldgenCatalogIndex.of(catalog).blockTag("examplemod:glimmer_hosts").orElseThrow());
|
||||
}
|
||||
|
||||
static NativeWorldgenSnapshot snapshot() {
|
||||
JsonObject definition = new JsonObject();
|
||||
definition.add("downfall", new JsonPrimitive(0.4F));
|
||||
JsonObject config = new JsonObject();
|
||||
config.add("type", new JsonPrimitive("minecraft:ore"));
|
||||
JsonObject config = JsonParser.parseString("""
|
||||
{"type": "minecraft:ore", "targets": [{"state": "examplemod:glimmer_ore",
|
||||
"target": {"predicate_type": "minecraft:tag_match", "tag": "examplemod:glimmer_hosts"}}]}
|
||||
""").getAsJsonObject();
|
||||
return new NativeWorldgenSnapshot(
|
||||
List.of(new WorldgenBiome("examplemod:glade", 0.7F, 0.4F, true, List.of("minecraft:is_overworld"),
|
||||
List.of(List.of(), List.of("examplemod:ore_glimmer")),
|
||||
@@ -124,6 +138,8 @@ public class WorldgenCatalogAssemblerTest {
|
||||
List.of(new WorldgenDimensionType("minecraft:overworld", -64, 384, 384, null)),
|
||||
List.of(new WorldgenLevelStem("minecraft:overworld", "minecraft:overworld", "minecraft:noise",
|
||||
"minecraft:multi_noise", null)),
|
||||
Map.of("examplemod:glimmer_hosts", List.of("minecraft:dirt", "minecraft:grass_block"),
|
||||
"minecraft:unreferenced", List.of("minecraft:stone")),
|
||||
new EncodeFailures(0, List.of(), null));
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -30,6 +30,7 @@ import art.arcane.iris.pack.ingest.catalog.WorldgenCatalog;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -74,7 +75,7 @@ public class WorldgenCatalogSummaryTest {
|
||||
@Test
|
||||
public void keyNamespacesWithoutModMetadataStillSummarize() {
|
||||
WorldgenCatalog catalog = new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 1L, "x",
|
||||
List.of(), List.of(biome("datapack:dunes")), List.of(), List.of(), List.of(), List.of());
|
||||
List.of(), List.of(biome("datapack:dunes")), List.of(), List.of(), List.of(), List.of(), Map.of());
|
||||
|
||||
NamespaceSummary summary = WorldgenCatalogSummary.namespace(catalog, "datapack").orElseThrow();
|
||||
|
||||
@@ -104,7 +105,7 @@ public class WorldgenCatalogSummaryTest {
|
||||
List.of(new CatalogStructure("examplemod:tower", "minecraft:jigsaw", "surface_structures", "none", "",
|
||||
List.of(), List.of())),
|
||||
List.of(new CatalogDimensionType("examplemod:sky", 0, 256, 256, null)),
|
||||
List.of(new CatalogLevelStem("examplemod:sky", "examplemod:sky", "minecraft:noise", "minecraft:fixed", null)));
|
||||
List.of(new CatalogLevelStem("examplemod:sky", "examplemod:sky", "minecraft:noise", "minecraft:fixed", null)), Map.of());
|
||||
}
|
||||
|
||||
private static CatalogBiome biome(String key) {
|
||||
|
||||
@@ -27,6 +27,8 @@ import com.google.gson.JsonObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -37,6 +39,8 @@ import java.util.TreeSet;
|
||||
|
||||
final class ModFeatureIngester {
|
||||
private static final String VANILLA_NAMESPACE = "minecraft";
|
||||
private static final String REPLACEABLE_BLOCKS = "replaceableBlocks";
|
||||
private static final String PALETTE = "palette";
|
||||
|
||||
private final IngestPackModel model;
|
||||
private final WorldgenCatalogIndex index;
|
||||
@@ -96,7 +100,7 @@ final class ModFeatureIngester {
|
||||
universe.addAll(model.reachableBiomes().values());
|
||||
Set<String> caveKeys = caveKeys(adopted);
|
||||
List<ModIngestItem> items = new ArrayList<>(plans.size());
|
||||
for (FeaturePlan plan : plans.values()) {
|
||||
for (FeaturePlan plan : hostProducersFirst(plans.values())) {
|
||||
String namespace = WorldgenCatalogIndex.namespaceOf(plan.key());
|
||||
Set<String> hosts = new TreeSet<>(plan.declaring());
|
||||
hosts.retainAll(universe);
|
||||
@@ -128,6 +132,57 @@ final class ModFeatureIngester {
|
||||
return items;
|
||||
}
|
||||
|
||||
static List<FeaturePlan> hostProducersFirst(Collection<FeaturePlan> plans) {
|
||||
List<FeaturePlan> pending = new ArrayList<>(plans);
|
||||
List<FeaturePlan> ordered = new ArrayList<>(pending.size());
|
||||
while (!pending.isEmpty()) {
|
||||
FeaturePlan next = pending.getFirst();
|
||||
for (FeaturePlan candidate : pending) {
|
||||
if (!waitsForHostProducer(candidate, pending)) {
|
||||
next = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pending.remove(next);
|
||||
ordered.add(next);
|
||||
}
|
||||
return ordered;
|
||||
}
|
||||
|
||||
private static boolean waitsForHostProducer(FeaturePlan consumer, List<FeaturePlan> pending) {
|
||||
Set<String> hosts = depositBlocks(consumer, REPLACEABLE_BLOCKS);
|
||||
if (hosts.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (FeaturePlan producer : pending) {
|
||||
if (producer != consumer && !Collections.disjoint(depositBlocks(producer, PALETTE), hosts)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Set<String> depositBlocks(FeaturePlan plan, String field) {
|
||||
if (plan.deposits() == null) {
|
||||
return Set.of();
|
||||
}
|
||||
Set<String> blocks = new TreeSet<>();
|
||||
for (JsonObject deposit : plan.deposits()) {
|
||||
JsonElement values = deposit.get(field);
|
||||
if (values == null || !values.isJsonArray()) {
|
||||
continue;
|
||||
}
|
||||
for (JsonElement value : values.getAsJsonArray()) {
|
||||
if (value.isJsonPrimitive()) {
|
||||
blocks.add(value.getAsString());
|
||||
} else if (value.isJsonObject() && value.getAsJsonObject().has("block")) {
|
||||
blocks.add(value.getAsJsonObject().get("block").getAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
private boolean treeInPackVanillaBiomes(FeaturePlan plan) {
|
||||
Optional<CatalogPlacedFeature> feature = index.placedFeature(plan.key());
|
||||
if (feature.isEmpty() || !trees.isTree(feature.get())) {
|
||||
@@ -149,7 +204,8 @@ final class ModFeatureIngester {
|
||||
CatalogPlacedFeature placed = feature.get();
|
||||
try {
|
||||
if (OreDepositConverter.handles(placed)) {
|
||||
List<JsonObject> deposits = OreDepositConverter.convert(placed, model.verticalBounds(), model.packMinY());
|
||||
List<JsonObject> deposits = OreDepositConverter.convert(placed, model.verticalBounds(), model.packMinY(),
|
||||
index.catalog().blockTags());
|
||||
return new FeaturePlan(key, declaring, steps, deposits, null, "");
|
||||
}
|
||||
if (PatchDecoratorConverter.handles(placed)) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
@@ -42,7 +43,8 @@ final class OreDepositConverter {
|
||||
return ORE_TYPES.contains(VanillaJson.namespaced(feature.featureType()));
|
||||
}
|
||||
|
||||
static List<JsonObject> convert(CatalogPlacedFeature feature, VanillaHeights.VerticalBounds bounds, int packMinY) throws IngestConversionException {
|
||||
static List<JsonObject> convert(CatalogPlacedFeature feature, VanillaHeights.VerticalBounds bounds, int packMinY,
|
||||
Map<String, List<String>> blockTags) throws IngestConversionException {
|
||||
JsonObject config = VanillaJson.featureConfig(feature.config());
|
||||
Placement placement = placement(VanillaJson.placement(feature.placement()), bounds);
|
||||
int size = VanillaJson.integer(config.get("size"), "ore size");
|
||||
@@ -52,7 +54,7 @@ final class OreDepositConverter {
|
||||
double discard = VanillaJson.number(config, "discard_chance_on_air_exposure", 0D, "ore");
|
||||
String shape = VanillaJson.namespaced(feature.featureType()).equals("minecraft:scattered_ore") ? "VANILLA_SCATTERED" : "VANILLA_ELLIPSOID";
|
||||
List<JsonObject> deposits = new ArrayList<>();
|
||||
for (Target target : targets(VanillaJson.array(config.get("targets"), "ore targets"))) {
|
||||
for (Target target : targets(VanillaJson.array(config.get("targets"), "ore targets"), blockTags)) {
|
||||
JsonObject deposit = new JsonObject();
|
||||
deposit.addProperty("minHeight", placement.band().min() - packMinY);
|
||||
deposit.addProperty("maxHeight", placement.band().max() - packMinY);
|
||||
@@ -84,13 +86,13 @@ final class OreDepositConverter {
|
||||
return deposits;
|
||||
}
|
||||
|
||||
private static List<Target> targets(JsonArray targets) throws IngestConversionException {
|
||||
private static List<Target> targets(JsonArray targets, Map<String, List<String>> blockTags) throws IngestConversionException {
|
||||
List<Target> result = new ArrayList<>(targets.size());
|
||||
SortedSet<String> claimed = new TreeSet<>();
|
||||
for (JsonElement entry : targets) {
|
||||
JsonObject target = VanillaJson.object(entry, "ore target");
|
||||
JsonObject block = VanillaBlockStates.irisBlock(target.get("state"));
|
||||
VanillaRuleTests.HostSet hosts = VanillaRuleTests.hosts(target.get("target"));
|
||||
VanillaRuleTests.HostSet hosts = VanillaRuleTests.hosts(target.get("target"), blockTags);
|
||||
if (hosts.any()) {
|
||||
if (!claimed.isEmpty()) {
|
||||
throw new IngestConversionException("ore target accepting any block follows restricted targets");
|
||||
@@ -125,6 +127,11 @@ final class OreDepositConverter {
|
||||
}
|
||||
case "minecraft:rarity_filter" -> spawnChance /= Math.max(1D, VanillaJson.number(object.get("chance"), "rarity_filter.chance"));
|
||||
case "minecraft:random_chance" -> spawnChance *= Math.max(0D, Math.min(1D, VanillaJson.number(object.get("chance"), "random_chance.chance")));
|
||||
case "minecraft:noise_based_count", "minecraft:noise_threshold_count" -> {
|
||||
double mean = VanillaPlacementChain.noiseCountMean(object);
|
||||
minCount = Math.min(MAX_PER_CHUNK, Math.round(minCount * mean));
|
||||
maxCount = Math.min(MAX_PER_CHUNK, Math.round(maxCount * mean));
|
||||
}
|
||||
case "minecraft:in_square", "minecraft:biome" -> {
|
||||
}
|
||||
case "minecraft:height_range" -> band = VanillaHeights.band(object.get("height"), bounds);
|
||||
|
||||
@@ -166,6 +166,14 @@ final class VanillaPlacementChain {
|
||||
return values.get(0).getAsInt() == 0 && values.get(1).getAsInt() == -1 && values.get(2).getAsInt() == 0;
|
||||
}
|
||||
|
||||
static double noiseCountMean(JsonObject object) throws IngestConversionException {
|
||||
if (VanillaJson.type(object, "type").equals("minecraft:noise_threshold_count")) {
|
||||
return (VanillaJson.number(object.get("below_noise"), "noise_threshold_count.below_noise")
|
||||
+ VanillaJson.number(object.get("above_noise"), "noise_threshold_count.above_noise")) / 2D;
|
||||
}
|
||||
return noiseBasedMean(object);
|
||||
}
|
||||
|
||||
private static double noiseBasedMean(JsonObject object) throws IngestConversionException {
|
||||
double ratio = VanillaJson.number(object.get("noise_to_count_ratio"), "noise_based_count.noise_to_count_ratio");
|
||||
double offset = VanillaJson.number(object, "noise_offset", 0D, "noise_based_count");
|
||||
|
||||
@@ -42,32 +42,32 @@ final class VanillaRuleTests {
|
||||
private VanillaRuleTests() {
|
||||
}
|
||||
|
||||
static HostSet hosts(JsonElement ruleTest) throws IngestConversionException {
|
||||
static HostSet hosts(JsonElement ruleTest, Map<String, List<String>> blockTags) throws IngestConversionException {
|
||||
JsonObject object = VanillaJson.object(ruleTest, "ore target predicate");
|
||||
String type = VanillaJson.type(object, "predicate_type");
|
||||
return switch (type) {
|
||||
case "minecraft:always_true" -> HostSet.ANY;
|
||||
case "minecraft:tag_match" -> tag(VanillaJson.namespaced(VanillaJson.string(object.get("tag"), "tag_match.tag")));
|
||||
case "minecraft:tag_match" -> tag(VanillaJson.namespaced(VanillaJson.string(object.get("tag"), "tag_match.tag")), blockTags);
|
||||
case "minecraft:block_match" -> HostSet.of(Set.of(VanillaJson.namespaced(VanillaJson.string(object.get("block"), "block_match.block"))));
|
||||
case "minecraft:blockstate_match" -> HostSet.of(Set.of(VanillaBlockStates.irisBlock(object.get("block_state")).get("block").getAsString()));
|
||||
case "minecraft:any_of" -> anyOf(rules(object));
|
||||
case "minecraft:all_of" -> allOf(rules(object));
|
||||
case "minecraft:any_of" -> anyOf(rules(object), blockTags);
|
||||
case "minecraft:all_of" -> allOf(rules(object), blockTags);
|
||||
default -> throw new IngestConversionException("unsupported ore target predicate " + (type.isEmpty() ? object.toString() : type));
|
||||
};
|
||||
}
|
||||
|
||||
private static HostSet tag(String tag) throws IngestConversionException {
|
||||
List<String> blocks = BLOCK_TAGS.get(tag);
|
||||
private static HostSet tag(String tag, Map<String, List<String>> blockTags) throws IngestConversionException {
|
||||
List<String> blocks = blockTags.containsKey(tag) ? blockTags.get(tag) : BLOCK_TAGS.get(tag);
|
||||
if (blocks == null) {
|
||||
throw new IngestConversionException("ore target uses block tag " + tag + " whose members are not known offline");
|
||||
throw new IngestConversionException("ore target uses block tag " + tag + " whose members the catalog did not capture");
|
||||
}
|
||||
return HostSet.of(Set.copyOf(blocks));
|
||||
}
|
||||
|
||||
private static HostSet anyOf(List<JsonElement> rules) throws IngestConversionException {
|
||||
private static HostSet anyOf(List<JsonElement> rules, Map<String, List<String>> blockTags) throws IngestConversionException {
|
||||
SortedSet<String> union = new TreeSet<>();
|
||||
for (JsonElement rule : rules) {
|
||||
HostSet hosts = hosts(rule);
|
||||
HostSet hosts = hosts(rule, blockTags);
|
||||
if (hosts.any()) {
|
||||
return HostSet.ANY;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ final class VanillaRuleTests {
|
||||
return HostSet.of(union);
|
||||
}
|
||||
|
||||
private static HostSet allOf(List<JsonElement> rules) throws IngestConversionException {
|
||||
private static HostSet allOf(List<JsonElement> rules, Map<String, List<String>> blockTags) throws IngestConversionException {
|
||||
HostSet positive = null;
|
||||
SortedSet<String> negative = new TreeSet<>();
|
||||
for (JsonElement rule : rules) {
|
||||
@@ -86,14 +86,14 @@ final class VanillaRuleTests {
|
||||
continue;
|
||||
}
|
||||
if (type.equals("minecraft:not")) {
|
||||
HostSet excluded = hosts(ruleObject.get("rule"));
|
||||
HostSet excluded = hosts(ruleObject.get("rule"), blockTags);
|
||||
if (excluded.any()) {
|
||||
throw new IngestConversionException("ore target predicate never matches");
|
||||
}
|
||||
negative.addAll(excluded.blocks());
|
||||
continue;
|
||||
}
|
||||
positive = intersect(positive, hosts(ruleObject));
|
||||
positive = intersect(positive, hosts(ruleObject, blockTags));
|
||||
}
|
||||
if (positive == null) {
|
||||
throw new IngestConversionException("ore target all_of has no block constraint");
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package art.arcane.iris.pack.ingest.catalog;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public record WorldgenCatalog(
|
||||
@@ -33,8 +34,9 @@ public record WorldgenCatalog(
|
||||
List<CatalogPlacedFeature> placedFeatures,
|
||||
List<CatalogStructure> structures,
|
||||
List<CatalogDimensionType> dimensionTypes,
|
||||
List<CatalogLevelStem> levelStems) {
|
||||
public static final int FORMAT = 1;
|
||||
List<CatalogLevelStem> levelStems,
|
||||
Map<String, List<String>> blockTags) {
|
||||
public static final int FORMAT = 2;
|
||||
|
||||
public WorldgenCatalog {
|
||||
Objects.requireNonNull(minecraftVersion, "minecraftVersion");
|
||||
@@ -46,5 +48,6 @@ public record WorldgenCatalog(
|
||||
structures = List.copyOf(Objects.requireNonNull(structures, "structures"));
|
||||
dimensionTypes = List.copyOf(Objects.requireNonNull(dimensionTypes, "dimensionTypes"));
|
||||
levelStems = List.copyOf(Objects.requireNonNull(levelStems, "levelStems"));
|
||||
blockTags = Map.copyOf(Objects.requireNonNull(blockTags, "blockTags"));
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,10 @@ public final class WorldgenCatalogIndex {
|
||||
return Optional.ofNullable(dimensionTypes.get(key));
|
||||
}
|
||||
|
||||
public Optional<List<String>> blockTag(String key) {
|
||||
return Optional.ofNullable(catalog.blockTags().get(key));
|
||||
}
|
||||
|
||||
public Optional<CatalogLevelStem> levelStem(String key) {
|
||||
return Optional.ofNullable(levelStems.get(key));
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -53,7 +54,7 @@ public class CanopyEstimateTest {
|
||||
JsonParser.parseString("[{\"type\": \"minecraft:count\", \"count\": 10}, {\"type\": \"minecraft:in_square\"},"
|
||||
+ "{\"type\": \"minecraft:surface_water_depth_filter\", \"max_water_depth\": 0}, {\"type\": \"minecraft:biome\"}]")));
|
||||
return new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 1L, "canopy", List.of(new CatalogNamespace("mod", "Mod", "1")),
|
||||
biomes, features, List.of(), List.of(), List.of());
|
||||
biomes, features, List.of(), List.of(), List.of(), Map.of());
|
||||
}
|
||||
|
||||
private static CatalogBiome biome(String key, List<String> vegetal) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.gson.JsonParser;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
@@ -65,7 +66,7 @@ public class FeatureConverterFormatTest {
|
||||
{"type": "minecraft:biome"}]
|
||||
""");
|
||||
|
||||
List<JsonObject> deposits = OreDepositConverter.convert(iron, OVERWORLD, -64);
|
||||
List<JsonObject> deposits = OreDepositConverter.convert(iron, OVERWORLD, -64, Map.of());
|
||||
|
||||
assertEquals(2, deposits.size());
|
||||
assertEquals("minecraft:iron_ore", deposits.get(0).getAsJsonArray("palette").get(0).getAsJsonObject().get("block").getAsString());
|
||||
@@ -88,7 +89,7 @@ public class FeatureConverterFormatTest {
|
||||
{"type": "minecraft:biome"}]
|
||||
"""));
|
||||
|
||||
List<JsonObject> deposits = OreDepositConverter.convert(clay, OVERWORLD, -64);
|
||||
List<JsonObject> deposits = OreDepositConverter.convert(clay, OVERWORLD, -64, Map.of());
|
||||
|
||||
assertEquals(1, deposits.size());
|
||||
assertEquals("FULL_HEIGHT", deposits.getFirst().get("placementScope").getAsString());
|
||||
@@ -101,7 +102,7 @@ public class FeatureConverterFormatTest {
|
||||
"{\"size\": 4, \"targets\": [{\"state\": \"minecraft:stone\", \"target\": {\"predicate_type\": \"minecraft:always_true\"}}]}",
|
||||
"[{\"type\": \"minecraft:environment_scan\", \"direction_of_search\": \"down\", \"max_steps\": 16, \"target_condition\": {\"type\": \"minecraft:solid\"}}]");
|
||||
|
||||
IngestConversionException failure = assertThrows(IngestConversionException.class, () -> OreDepositConverter.convert(buried, OVERWORLD, -64));
|
||||
IngestConversionException failure = assertThrows(IngestConversionException.class, () -> OreDepositConverter.convert(buried, OVERWORLD, -64, Map.of()));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("minecraft:environment_scan"));
|
||||
}
|
||||
@@ -178,6 +179,6 @@ public class FeatureConverterFormatTest {
|
||||
|
||||
private static WorldgenCatalogIndex index() {
|
||||
return WorldgenCatalogIndex.of(new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 0L, "",
|
||||
List.of(), List.of(), List.of(), List.of(), List.of(), List.of()));
|
||||
List.of(), List.of(), List.of(), List.of(), List.of(), List.of(), Map.of()));
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ 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.TreeSet;
|
||||
|
||||
@@ -129,7 +130,7 @@ public class IngestBiomePlacementTest {
|
||||
}
|
||||
return new WorldgenCatalog(WorldgenCatalog.FORMAT, "26.3", "fabric", 1000L, "fingerprint-placement",
|
||||
List.of(new CatalogNamespace(MOD, "Mod Pack", "1.0.0")), biomes, List.of(), List.of(),
|
||||
List.of(new CatalogDimensionType("minecraft:overworld", -64, 384, 384, null)), 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, String categoryTag) {
|
||||
|
||||
@@ -52,7 +52,7 @@ public class IngestHeightBoundsTest {
|
||||
types.add(new CatalogDimensionType("examplemod:skyrealm", 0, 256, 256, null));
|
||||
WorldgenCatalog catalog = new WorldgenCatalog(fixture.format(), fixture.minecraftVersion(), fixture.platform(),
|
||||
fixture.capturedAtEpochMillis(), fixture.contentFingerprint(), fixture.namespaces(), fixture.biomes(),
|
||||
fixture.placedFeatures(), fixture.structures(), types, fixture.levelStems());
|
||||
fixture.placedFeatures(), fixture.structures(), types, fixture.levelStems(), fixture.blockTags());
|
||||
|
||||
JsonObject stone = tinDeposit("\"dimensionHeight\": {\"min\": 0, \"max\": 192}, \"borrowedDimensionType\": \"examplemod:skyrealm\"",
|
||||
catalog);
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.block.B;
|
||||
import art.arcane.iris.generation.block.IrisBlockData;
|
||||
import art.arcane.iris.generation.concurrent.MultiBurst;
|
||||
import art.arcane.iris.generation.context.ChunkContext;
|
||||
import art.arcane.iris.generation.context.ChunkedDataCache;
|
||||
import art.arcane.iris.generation.decoration.IrisDepositGenerator;
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import art.arcane.iris.generation.stage.IrisDepositModifier;
|
||||
import art.arcane.iris.generation.terrain.IrisDimension;
|
||||
import art.arcane.iris.generation.terrain.IrisRegion;
|
||||
import art.arcane.iris.pack.ingest.catalog.CatalogBiome;
|
||||
import art.arcane.iris.pack.ingest.catalog.CatalogPlacedFeature;
|
||||
import art.arcane.iris.pack.ingest.catalog.WorldgenCatalog;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.testsupport.KeyedBlockState;
|
||||
import art.arcane.iris.testsupport.PlatformLeakGuard;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.hunk.Hunk;
|
||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
||||
import art.arcane.volmlib.util.matter.Matter;
|
||||
import art.arcane.volmlib.util.stream.ProceduralStream;
|
||||
import art.arcane.volmlib.util.stream.interpolation.Interpolated;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.withSettings;
|
||||
|
||||
public class IngestOreHostOrderTest {
|
||||
@ClassRule
|
||||
public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean();
|
||||
private static final String MOD = ModIngestFixture.MOD;
|
||||
private static final int PACK_MIN_Y = -128;
|
||||
private static final int HEIGHT = 448;
|
||||
private static final int CHUNKS = 2;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
private final Map<String, NativeBlockState> states = new HashMap<>();
|
||||
|
||||
@Test
|
||||
public void hostProducingOreIsConvertedAndRunsBeforeTheOreThatConsumesItsHost() throws Exception {
|
||||
JsonArray deposits = ingestedDeposits();
|
||||
|
||||
assertEquals(2, deposits.size());
|
||||
assertEquals("minecraft:sand", paletteBlock(deposits.get(0)));
|
||||
assertEquals("minecraft:clay", paletteBlock(deposits.get(1)));
|
||||
assertTrue(placed(List.of(deposits.get(0), deposits.get(1)), "minecraft:clay") > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void consumerRunningBeforeItsHostProducerFindsNoHost() throws Exception {
|
||||
JsonArray deposits = ingestedDeposits();
|
||||
|
||||
assertEquals(0, placed(List.of(deposits.get(1), deposits.get(0)), "minecraft:clay"));
|
||||
}
|
||||
|
||||
private JsonArray ingestedDeposits() throws Exception {
|
||||
Path root = folder.newFolder().toPath();
|
||||
Path source = ModIngestFixture.writeSourcePack(root);
|
||||
Path target = root.resolve("packs").resolve("isles");
|
||||
ModIngestReport report = ModIngester.ingest(new ModIngestRequest(source, target, "overworld", catalog(), Set.of(MOD), Set.of(), false));
|
||||
assertEquals(ModIngestTier.NATIVE, ModIngestFixture.item(report, MOD + ":isles/sand_beaches").tier());
|
||||
assertEquals(ModIngestTier.NATIVE, ModIngestFixture.item(report, MOD + ":isles/clay_patch").tier());
|
||||
return ModIngestFixture.readJson(target.resolve("biomes/ingest/" + MOD + "/sand_isles.json")).getAsJsonArray("deposits");
|
||||
}
|
||||
|
||||
private static String paletteBlock(JsonElement deposit) {
|
||||
return deposit.getAsJsonObject().getAsJsonArray("palette").get(0).getAsJsonObject().get("block").getAsString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private int placed(List<JsonElement> depositJson, String block) {
|
||||
Gson gson = new Gson();
|
||||
IrisBiome surface = new IrisBiome();
|
||||
for (JsonElement json : depositJson) {
|
||||
ResolvedDeposit deposit = gson.fromJson(json, ResolvedDeposit.class);
|
||||
deposit.bind(states);
|
||||
surface.getDeposits().add(deposit);
|
||||
}
|
||||
Engine engine = mock(Engine.class, withSettings().stubOnly().defaultAnswer(RETURNS_DEEP_STUBS));
|
||||
ChunkContext context = mock(ChunkContext.class, withSettings().stubOnly().defaultAnswer(RETURNS_DEEP_STUBS));
|
||||
IrisRegion region = new IrisRegion();
|
||||
when(engine.getHeight()).thenReturn(HEIGHT);
|
||||
when(engine.getMinHeight()).thenReturn(PACK_MIN_Y);
|
||||
when(engine.getSeedManager().getDeposit()).thenReturn(11L);
|
||||
when(engine.getDimension()).thenReturn(new IrisDimension());
|
||||
when(context.getRoundedHeight(anyInt(), anyInt())).thenReturn(61 - PACK_MIN_Y);
|
||||
when(context.getBiome()).thenReturn(new ChunkedDataCache<>(
|
||||
ProceduralStream.of((x, z) -> surface, Interpolated.of(value -> 0D, value -> null)), 0, 0));
|
||||
when(context.getRegion()).thenReturn(new ChunkedDataCache<>(
|
||||
ProceduralStream.of((x, z) -> region, Interpolated.of(value -> 0D, value -> null)), 0, 0));
|
||||
MantleChunk<Matter> chunk = mock(MantleChunk.class, withSettings().stubOnly());
|
||||
Mantle<Matter> mantle = engine.getMantle().getMantle();
|
||||
doReturn(chunk).when(mantle).useChunk(anyInt(), anyInt());
|
||||
when(chunk.get(anyInt(), anyInt(), anyInt(), any())).thenReturn(null);
|
||||
MultiBurst burst = new MultiBurst("Ingested deposit order", () -> 2);
|
||||
when(engine.burst()).thenReturn(burst);
|
||||
int count = 0;
|
||||
try (MockedStatic<B> blocks = mockStatic(B.class, withSettings().stubOnly().defaultAnswer(CALLS_REAL_METHODS))) {
|
||||
blocks.when(() -> B.getStateOrNull(anyString(), anyBoolean())).thenAnswer(invocation -> state(invocation.getArgument(0)));
|
||||
blocks.when(() -> B.toDeepSlateOre(any(), any())).thenAnswer(invocation -> invocation.getArgument(1));
|
||||
IrisDepositModifier modifier = new IrisDepositModifier(engine);
|
||||
for (int chunkX = 0; chunkX < CHUNKS; chunkX++) {
|
||||
Hunk<NativeBlockState> terrain = islandChunk();
|
||||
modifier.generateDeposits(terrain, chunkX, 0, false, context);
|
||||
count += count(terrain, block);
|
||||
}
|
||||
} finally {
|
||||
burst.close();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private Hunk<NativeBlockState> islandChunk() {
|
||||
Hunk<NativeBlockState> terrain = Hunk.newArrayHunk(16, HEIGHT, 16);
|
||||
for (int y = 0; y < HEIGHT; y++) {
|
||||
int worldY = y + PACK_MIN_Y;
|
||||
String key = worldY < 55 ? "minecraft:stone" : worldY < 61 ? "minecraft:dirt" : worldY == 61 ? "minecraft:grass_block" : "minecraft:air";
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
terrain.set(x, y, z, state(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
return terrain;
|
||||
}
|
||||
|
||||
private static int count(Hunk<NativeBlockState> terrain, String block) {
|
||||
int count = 0;
|
||||
for (int y = 0; y < HEIGHT; y++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
if (terrain.get(x, y, z).key().equals(block)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private NativeBlockState state(String block) {
|
||||
String key = block.contains(":") ? block : "minecraft:" + block;
|
||||
return states.computeIfAbsent(key, KeyedBlockState::new);
|
||||
}
|
||||
|
||||
private static WorldgenCatalog catalog() {
|
||||
WorldgenCatalog fixture = ModIngestFixture.catalog();
|
||||
List<CatalogBiome> biomes = new ArrayList<>(fixture.biomes());
|
||||
List<List<String>> steps = new ArrayList<>(11);
|
||||
for (int index = 0; index < 11; index++) {
|
||||
steps.add(index == 5 ? List.of(MOD + ":isles/clay_patch") : index == 7 ? List.of(MOD + ":isles/sand_beaches") : List.of());
|
||||
}
|
||||
biomes.add(new CatalogBiome(MOD + ":sand_isles", 0.7F, 0.8F, true, List.of("minecraft:is_overworld"), steps, List.of(), null));
|
||||
List<CatalogPlacedFeature> features = new ArrayList<>(fixture.placedFeatures());
|
||||
features.add(feature("isles/clay_patch", """
|
||||
{"targets": [{"target": {"block": "minecraft:sand", "predicate_type": "minecraft:block_match"}, "state": "minecraft:clay"}],
|
||||
"size": 50, "discard_chance_on_air_exposure": 0.0}
|
||||
""", """
|
||||
[{"count": 2, "type": "minecraft:count"}, {"type": "minecraft:in_square"},
|
||||
{"height": {"min_inclusive": {"absolute": 57}, "max_inclusive": {"absolute": 62}, "type": "minecraft:uniform"}, "type": "minecraft:height_range"},
|
||||
{"type": "minecraft:biome"}]
|
||||
"""));
|
||||
features.add(feature("isles/sand_beaches", """
|
||||
{"targets": [{"target": {"tag": "examplemod:soil_and_gravel", "predicate_type": "minecraft:tag_match"}, "state": "minecraft:sand"}],
|
||||
"size": 50, "discard_chance_on_air_exposure": 0.0}
|
||||
""", """
|
||||
[{"count": 6, "type": "minecraft:count"}, {"count": 5, "type": "minecraft:count"}, {"type": "minecraft:in_square"},
|
||||
{"noise_to_count_ratio": 1, "noise_factor": 275.0, "noise_offset": 0.2, "type": "minecraft:noise_based_count"},
|
||||
{"height": {"min_inclusive": {"absolute": 50}, "max_inclusive": {"absolute": 61}, "type": "minecraft:uniform"}, "type": "minecraft:height_range"},
|
||||
{"type": "minecraft:biome"}]
|
||||
"""));
|
||||
Map<String, List<String>> tags = Map.of(MOD + ":soil_and_gravel", List.of("minecraft:coarse_dirt", "minecraft:dirt", "minecraft:grass_block", "minecraft:gravel"));
|
||||
return new WorldgenCatalog(fixture.format(), fixture.minecraftVersion(), fixture.platform(), fixture.capturedAtEpochMillis(),
|
||||
fixture.contentFingerprint(), fixture.namespaces(), biomes, features, fixture.structures(), fixture.dimensionTypes(),
|
||||
fixture.levelStems(), tags);
|
||||
}
|
||||
|
||||
private static CatalogPlacedFeature feature(String path, String config, String placement) {
|
||||
String key = MOD + ":" + path;
|
||||
return new CatalogPlacedFeature(key, key, "minecraft:ore", JsonParser.parseString(config), JsonParser.parseString(placement));
|
||||
}
|
||||
|
||||
private static final class ResolvedDeposit extends IrisDepositGenerator {
|
||||
private transient KList<NativeBlockState> resolved;
|
||||
|
||||
private void bind(Map<String, NativeBlockState> states) {
|
||||
resolved = new KList<>();
|
||||
for (IrisBlockData block : getPalette()) {
|
||||
resolved.add(states.computeIfAbsent(block.getBlock(), KeyedBlockState::new));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KList<NativeBlockState> getBlockData(IrisData data) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public class IngestReservedNamespaceTest {
|
||||
biomes.add(biome("underworld:ash_fields"));
|
||||
return new WorldgenCatalog(fixture.format(), fixture.minecraftVersion(), fixture.platform(),
|
||||
fixture.capturedAtEpochMillis(), fixture.contentFingerprint(), fixture.namespaces(), biomes,
|
||||
fixture.placedFeatures(), fixture.structures(), fixture.dimensionTypes(), fixture.levelStems());
|
||||
fixture.placedFeatures(), fixture.structures(), fixture.dimensionTypes(), fixture.levelStems(), fixture.blockTags());
|
||||
}
|
||||
|
||||
private static CatalogBiome biome(String key) {
|
||||
|
||||
@@ -107,7 +107,7 @@ public class IngestTreeScopeTest {
|
||||
features.add(feature("mossy_rocks", "minecraft:forest_rock", "{\"state\": \"minecraft:mossy_cobblestone\"}"));
|
||||
return new WorldgenCatalog(fixture.format(), fixture.minecraftVersion(), fixture.platform(), fixture.capturedAtEpochMillis(),
|
||||
fixture.contentFingerprint(), fixture.namespaces(), biomes, features, fixture.structures(),
|
||||
fixture.dimensionTypes(), fixture.levelStems());
|
||||
fixture.dimensionTypes(), fixture.levelStems(), fixture.blockTags());
|
||||
}
|
||||
|
||||
private static CatalogPlacedFeature feature(String path, String type, String config) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
final class ModIngestFixture {
|
||||
@@ -316,7 +317,7 @@ final class ModIngestFixture {
|
||||
List.of(new CatalogNamespace(MOD, "Example Mod", "1.0.0"), new CatalogNamespace(OTHER_MOD, "Other Mod", "2.0.0")),
|
||||
biomes, features, structures,
|
||||
List.of(new CatalogDimensionType("minecraft:overworld", -64, 384, 384, null)),
|
||||
List.of());
|
||||
List.of(), Map.of());
|
||||
}
|
||||
|
||||
static ModIngestRequest request(Path source, Path target, Set<String> namespaces, boolean replace) {
|
||||
|
||||
@@ -216,7 +216,7 @@ public class ModIngesterPackTest {
|
||||
namespaces.add(new CatalogNamespace("quietmod", "Quiet Mod", "1.0.0"));
|
||||
WorldgenCatalog catalog = new WorldgenCatalog(fixture.format(), fixture.minecraftVersion(), fixture.platform(),
|
||||
fixture.capturedAtEpochMillis(), fixture.contentFingerprint(), namespaces, fixture.biomes(),
|
||||
fixture.placedFeatures(), fixture.structures(), fixture.dimensionTypes(), fixture.levelStems());
|
||||
fixture.placedFeatures(), fixture.structures(), fixture.dimensionTypes(), fixture.levelStems(), fixture.blockTags());
|
||||
|
||||
ModIngestReport report = ModIngester.ingest(new ModIngestRequest(source, target, "overworld", catalog, Set.of(), Set.of(), false));
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.google.gson.JsonParser;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -93,20 +94,23 @@ public class VanillaFeatureParsersTest {
|
||||
{"predicate_type": "minecraft:all_of", "rules": [
|
||||
{"predicate_type": "minecraft:not", "rule": {"predicate_type": "minecraft:tag_match", "tag": "minecraft:height_specific_ore_replaceables"}},
|
||||
{"predicate_type": "minecraft:tag_match", "tag": "minecraft:stone_ore_replaceables"}]}]}
|
||||
"""));
|
||||
"""), Map.of());
|
||||
|
||||
assertFalse(stone.any());
|
||||
assertEquals(Set.of("minecraft:tuff", "minecraft:stone", "minecraft:granite", "minecraft:diorite", "minecraft:andesite"), stone.blocks());
|
||||
assertEquals(Set.of("minecraft:netherrack"),
|
||||
VanillaRuleTests.hosts(json("{\"predicate_type\": \"minecraft:block_match\", \"block\": \"minecraft:netherrack\"}")).blocks());
|
||||
VanillaRuleTests.hosts(json("{\"predicate_type\": \"minecraft:block_match\", \"block\": \"minecraft:netherrack\"}"), Map.of()).blocks());
|
||||
assertEquals(Set.of("minecraft:end_stone"), VanillaRuleTests.hosts(json(
|
||||
"{\"predicate_type\": \"minecraft:blockstate_match\", \"block_state\": {\"Name\": \"minecraft:end_stone\"}}")).blocks());
|
||||
assertTrue(VanillaRuleTests.hosts(json("{\"predicate_type\": \"minecraft:always_true\"}")).any());
|
||||
"{\"predicate_type\": \"minecraft:blockstate_match\", \"block_state\": {\"Name\": \"minecraft:end_stone\"}}"), Map.of()).blocks());
|
||||
assertTrue(VanillaRuleTests.hosts(json("{\"predicate_type\": \"minecraft:always_true\"}"), Map.of()).any());
|
||||
assertThrows(IngestConversionException.class, () -> VanillaRuleTests.hosts(json(
|
||||
"{\"predicate_type\": \"minecraft:random_block_match\", \"block\": \"minecraft:stone\", \"probability\": 0.5}")));
|
||||
"{\"predicate_type\": \"minecraft:random_block_match\", \"block\": \"minecraft:stone\", \"probability\": 0.5}"), Map.of()));
|
||||
IngestConversionException unknownTag = assertThrows(IngestConversionException.class, () -> VanillaRuleTests.hosts(json(
|
||||
"{\"predicate_type\": \"minecraft:tag_match\", \"tag\": \"examplemod:moon_rock\"}")));
|
||||
"{\"predicate_type\": \"minecraft:tag_match\", \"tag\": \"examplemod:moon_rock\"}"), Map.of()));
|
||||
assertTrue(unknownTag.getMessage().contains("examplemod:moon_rock"));
|
||||
assertEquals(Set.of("examplemod:moon_stone"), VanillaRuleTests.hosts(json(
|
||||
"{\"predicate_type\": \"minecraft:tag_match\", \"tag\": \"examplemod:moon_rock\"}"),
|
||||
Map.of("examplemod:moon_rock", List.of("examplemod:moon_stone"))).blocks());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-2
@@ -29,6 +29,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -55,7 +56,7 @@ public class WorldgenCatalogStoreTest {
|
||||
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)));
|
||||
List.of(new CatalogLevelStem("examplemod:sky", "examplemod:sky", "minecraft:noise", "minecraft:multi_noise", null)), Map.of());
|
||||
Path file = folder.getRoot().toPath().resolve("nested").resolve(WorldgenCatalogStore.FILE_NAME);
|
||||
|
||||
WorldgenCatalogStore.write(file, catalog);
|
||||
@@ -72,7 +73,7 @@ public class WorldgenCatalogStoreTest {
|
||||
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());
|
||||
List.of(), List.of(), List.of(), List.of(), Map.of());
|
||||
WorldgenCatalogIndex index = WorldgenCatalogIndex.of(catalog);
|
||||
|
||||
assertEquals(1, index.biomesIn("examplemod").size());
|
||||
|
||||
Reference in new issue
Block a user