From 0012c3e0f2fc2f79d38df659dbdbfc6fa1c045a5 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Tue, 5 Aug 2025 14:29:23 +0200 Subject: [PATCH] add structure populator for 1.21.5-8 --- .../core/nms/v1_21_R4/IrisChunkGenerator.java | 146 +++++++++++++++--- .../core/nms/v1_21_R5/IrisChunkGenerator.java | 146 +++++++++++++++--- 2 files changed, 248 insertions(+), 44 deletions(-) diff --git a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/IrisChunkGenerator.java b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/IrisChunkGenerator.java index 28513cd77..5db2c62ca 100644 --- a/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/IrisChunkGenerator.java +++ b/nms/v1_21_R4/src/main/java/com/volmit/iris/core/nms/v1_21_R4/IrisChunkGenerator.java @@ -8,12 +8,16 @@ import com.volmit.iris.engine.framework.ResultLocator; import com.volmit.iris.engine.framework.WrongEngineBroException; import com.volmit.iris.engine.object.IrisJigsawStructure; import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.engine.object.IrisStructurePopulator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.mantle.MantleFlag; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.reflect.WrappedField; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; import net.minecraft.core.*; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; @@ -26,38 +30,48 @@ import net.minecraft.util.random.WeightedList; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.*; import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.*; import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R4.CraftWorld; import org.bukkit.craftbukkit.v1_21_R4.generator.CustomChunkGenerator; +import org.bukkit.craftbukkit.v1_21_R4.generator.structure.CraftStructure; +import org.bukkit.event.world.AsyncStructureSpawnEvent; import org.spigotmc.SpigotWorldConfig; import javax.annotation.Nullable; import java.lang.reflect.Field; +import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; public class IrisChunkGenerator extends CustomChunkGenerator { private static final WrappedField BIOME_SOURCE; private final ChunkGenerator delegate; private final Engine engine; private final KMap, KSet> structures = new KMap<>(); + private final IrisStructurePopulator populator; public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null); this.delegate = delegate; this.engine = engine; + this.populator = new IrisStructurePopulator(engine); var dimension = engine.getDimension(); KSet placements = new KSet<>(); @@ -174,8 +188,61 @@ public class IrisChunkGenerator extends CustomChunkGenerator { } @Override - public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey resourcekey) { - delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey); + public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey levelKey) { + if (!structureManager.shouldGenerateStructures()) + return; + var chunkPos = access.getPos(); + var sectionPos = SectionPos.bottomOf(access); + var registry = registryAccess.lookupOrThrow(Registries.STRUCTURE); + populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> { + var loc = ResourceLocation.tryParse(key); + if (loc == null) return false; + var holder = registry.get(loc).orElse(null); + if (holder == null) return false; + var structure = holder.value(); + var biomes = structure.biomes(); + + var start = structure.generate( + holder, + levelKey, + registryAccess, + this, + biomeSource, + structureState.randomState(), + templateManager, + structureState.getLevelSeed(), + chunkPos, + fetchReferences(structureManager, access, sectionPos, structure), + access, + biome -> ignoreBiomes || biomes.contains(biome) + ); + + if (!start.isValid()) + return false; + + BoundingBox box = start.getBoundingBox(); + AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent( + structureManager.level.getMinecraftWorld().getWorld(), + CraftStructure.minecraftToBukkit(structure), + new org.bukkit.util.BoundingBox( + box.minX(), + box.minY(), + box.minZ(), + box.maxX(), + box.maxY(), + box.maxZ() + ), chunkPos.x, chunkPos.z); + Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + structureManager.setStartForStructure(sectionPos, structure, start, access); + } + return true; + }); + } + + private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) { + StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access); + return structurestart != null ? structurestart.getReferences() : 0; } @Override @@ -208,11 +275,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator { return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess); } - @Override - public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); - } - @Override public WeightedList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); @@ -220,7 +282,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { - delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true); } @Override @@ -230,22 +292,49 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { - delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla); + addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false); } @Override - public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) { + if (!structureManager.shouldGenerateStructures()) + return; + + SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY()); + BlockPos blockPos = sectionPos.origin(); + WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); + long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ()); + var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); + var list = structures.stream() + .sorted(Comparator.comparingInt(s -> s.step().ordinal())) + .toList(); + + for (int j = 0; j < list.size(); j++) { + Structure structure = list.get(j); + random.setFeatureSeed(i, j, structure.step().ordinal()); + Supplier supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString); + + try { + level.setCurrentlyGenerating(supplier); + structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos())); + } catch (Exception exception) { + CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement"); + CrashReportCategory category = crashReport.addCategory("Feature"); + category.setDetail("Description", supplier::get); + throw new ReportedException(crashReport); + } + } } - @Override - public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate); - } - - @Override - public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { - delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); + private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) { + ChunkPos chunkPos = ichunkaccess.getPos(); + int minX = chunkPos.getMinBlockX(); + int minZ = chunkPos.getMinBlockZ(); + LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration(); + int minY = heightAccessor.getMinY() + 1; + int maxY = heightAccessor.getMaxY(); + return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15); } @Override @@ -263,9 +352,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator { return delegate.getGenDepth(); } + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1; + } + @Override public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + int block = engine.getHeight(i, j, true); + int water = engine.getHeight(i, j, false); + BlockState[] column = new BlockState[levelheightaccessor.getHeight()]; + for (int k = 0; k < column.length; k++) { + if (k <= block) column[k] = Blocks.STONE.defaultBlockState(); + else if (k <= water) column[k] = Blocks.WATER.defaultBlockState(); + else column[k] = Blocks.AIR.defaultBlockState(); + } + return new NoiseColumn(levelheightaccessor.getMinY(), column); } @Override diff --git a/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/IrisChunkGenerator.java b/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/IrisChunkGenerator.java index c156c09df..a7d4241c8 100644 --- a/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/IrisChunkGenerator.java +++ b/nms/v1_21_R5/src/main/java/com/volmit/iris/core/nms/v1_21_R5/IrisChunkGenerator.java @@ -8,12 +8,16 @@ import com.volmit.iris.engine.framework.ResultLocator; import com.volmit.iris.engine.framework.WrongEngineBroException; import com.volmit.iris.engine.object.IrisJigsawStructure; import com.volmit.iris.engine.object.IrisJigsawStructurePlacement; +import com.volmit.iris.engine.object.IrisStructurePopulator; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.mantle.MantleFlag; import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.reflect.WrappedField; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; import net.minecraft.core.*; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; @@ -26,38 +30,48 @@ import net.minecraft.util.random.WeightedList; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.*; import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.*; import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R5.CraftWorld; import org.bukkit.craftbukkit.v1_21_R5.generator.CustomChunkGenerator; +import org.bukkit.craftbukkit.v1_21_R5.generator.structure.CraftStructure; +import org.bukkit.event.world.AsyncStructureSpawnEvent; import org.spigotmc.SpigotWorldConfig; import javax.annotation.Nullable; import java.lang.reflect.Field; +import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; public class IrisChunkGenerator extends CustomChunkGenerator { private static final WrappedField BIOME_SOURCE; private final ChunkGenerator delegate; private final Engine engine; private final KMap, KSet> structures = new KMap<>(); + private final IrisStructurePopulator populator; public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null); this.delegate = delegate; this.engine = engine; + this.populator = new IrisStructurePopulator(engine); var dimension = engine.getDimension(); KSet placements = new KSet<>(); @@ -174,8 +188,61 @@ public class IrisChunkGenerator extends CustomChunkGenerator { } @Override - public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, ResourceKey resourcekey) { - delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager, resourcekey); + public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey levelKey) { + if (!structureManager.shouldGenerateStructures()) + return; + var chunkPos = access.getPos(); + var sectionPos = SectionPos.bottomOf(access); + var registry = registryAccess.lookupOrThrow(Registries.STRUCTURE); + populator.populateStructures(chunkPos.x, chunkPos.z, (key, ignoreBiomes) -> { + var loc = ResourceLocation.tryParse(key); + if (loc == null) return false; + var holder = registry.get(loc).orElse(null); + if (holder == null) return false; + var structure = holder.value(); + var biomes = structure.biomes(); + + var start = structure.generate( + holder, + levelKey, + registryAccess, + this, + biomeSource, + structureState.randomState(), + templateManager, + structureState.getLevelSeed(), + chunkPos, + fetchReferences(structureManager, access, sectionPos, structure), + access, + biome -> ignoreBiomes || biomes.contains(biome) + ); + + if (!start.isValid()) + return false; + + BoundingBox box = start.getBoundingBox(); + AsyncStructureSpawnEvent event = new AsyncStructureSpawnEvent( + structureManager.level.getMinecraftWorld().getWorld(), + CraftStructure.minecraftToBukkit(structure), + new org.bukkit.util.BoundingBox( + box.minX(), + box.minY(), + box.minZ(), + box.maxX(), + box.maxY(), + box.maxZ() + ), chunkPos.x, chunkPos.z); + Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + structureManager.setStartForStructure(sectionPos, structure, start, access); + } + return true; + }); + } + + private static int fetchReferences(StructureManager structureManager, ChunkAccess access, SectionPos sectionPos, Structure structure) { + StructureStart structurestart = structureManager.getStartForStructure(sectionPos, structure, access); + return structurestart != null ? structurestart.getReferences() : 0; } @Override @@ -208,11 +275,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator { return delegate.fillFromNoise(blender, randomstate, structuremanager, ichunkaccess); } - @Override - public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate); - } - @Override public WeightedList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); @@ -220,7 +282,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { - delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager); + applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true); } @Override @@ -230,22 +292,49 @@ public class IrisChunkGenerator extends CustomChunkGenerator { @Override public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { - delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, vanilla); + addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); + delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false); } @Override - public int getFirstFreeHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - return delegate.getFirstFreeHeight(i, j, heightmap_type, levelheightaccessor, randomstate); + public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) { + if (!structureManager.shouldGenerateStructures()) + return; + + SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY()); + BlockPos blockPos = sectionPos.origin(); + WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); + long i = random.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ()); + var structures = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); + var list = structures.stream() + .sorted(Comparator.comparingInt(s -> s.step().ordinal())) + .toList(); + + for (int j = 0; j < list.size(); j++) { + Structure structure = list.get(j); + random.setFeatureSeed(i, j, structure.step().ordinal()); + Supplier supplier = () -> structures.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString); + + try { + level.setCurrentlyGenerating(supplier); + structureManager.startsForStructure(sectionPos, structure).forEach((start) -> start.placeInChunk(level, structureManager, this, random, getWritableArea(chunkAccess), chunkAccess.getPos())); + } catch (Exception exception) { + CrashReport crashReport = CrashReport.forThrowable(exception, "Feature placement"); + CrashReportCategory category = crashReport.addCategory("Feature"); + category.setDetail("Description", supplier::get); + throw new ReportedException(crashReport); + } + } } - @Override - public int getFirstOccupiedHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - return delegate.getFirstOccupiedHeight(i, j, heightmap_type, levelheightaccessor, randomstate); - } - - @Override - public void addVanillaDecorations(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { - delegate.addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); + private static BoundingBox getWritableArea(ChunkAccess ichunkaccess) { + ChunkPos chunkPos = ichunkaccess.getPos(); + int minX = chunkPos.getMinBlockX(); + int minZ = chunkPos.getMinBlockZ(); + LevelHeightAccessor heightAccessor = ichunkaccess.getHeightAccessorForGeneration(); + int minY = heightAccessor.getMinY() + 1; + int maxY = heightAccessor.getMaxY(); + return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15); } @Override @@ -263,9 +352,22 @@ public class IrisChunkGenerator extends CustomChunkGenerator { return delegate.getGenDepth(); } + @Override + public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { + return levelheightaccessor.getMinY() + engine.getHeight(i, j, !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState())) + 1; + } + @Override public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate); + int block = engine.getHeight(i, j, true); + int water = engine.getHeight(i, j, false); + BlockState[] column = new BlockState[levelheightaccessor.getHeight()]; + for (int k = 0; k < column.length; k++) { + if (k <= block) column[k] = Blocks.STONE.defaultBlockState(); + else if (k <= water) column[k] = Blocks.WATER.defaultBlockState(); + else column[k] = Blocks.AIR.defaultBlockState(); + } + return new NoiseColumn(levelheightaccessor.getMinY(), column); } @Override