diff --git a/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/NMSChunkGeneratorDelegate.java b/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/NMSChunkGeneratorDelegate.java index cd3cf0ac0..b95a7f53c 100644 --- a/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/NMSChunkGeneratorDelegate.java +++ b/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/NMSChunkGeneratorDelegate.java @@ -1,12 +1,20 @@ package com.dfsek.terra.bukkit.nms.v1_19_R1; +import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.SectionPos; import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.NoiseColumn; import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; @@ -15,20 +23,33 @@ import net.minecraft.world.level.levelgen.GenerationStep.Carving; import net.minecraft.world.level.levelgen.Heightmap.Types; import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.minecraft.world.level.levelgen.structure.StructureSet.StructureSelectionEntry; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.stream.Stream; import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.util.generic.Lazy; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.info.WorldProperties; public class NMSChunkGeneratorDelegate extends ChunkGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class); private final com.dfsek.terra.api.world.chunk.generation.ChunkGenerator delegate; private final ChunkGenerator vanilla; @@ -43,7 +64,7 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator { this.pack = pack; this.seed = seed; } - + @Override protected @NotNull Codec codec() { return ChunkGenerator.CODEC; @@ -56,12 +77,14 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator { } @Override - public void buildSurface(@NotNull WorldGenRegion region, @NotNull StructureManager structures, @NotNull RandomState noiseConfig, @NotNull ChunkAccess chunk) { + public void buildSurface(@NotNull WorldGenRegion region, @NotNull StructureManager structures, @NotNull RandomState noiseConfig, + @NotNull ChunkAccess chunk) { // no-op } @Override - public void applyBiomeDecoration(@NotNull WorldGenLevel world, @NotNull ChunkAccess chunk, @NotNull StructureManager structureAccessor) { + public void applyBiomeDecoration(@NotNull WorldGenLevel world, @NotNull ChunkAccess chunk, + @NotNull StructureManager structureAccessor) { vanilla.applyBiomeDecoration(world, chunk, structureAccessor); } @@ -76,7 +99,8 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator { } @Override - public @NotNull CompletableFuture fillFromNoise(@NotNull Executor executor, @NotNull Blender blender, @NotNull RandomState noiseConfig, + public @NotNull CompletableFuture fillFromNoise(@NotNull Executor executor, @NotNull Blender blender, + @NotNull RandomState noiseConfig, @NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) { return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk); } @@ -119,4 +143,99 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator { public void addDebugScreenInfo(@NotNull List text, @NotNull RandomState noiseConfig, @NotNull BlockPos pos) { } + + private volatile boolean rings = false; + private final Map>> ringPositions = new Object2ObjectArrayMap<>(); + + @Override + public void ensureStructuresGenerated(@NotNull RandomState noiseConfig) { + if(!this.rings) { + super.ensureStructuresGenerated(noiseConfig); + this.populateStrongholdData(noiseConfig); + this.rings = true; + } + + } + + @Override + public List getRingPositionsFor(@NotNull ConcentricRingsStructurePlacement structurePlacement, @NotNull RandomState noiseConfig) { + ensureStructuresGenerated(noiseConfig); + return ringPositions.get(structurePlacement).value(); + } + + private void populateStrongholdData(RandomState noiseConfig) { + LOGGER.info("Generating safe stronghold data. This may take up to a minute."); + Set> set = this.biomeSource.possibleBiomes(); + possibleStructureSets().map(Holder::value).forEach((holder) -> { + boolean match = false; + for(StructureSelectionEntry structureset_a : holder.structures()) { + Structure structure = structureset_a.structure().value(); + Stream> stream = structure.biomes().stream(); + if(stream.anyMatch(set::contains)) { + match = true; + } + } + + if (match) { + if (holder.placement() instanceof ConcentricRingsStructurePlacement concentricringsstructureplacement) { + this.ringPositions.put(concentricringsstructureplacement, Lazy.lazy(() -> this.generateRingPositions(holder, noiseConfig, concentricringsstructureplacement))); + } + } + }); + } + private List generateRingPositions(StructureSet holder, RandomState randomstate, + ConcentricRingsStructurePlacement concentricringsstructureplacement) { // Spigot + if(concentricringsstructureplacement.count() == 0) { + return List.of(); + } + + List list = new ArrayList<>(); + int i = concentricringsstructureplacement.distance(); + int j = concentricringsstructureplacement.count(); + int k = concentricringsstructureplacement.spread(); + HolderSet holderset = concentricringsstructureplacement.preferredBiomes(); + RandomSource randomsource = RandomSource.create(); + + if(this.conf.strongholdSeed != null && this.structureSets.getResourceKey(holder).orElse(null) == + net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS) { + randomsource.setSeed(this.conf.strongholdSeed); + } else { + randomsource.setSeed(randomstate.legacyLevelSeed()); + } + double d0 = randomsource.nextDouble() * 3.141592653589793D * 2.0D; + int l = 0; + int i1 = 0; + + for(int j1 = 0; j1 < j; ++j1) { + double d1 = (double) (4 * i + i * i1 * 6) + (randomsource.nextDouble() - 0.5D) * (double) i * 2.5D; + int k1 = (int) Math.round(Math.cos(d0) * d1); + int l1 = (int) Math.round(Math.sin(d0) * d1); + int i2 = SectionPos.sectionToBlockCoord(k1, 8); + int j2 = SectionPos.sectionToBlockCoord(l1, 8); + + Objects.requireNonNull(holderset); + Pair> pair = this.biomeSource.findBiomeHorizontal(i2, 0, j2, 112, holderset::contains, randomsource, + randomstate.sampler()); + + if(pair != null) { + BlockPos blockposition = pair.getFirst(); + + k1 = SectionPos.blockToSectionCoord(blockposition.getX()); + l1 = SectionPos.blockToSectionCoord(blockposition.getZ()); + } + + list.add(new ChunkPos(k1, l1)); + d0 += 6.283185307179586D / (double) k; + ++l; + if(l == k) { + ++i1; + l = 0; + k += 2 * k / (i1 + 1); + k = Math.min(k, j - j1); + d0 += randomsource.nextDouble() * 3.141592653589793D * 2.0D; + } + } + + return list; + } }