From 55fa02630a487a92cdeb34423017942bde08e5e8 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Sat, 5 Apr 2025 20:15:26 +0200 Subject: [PATCH] Add command for generating configs for datapack structures --- build.gradle | 1 + .../iris/core/commands/CommandDeveloper.java | 111 ++++++++++++++++++ .../iris/core/loader/IrisRegistrant.java | 2 + .../com/volmit/iris/core/nms/INMSBinding.java | 4 + .../nms/container/StructurePlacement.java | 77 ++++++++++++ .../iris/core/nms/v1X/NMSBinding1X.java | 7 ++ .../NullableDimensionHandler.java | 88 ++++++++++++++ core/src/main/resources/plugin.yml | 1 + .../iris/core/nms/v1_20_R1/NMSBinding.java | 63 ++++++++++ .../iris/core/nms/v1_20_R2/NMSBinding.java | 63 ++++++++++ .../iris/core/nms/v1_20_R3/NMSBinding.java | 63 ++++++++++ .../iris/core/nms/v1_20_R4/NMSBinding.java | 63 ++++++++++ .../iris/core/nms/v1_21_R1/NMSBinding.java | 63 ++++++++++ .../iris/core/nms/v1_21_R2/NMSBinding.java | 63 ++++++++++ .../iris/core/nms/v1_21_R3/NMSBinding.java | 84 +++++++++++-- 15 files changed, 745 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/core/nms/container/StructurePlacement.java create mode 100644 core/src/main/java/com/volmit/iris/util/decree/specialhandlers/NullableDimensionHandler.java diff --git a/build.gradle b/build.gradle index da1f2c218..46037f9ca 100644 --- a/build.gradle +++ b/build.gradle @@ -152,6 +152,7 @@ allprojects { compileOnly 'com.github.ben-manes.caffeine:caffeine:3.0.6' compileOnly 'org.apache.commons:commons-lang3:3.12.0' compileOnly 'com.github.oshi:oshi-core:6.6.5' + compileOnly 'org.apache.commons:commons-math3:3.6.1' } /** diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index 9b4203490..3f286ba5b 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -18,19 +18,25 @@ package com.volmit.iris.core.commands; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; import com.volmit.iris.Iris; import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.service.IrisEngineSVC; import com.volmit.iris.core.tools.IrisPackBenchmarking; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; import com.volmit.iris.util.decree.annotations.Decree; import com.volmit.iris.util.decree.annotations.Param; +import com.volmit.iris.util.decree.specialhandlers.NullableDimensionHandler; import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.io.CountingDataInputStream; @@ -40,6 +46,7 @@ import com.volmit.iris.util.nbt.mca.MCAFile; import com.volmit.iris.util.nbt.mca.MCAUtil; import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.plugin.VolmitSender; +import lombok.SneakyThrows; import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; import net.jpountz.lz4.LZ4FrameInputStream; @@ -56,6 +63,8 @@ import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -136,6 +145,108 @@ public class CommandDeveloper implements DecreeExecutor { } + @SneakyThrows + @Decree(description = "Generate Iris structures for all loaded datapack structures") + public void generateStructures( + @Param(description = "The pack to add the generated structures to", aliases = "pack", defaultValue = "---", customHandler = NullableDimensionHandler.class) + IrisDimension dimension, + @Param(description = "Ignore existing structures", defaultValue = "false") + boolean force + ) { + var map = INMS.get().collectStructures(); + if (map.isEmpty()) { + sender().sendMessage(C.IRIS + "No structures found"); + return; + } + + sender().sendMessage(C.IRIS + "Found " + map.size() + " structures"); + + IrisData data; + Set existingStructures; + File dimensionFile; + File structuresFolder; + + var dimensionObj = new JsonObject(); + + if (dimension == null) { + File dataDir = Iris.instance.getDataFolder("structures"); + IO.delete(dataDir); + data = IrisData.get(dataDir); + structuresFolder = new File(dataDir, "jigsaw-structures"); + existingStructures = Set.of(); + dimensionFile = new File(dataDir, "structures.json"); + } else { + data = dimension.getLoader(); + existingStructures = Stream.concat( + dimension.getJigsawStructures().stream().map(IrisJigsawStructurePlacement::getStructure), + Arrays.stream(data.getJigsawStructureLoader().getPossibleKeys())) + .collect(Collectors.toSet()); + + dimensionObj = data.getGson().fromJson(IO.readAll(dimension.getLoadFile()), JsonObject.class); + dimensionFile = dimension.getLoadFile(); + structuresFolder = new File(data.getDataFolder(), "jigsaw-structures"); + } + + var gson = data.getGson(); + var jigsawStructures = Optional.ofNullable(dimensionObj.getAsJsonArray("jigsawStructures")) + .orElse(new JsonArray(map.size())); + + map.forEach((key, placement) -> { + String loadKey = "datapack/" + key.namespace() + "/" + key.key(); + if (existingStructures.contains(loadKey) && !force) + return; + + var structures = placement.structures(); + var obj = placement.toJson(loadKey); + if (obj == null || structures.isEmpty()) { + sender().sendMessage(C.RED + "Failed to generate hook for " + key); + return; + } + + String structureKey; + if (structures.size() > 1) { + KList common = new KList<>(); + for (int i = 0; i < structures.size(); i++) { + var tags = structures.get(i).tags(); + if (i == 0) common.addAll(tags); + else common.removeIf(tag -> !tags.contains(tag)); + } + structureKey = common.isNotEmpty() ? common.getFirst() : structures.getFirst().key(); + } else structureKey = structures.getFirst().key(); + + JsonArray array = new JsonArray(); + if (structures.size() > 1) { + structures.stream() + .flatMap(structure -> { + String[] arr = new String[structure.weight()]; + Arrays.fill(arr, structure.key()); + return Arrays.stream(arr); + }) + .forEach(array::add); + } else array.add(structureKey); + + jigsawStructures.asList().removeIf(e -> e.getAsJsonObject().get("structure").getAsString().equals(loadKey)); + jigsawStructures.add(obj); + + obj = new JsonObject(); + obj.addProperty("structureKey", structureKey); + obj.add("datapackStructures", array); + + File out = new File(structuresFolder, loadKey + ".json"); + out.getParentFile().mkdirs(); + try { + IO.writeAll(out, gson.toJson(obj)); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + dimensionObj.add("jigsawStructures", jigsawStructures); + IO.writeAll(dimensionFile, gson.toJson(dimensionObj)); + + data.hotloaded(); + } + @Decree(description = "Test") public void packBenchmark( @Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld") diff --git a/core/src/main/java/com/volmit/iris/core/loader/IrisRegistrant.java b/core/src/main/java/com/volmit/iris/core/loader/IrisRegistrant.java index eabba9d1c..8dd218916 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/IrisRegistrant.java +++ b/core/src/main/java/com/volmit/iris/core/loader/IrisRegistrant.java @@ -28,6 +28,7 @@ import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.plugin.VolmitSender; import lombok.Data; +import lombok.EqualsAndHashCode; import java.awt.*; import java.io.File; @@ -39,6 +40,7 @@ public abstract class IrisRegistrant { @ArrayType(min = 1, type = String.class) private KList preprocessors = new KList<>(); + @EqualsAndHashCode.Exclude private transient IrisData loader; private transient String loadKey; diff --git a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java index 6945e2e6c..8b75a6b58 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java +++ b/core/src/main/java/com/volmit/iris/core/nms/INMSBinding.java @@ -18,9 +18,11 @@ package com.volmit.iris.core.nms; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.Pair; +import com.volmit.iris.core.nms.container.StructurePlacement; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; @@ -137,4 +139,6 @@ public interface INMSBinding { boolean missingDimensionTypes(boolean overworld, boolean nether, boolean end); void placeStructures(Chunk chunk); + + KMap collectStructures(); } diff --git a/core/src/main/java/com/volmit/iris/core/nms/container/StructurePlacement.java b/core/src/main/java/com/volmit/iris/core/nms/container/StructurePlacement.java new file mode 100644 index 000000000..173251ddc --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/nms/container/StructurePlacement.java @@ -0,0 +1,77 @@ +package com.volmit.iris.core.nms.container; + +import com.google.gson.JsonObject; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement.SpreadType; +import lombok.*; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; +import org.apache.commons.math3.fraction.Fraction; + +import java.util.List; + +@Data +@SuperBuilder +@Accessors(fluent = true, chain = true) +public abstract class StructurePlacement { + private final int salt; + private final float frequency; + private final List structures; + + public abstract JsonObject toJson(String structure); + + protected JsonObject createBase(String structure) { + JsonObject object = new JsonObject(); + object.addProperty("structure", structure); + object.addProperty("salt", salt); + return object; + } + + public int frequencyToSpacing() { + var frac = new Fraction(Math.max(Math.min(frequency, 1), 0.000000001f)); + return (int) Math.round(Math.sqrt((double) frac.getDenominator() / frac.getNumerator())); + } + + @Getter + @Accessors(chain = true, fluent = true) + @EqualsAndHashCode(callSuper = true) + @SuperBuilder + public static class RandomSpread extends StructurePlacement { + private final int spacing; + private final int separation; + private final SpreadType spreadType; + + @Override + public JsonObject toJson(String structure) { + JsonObject object = createBase(structure); + object.addProperty("spacing", Math.max(spacing, frequencyToSpacing())); + object.addProperty("separation", separation); + object.addProperty("spreadType", spreadType.name()); + return object; + } + } + + @Getter + @EqualsAndHashCode(callSuper = true) + @SuperBuilder + public static class ConcentricRings extends StructurePlacement { + private final int distance; + private final int spread; + private final int count; + + @Override + public JsonObject toJson(String structure) { + return null; + } + } + + public record Structure( + int weight, + String key, + List tags + ) { + + public boolean isValid() { + return weight > 0 && key != null; + } + } +} diff --git a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java index 74c6a316b..a66ab2c2a 100644 --- a/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/com/volmit/iris/core/nms/v1X/NMSBinding1X.java @@ -19,10 +19,12 @@ package com.volmit.iris.core.nms.v1X; import com.volmit.iris.Iris; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.Pair; +import com.volmit.iris.core.nms.container.StructurePlacement; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; @@ -140,6 +142,11 @@ public class NMSBinding1X implements INMSBinding { } + @Override + public KMap collectStructures() { + return new KMap<>(); + } + @Override public CompoundTag serializeEntity(Entity location) { return null; diff --git a/core/src/main/java/com/volmit/iris/util/decree/specialhandlers/NullableDimensionHandler.java b/core/src/main/java/com/volmit/iris/util/decree/specialhandlers/NullableDimensionHandler.java new file mode 100644 index 000000000..706dd61a5 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/decree/specialhandlers/NullableDimensionHandler.java @@ -0,0 +1,88 @@ +/* + * 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 com.volmit.iris.util.decree.specialhandlers; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.engine.object.IrisDimension; +import com.volmit.iris.util.collection.KList; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.decree.DecreeParameterHandler; +import com.volmit.iris.util.decree.exceptions.DecreeParsingException; + +import java.io.File; + +public class NullableDimensionHandler implements DecreeParameterHandler { + @Override + public KList getPossibilities() { + KMap p = new KMap<>(); + + //noinspection ConstantConditions + for (File i : Iris.instance.getDataFolder("packs").listFiles()) { + if (i.isDirectory()) { + IrisData data = IrisData.get(i); + for (IrisDimension j : data.getDimensionLoader().loadAll(data.getDimensionLoader().getPossibleKeys())) { + p.putIfAbsent(j.getLoadKey(), j); + } + + data.close(); + } + } + + return p.v(); + } + + @Override + public String toString(IrisDimension dim) { + return dim.getLoadKey(); + } + + @Override + public IrisDimension parse(String in, boolean force) throws DecreeParsingException { + if (in.equalsIgnoreCase("---")) + return null; + + if (in.equalsIgnoreCase("default")) { + return parse(IrisSettings.get().getGenerator().getDefaultWorldType()); + } + + KList options = getPossibilities(in); + + + if (options.isEmpty()) { + throw new DecreeParsingException("Unable to find Dimension \"" + in + "\""); + } + try { + return options.stream().filter((i) -> toString(i).equalsIgnoreCase(in)).toList().get(0); + } catch (Throwable e) { + throw new DecreeParsingException("Unable to filter which Dimension \"" + in + "\""); + } + } + + @Override + public boolean supports(Class type) { + return type.equals(IrisDimension.class); + } + + @Override + public String getRandomDefault() { + return "dimension"; + } +} diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 7ff73ff7e..a7f3ce21f 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -20,6 +20,7 @@ libraries: - bsf:bsf:2.4.0 - org.lz4:lz4-java:1.8.0 - com.github.oshi:oshi-core:6.6.5 + - org.apache.commons:commons-math3:3.6.1 commands: iris: aliases: [ ir, irs ] diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java index daae97c27..9b549c77d 100644 --- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java +++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/NMSBinding.java @@ -4,11 +4,14 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.StructurePlacement; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.hunk.Hunk; @@ -52,6 +55,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -88,6 +93,7 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import java.util.stream.Collectors; public class NMSBinding implements INMSBinding { @@ -695,6 +701,63 @@ public class NMSBinding implements INMSBinding { level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager()); } + @Override + public KMap collectStructures() { + var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET); + var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT); + return structureSets.registryKeySet() + .stream() + .map(structureSets::getHolder) + .filter(Optional::isPresent) + .map(Optional::get) + .map(holder -> { + var set = holder.value(); + var placement = set.placement(); + var key = holder.key().location(); + StructurePlacement.StructurePlacementBuilder builder; + if (placement instanceof RandomSpreadStructurePlacement random) { + builder = StructurePlacement.RandomSpread.builder() + .separation(random.separation()) + .spacing(random.spacing()) + .spreadType(switch (random.spreadType()) { + case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR; + case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR; + }); + } else if (placement instanceof ConcentricRingsStructurePlacement rings) { + builder = StructurePlacement.ConcentricRings.builder() + .distance(rings.distance()) + .spread(rings.spread()) + .count(rings.count()); + } else { + Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type())); + return null; + } + + return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder + .salt(placement.salt) + .frequency(placement.frequency) + .structures(set.structures() + .stream() + .map(entry -> new StructurePlacement.Structure( + entry.weight(), + entry.structure() + .unwrapKey() + .map(ResourceKey::location) + .map(ResourceLocation::toString) + .orElse(null), + entry.structure().tags() + .map(TagKey::location) + .map(ResourceLocation::toString) + .toList() + )) + .filter(StructurePlacement.Structure::isValid) + .toList()) + .build()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new)); + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java index 28a06c3a6..cf12faaac 100644 --- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java +++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/NMSBinding.java @@ -12,11 +12,15 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import java.util.stream.Collectors; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.StructurePlacement; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; import net.minecraft.core.*; @@ -36,6 +40,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -696,6 +702,63 @@ public class NMSBinding implements INMSBinding { level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager()); } + @Override + public KMap collectStructures() { + var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET); + var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT); + return structureSets.registryKeySet() + .stream() + .map(structureSets::getHolder) + .filter(Optional::isPresent) + .map(Optional::get) + .map(holder -> { + var set = holder.value(); + var placement = set.placement(); + var key = holder.key().location(); + StructurePlacement.StructurePlacementBuilder builder; + if (placement instanceof RandomSpreadStructurePlacement random) { + builder = StructurePlacement.RandomSpread.builder() + .separation(random.separation()) + .spacing(random.spacing()) + .spreadType(switch (random.spreadType()) { + case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR; + case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR; + }); + } else if (placement instanceof ConcentricRingsStructurePlacement rings) { + builder = StructurePlacement.ConcentricRings.builder() + .distance(rings.distance()) + .spread(rings.spread()) + .count(rings.count()); + } else { + Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type())); + return null; + } + + return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder + .salt(placement.salt) + .frequency(placement.frequency) + .structures(set.structures() + .stream() + .map(entry -> new StructurePlacement.Structure( + entry.weight(), + entry.structure() + .unwrapKey() + .map(ResourceKey::location) + .map(ResourceLocation::toString) + .orElse(null), + entry.structure().tags() + .map(TagKey::location) + .map(ResourceLocation::toString) + .toList() + )) + .filter(StructurePlacement.Structure::isValid) + .toList()) + .build()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new)); + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java index 9422ea640..2d766197f 100644 --- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java +++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/NMSBinding.java @@ -12,11 +12,15 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import java.util.stream.Collectors; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.StructurePlacement; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; import net.minecraft.core.*; @@ -36,6 +40,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -697,6 +703,63 @@ public class NMSBinding implements INMSBinding { level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager()); } + @Override + public KMap collectStructures() { + var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET); + var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT); + return structureSets.registryKeySet() + .stream() + .map(structureSets::getHolder) + .filter(Optional::isPresent) + .map(Optional::get) + .map(holder -> { + var set = holder.value(); + var placement = set.placement(); + var key = holder.key().location(); + StructurePlacement.StructurePlacementBuilder builder; + if (placement instanceof RandomSpreadStructurePlacement random) { + builder = StructurePlacement.RandomSpread.builder() + .separation(random.separation()) + .spacing(random.spacing()) + .spreadType(switch (random.spreadType()) { + case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR; + case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR; + }); + } else if (placement instanceof ConcentricRingsStructurePlacement rings) { + builder = StructurePlacement.ConcentricRings.builder() + .distance(rings.distance()) + .spread(rings.spread()) + .count(rings.count()); + } else { + Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type())); + return null; + } + + return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder + .salt(placement.salt) + .frequency(placement.frequency) + .structures(set.structures() + .stream() + .map(entry -> new StructurePlacement.Structure( + entry.weight(), + entry.structure() + .unwrapKey() + .map(ResourceKey::location) + .map(ResourceLocation::toString) + .orElse(null), + entry.structure().tags() + .map(TagKey::location) + .map(ResourceLocation::toString) + .toList() + )) + .filter(StructurePlacement.Structure::isValid) + .toList()) + .build()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new)); + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java index 372ccf4e0..e4ee8c4f5 100644 --- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java +++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/NMSBinding.java @@ -8,12 +8,16 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import java.util.stream.Collectors; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.StructurePlacement; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; import com.volmit.iris.util.nbt.tag.CompoundTag; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; @@ -48,6 +52,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -722,6 +728,63 @@ public class NMSBinding implements INMSBinding { level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager()); } + @Override + public KMap collectStructures() { + var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET); + var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT); + return structureSets.keySet() + .stream() + .map(structureSets::getHolder) + .filter(Optional::isPresent) + .map(Optional::get) + .map(holder -> { + var set = holder.value(); + var placement = set.placement(); + var key = holder.key().location(); + StructurePlacement.StructurePlacementBuilder builder; + if (placement instanceof RandomSpreadStructurePlacement random) { + builder = StructurePlacement.RandomSpread.builder() + .separation(random.separation()) + .spacing(random.spacing()) + .spreadType(switch (random.spreadType()) { + case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR; + case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR; + }); + } else if (placement instanceof ConcentricRingsStructurePlacement rings) { + builder = StructurePlacement.ConcentricRings.builder() + .distance(rings.distance()) + .spread(rings.spread()) + .count(rings.count()); + } else { + Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type())); + return null; + } + + return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder + .salt(placement.salt) + .frequency(placement.frequency) + .structures(set.structures() + .stream() + .map(entry -> new StructurePlacement.Structure( + entry.weight(), + entry.structure() + .unwrapKey() + .map(ResourceKey::location) + .map(ResourceLocation::toString) + .orElse(null), + entry.structure().tags() + .map(TagKey::location) + .map(ResourceLocation::toString) + .toList() + )) + .filter(StructurePlacement.Structure::isValid) + .toList()) + .build()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new)); + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java index 0ecba1ad4..d58e5940c 100644 --- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java +++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/NMSBinding.java @@ -12,12 +12,16 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import java.util.stream.Collectors; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; +import com.volmit.iris.core.nms.container.StructurePlacement; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; import net.minecraft.core.*; @@ -42,6 +46,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -726,6 +732,63 @@ public class NMSBinding implements INMSBinding { level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager()); } + @Override + public KMap collectStructures() { + var structureSets = registry().registryOrThrow(Registries.STRUCTURE_SET); + var structurePlacements = registry().registryOrThrow(Registries.STRUCTURE_PLACEMENT); + return structureSets.keySet() + .stream() + .map(structureSets::getHolder) + .filter(Optional::isPresent) + .map(Optional::get) + .map(holder -> { + var set = holder.value(); + var placement = set.placement(); + var key = holder.key().location(); + StructurePlacement.StructurePlacementBuilder builder; + if (placement instanceof RandomSpreadStructurePlacement random) { + builder = StructurePlacement.RandomSpread.builder() + .separation(random.separation()) + .spacing(random.spacing()) + .spreadType(switch (random.spreadType()) { + case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR; + case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR; + }); + } else if (placement instanceof ConcentricRingsStructurePlacement rings) { + builder = StructurePlacement.ConcentricRings.builder() + .distance(rings.distance()) + .spread(rings.spread()) + .count(rings.count()); + } else { + Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type())); + return null; + } + + return new com.volmit.iris.core.nms.container.Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder + .salt(placement.salt) + .frequency(placement.frequency) + .structures(set.structures() + .stream() + .map(entry -> new StructurePlacement.Structure( + entry.weight(), + entry.structure() + .unwrapKey() + .map(ResourceKey::location) + .map(ResourceLocation::toString) + .orElse(null), + entry.structure().tags() + .map(TagKey::location) + .map(ResourceLocation::toString) + .toList() + )) + .filter(StructurePlacement.Structure::isValid) + .toList()) + .build()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(com.volmit.iris.core.nms.container.Pair::getA, com.volmit.iris.core.nms.container.Pair::getB, (a, b) -> a, KMap::new)); + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java index 2071682df..305a77139 100644 --- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java +++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/NMSBinding.java @@ -8,12 +8,16 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import java.util.stream.Collectors; import com.mojang.serialization.Lifecycle; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.Pair; +import com.volmit.iris.core.nms.container.StructurePlacement; import com.volmit.iris.core.nms.datapack.DataVersion; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; import com.volmit.iris.util.scheduling.J; import lombok.SneakyThrows; import net.minecraft.core.*; @@ -37,6 +41,8 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -716,6 +722,63 @@ public class NMSBinding implements INMSBinding { level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager()); } + @Override + public KMap collectStructures() { + var structureSets = registry().lookupOrThrow(Registries.STRUCTURE_SET); + var structurePlacements = registry().lookupOrThrow(Registries.STRUCTURE_PLACEMENT); + return structureSets.keySet() + .stream() + .map(structureSets::get) + .filter(Optional::isPresent) + .map(Optional::get) + .map(holder -> { + var set = holder.value(); + var placement = set.placement(); + var key = holder.key().location(); + StructurePlacement.StructurePlacementBuilder builder; + if (placement instanceof RandomSpreadStructurePlacement random) { + builder = StructurePlacement.RandomSpread.builder() + .separation(random.separation()) + .spacing(random.spacing()) + .spreadType(switch (random.spreadType()) { + case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR; + case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR; + }); + } else if (placement instanceof ConcentricRingsStructurePlacement rings) { + builder = StructurePlacement.ConcentricRings.builder() + .distance(rings.distance()) + .spread(rings.spread()) + .count(rings.count()); + } else { + Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type())); + return null; + } + + return new Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder + .salt(placement.salt) + .frequency(placement.frequency) + .structures(set.structures() + .stream() + .map(entry -> new StructurePlacement.Structure( + entry.weight(), + entry.structure() + .unwrapKey() + .map(ResourceKey::location) + .map(ResourceLocation::toString) + .orElse(null), + entry.structure().tags() + .map(TagKey::location) + .map(ResourceLocation::toString) + .toList() + )) + .filter(StructurePlacement.Structure::isValid) + .toList()) + .build()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Pair::getA, Pair::getB, (a, b) -> a, KMap::new)); + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(), diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java index 5f8c204bb..552ccdb6d 100644 --- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java +++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/NMSBinding.java @@ -3,13 +3,17 @@ package com.volmit.iris.core.nms.v1_21_R3; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.serialization.Lifecycle; import com.volmit.iris.Iris; +import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.container.AutoClosing; import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.Pair; +import com.volmit.iris.core.nms.container.StructurePlacement; import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.framework.Engine; +import com.volmit.iris.engine.object.IrisJigsawStructure; +import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.hunk.Hunk; @@ -54,6 +58,9 @@ import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.FlatLevelSource; import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; import org.bukkit.*; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -78,9 +85,13 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class NMSBinding implements INMSBinding { private final KMap baseBiomeCache = new KMap<>(); @@ -168,14 +179,14 @@ public class NMSBinding implements INMSBinding { } @Contract(value = "null, _, _ -> null", pure = true) - private Object convertFromTag(net.minecraft.nbt.Tag tag, int depth, int maxDepth) { + private Object convertFromTag(Tag tag, int depth, int maxDepth) { if (tag == null || depth > maxDepth) return null; return switch (tag) { case CollectionTag collection -> { KList list = new KList<>(); for (Object i : collection) { - if (i instanceof net.minecraft.nbt.Tag t) + if (i instanceof Tag t) list.add(convertFromTag(t, depth + 1, maxDepth)); else list.add(i); } @@ -232,7 +243,7 @@ public class NMSBinding implements INMSBinding { yield tag; } case List list -> { - var tag = new net.minecraft.nbt.ListTag(); + var tag = new ListTag(); for (var i : list) { tag.add(convertToTag(i, depth + 1, maxDepth)); } @@ -486,13 +497,13 @@ public class NMSBinding implements INMSBinding { @Override public MCAPaletteAccess createPalette() { MCAIdMapper registry = registryCache.aquireNasty(() -> { - Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId"); - Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT"); - Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId"); + Field cf = IdMapper.class.getDeclaredField("tToId"); + Field df = IdMapper.class.getDeclaredField("idToT"); + Field bf = IdMapper.class.getDeclaredField("nextId"); cf.setAccessible(true); df.setAccessible(true); bf.setAccessible(true); - net.minecraft.core.IdMapper blockData = Block.BLOCK_STATE_REGISTRY; + IdMapper blockData = Block.BLOCK_STATE_REGISTRY; int b = bf.getInt(blockData); Object2IntMap c = (Object2IntMap) cf.get(blockData); List d = (List) df.get(blockData); @@ -590,7 +601,7 @@ public class NMSBinding implements INMSBinding { } @Override - public java.awt.Color getBiomeColor(Location location, BiomeColor type) { + public Color getBiomeColor(Location location, BiomeColor type) { LevelReader reader = ((CraftWorld) location.getWorld()).getHandle(); var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); var biome = holder.value(); @@ -715,6 +726,63 @@ public class NMSBinding implements INMSBinding { level.getChunkSource().getGenerator().applyBiomeDecoration(level, access, level.structureManager()); } + @Override + public KMap collectStructures() { + var structureSets = registry().lookupOrThrow(Registries.STRUCTURE_SET); + var structurePlacements = registry().lookupOrThrow(Registries.STRUCTURE_PLACEMENT); + return structureSets.keySet() + .stream() + .map(structureSets::get) + .filter(Optional::isPresent) + .map(Optional::get) + .map(holder -> { + var set = holder.value(); + var placement = set.placement(); + var key = holder.key().location(); + StructurePlacement.StructurePlacementBuilder builder; + if (placement instanceof RandomSpreadStructurePlacement random) { + builder = StructurePlacement.RandomSpread.builder() + .separation(random.separation()) + .spacing(random.spacing()) + .spreadType(switch (random.spreadType()) { + case LINEAR -> IrisJigsawStructurePlacement.SpreadType.LINEAR; + case TRIANGULAR -> IrisJigsawStructurePlacement.SpreadType.TRIANGULAR; + }); + } else if (placement instanceof ConcentricRingsStructurePlacement rings) { + builder = StructurePlacement.ConcentricRings.builder() + .distance(rings.distance()) + .spread(rings.spread()) + .count(rings.count()); + } else { + Iris.warn("Unsupported structure placement for set " + key + " with type " + structurePlacements.getKey(placement.type())); + return null; + } + + return new Pair<>(new Identifier(key.getNamespace(), key.getPath()), builder + .salt(placement.salt) + .frequency(placement.frequency) + .structures(set.structures() + .stream() + .map(entry -> new StructurePlacement.Structure( + entry.weight(), + entry.structure() + .unwrapKey() + .map(ResourceKey::location) + .map(ResourceLocation::toString) + .orElse(null), + entry.structure().tags() + .map(TagKey::location) + .map(ResourceLocation::toString) + .toList() + )) + .filter(StructurePlacement.Structure::isValid) + .toList()) + .build()); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Pair::getA, Pair::getB, (a,b) -> a, KMap::new)); + } + private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) { return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext( old.resources(),