From b8ff4a308437d10b00c943ee9c752765aac25a05 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Thu, 11 Jun 2026 21:15:50 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E8=83=BD=E7=94=9F=E6=88=90=E4=B8=9C?= =?UTF-8?q?=E8=A5=BF=20=E4=B8=BB=E4=B8=96=E7=95=8C=E4=B8=80=E6=A0=B7?= =?UTF-8?q?=E7=9A=84=E5=9C=B0=E7=89=A9=E5=92=8C=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldgen/PSVoidChunkGenerator.java | 255 +++++++++++++----- .../worldgen/world_preset/poopsky.json | 6 +- 2 files changed, 192 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java b/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java index f289ca30..0a1ef2d3 100644 --- a/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java +++ b/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java @@ -3,9 +3,11 @@ import com.altnoir.poopsky.Config; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.WorldGenRegion; import net.minecraft.tags.TagKey; @@ -15,11 +17,16 @@ import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.biome.BiomeSource; +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.*; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.structure.StructureSet; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; @@ -27,26 +34,54 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; public class PSVoidChunkGenerator extends NoiseBasedChunkGenerator { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( BiomeSource.CODEC.fieldOf("biome_source").forGetter(generator -> generator.biomeSource), NoiseGeneratorSettings.CODEC.fieldOf("settings").forGetter(generator -> generator.settings), - TagKey.codec(Registries.STRUCTURE_SET).fieldOf("allowed_structure_sets").forGetter(generator -> generator.allowedStructureSets) + TagKey.codec(Registries.STRUCTURE_SET) + .optionalFieldOf("allowed_structure_sets") + .forGetter(generator -> generator.ignoredAllowedStructureSets) ).apply(instance, instance.stable(PSVoidChunkGenerator::new))); + /* + * 给结构算法看的“假地表高度”。 + * + * 不建议用 getMinBuildHeight() + 4,因为 1.18+ 主世界最低高度是 -64, + * 那样结构会出现在 -60 附近,你很容易以为没生成。 + * + * 这里放在 64,结构会在虚空中正常可见。 + */ + private static final int VIRTUAL_SURFACE_Y = 64; + private final Holder settings; - private final TagKey allowedStructureSets; + + /* + * 只为了兼容旧 JSON。 + * 不再使用这个字段过滤结构,否则标签不存在时结构会被清空。 + */ + private final Optional> ignoredAllowedStructureSets; + private final boolean generateNormal; - private final boolean allowBiomeDecoration; - public PSVoidChunkGenerator(BiomeSource biomeSource, Holder settings, TagKey allowedStructureSets) { + public PSVoidChunkGenerator( + BiomeSource biomeSource, + Holder settings, + Optional> ignoredAllowedStructureSets + ) { super(biomeSource, settings); this.settings = settings; - this.allowedStructureSets = allowedStructureSets; - this.generateNormal = (settings.is(ResourceLocation.parse("minecraft:nether")) && !Config.voidNetherGeneration); - this.allowBiomeDecoration = !settings.is(ResourceLocation.parse("minecraft:overworld")); + this.ignoredAllowedStructureSets = ignoredAllowedStructureSets; + + this.generateNormal = settings.is(ResourceLocation.parse("minecraft:nether")) + && !Config.voidNetherGeneration; + } + + public PSVoidChunkGenerator( + BiomeSource biomeSource, + Holder settings + ) { + this(biomeSource, settings, Optional.empty()); } @Override @@ -54,103 +89,193 @@ protected MapCodec codec() { return CODEC; } + /* + * 结构状态必须走原版。 + * 不要 FilteredLookup。 + */ @Override - public void applyCarvers(WorldGenRegion level, long seed, RandomState randomState, BiomeManager biomeManager, StructureManager structureManager, ChunkAccess chunk, GenerationStep.Carving step) { - if (this.generateNormal) { - super.applyCarvers(level, seed, randomState, biomeManager, structureManager, chunk, step); - } + public ChunkGeneratorStructureState createState( + HolderLookup lookup, + RandomState randomState, + long seed + ) { + return super.createState(lookup, randomState, seed); } + /* + * 主世界虚空:不生成洞穴。 + */ @Override - public ChunkGeneratorStructureState createState(HolderLookup lookup, RandomState randomState, long seed) { - return this.generateNormal - ? super.createState(lookup, randomState, seed) - : super.createState(new FilteredLookup(lookup, this.allowedStructureSets), randomState, seed); + public void applyCarvers( + WorldGenRegion level, + long seed, + RandomState randomState, + BiomeManager biomeManager, + StructureManager structureManager, + ChunkAccess chunk, + GenerationStep.Carving step + ) { + if (this.generateNormal) { + super.applyCarvers(level, seed, randomState, biomeManager, structureManager, chunk, step); + } } + /* + * 主世界虚空:不生成地表。 + */ @Override - public void buildSurface(WorldGenRegion level, StructureManager structureManager, RandomState randomState, ChunkAccess chunk) { + public void buildSurface( + WorldGenRegion level, + StructureManager structureManager, + RandomState randomState, + ChunkAccess chunk + ) { if (this.generateNormal) { super.buildSurface(level, structureManager, randomState, chunk); } } + /* + * 主世界虚空:不生成基础噪声地形。 + */ @Override - public void spawnOriginalMobs(WorldGenRegion level) { + public CompletableFuture fillFromNoise( + Blender blender, + RandomState randomState, + StructureManager structureManager, + ChunkAccess chunk + ) { if (this.generateNormal) { - super.spawnOriginalMobs(level); + return super.fillFromNoise(blender, randomState, structureManager, chunk); } + + return CompletableFuture.completedFuture(chunk); } + /* + * 给村庄、前哨站、神殿等结构判断地表高度。 + * 这里不是实际生成方块,只是告诉结构:地表在 Y=64。 + */ @Override - public CompletableFuture fillFromNoise(Blender blender, RandomState randomState, StructureManager structureManager, ChunkAccess chunk) { - return this.generateNormal - ? super.fillFromNoise(blender, randomState, structureManager, chunk) - : CompletableFuture.completedFuture(chunk); + public int getBaseHeight( + int x, + int z, + Heightmap.Types type, + LevelHeightAccessor level, + RandomState randomState + ) { + if (this.generateNormal) { + return super.getBaseHeight(x, z, type, level, randomState); + } + + return clampVirtualSurfaceY(level); } + /* + * 给结构算法看的假方块列。 + * 不会写入区块。 + */ @Override - public int getBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor level, RandomState randomState) { - return this.generateNormal ? super.getBaseHeight(x, z, type, level, randomState) : getMinY(); + public NoiseColumn getBaseColumn( + int x, + int z, + LevelHeightAccessor heightAccessor, + RandomState randomState + ) { + if (this.generateNormal) { + return super.getBaseColumn(x, z, heightAccessor, randomState); + } + + int minY = heightAccessor.getMinBuildHeight(); + int surfaceY = clampVirtualSurfaceY(heightAccessor); + + int count = Math.max(0, surfaceY - minY); + BlockState[] states = new BlockState[count]; + + for (int i = 0; i < count; i++) { + int y = minY + i; + + if (y == minY) { + states[i] = Blocks.BEDROCK.defaultBlockState(); + } else if (y == surfaceY - 1) { + states[i] = Blocks.GRASS_BLOCK.defaultBlockState(); + } else { + states[i] = Blocks.DIRT.defaultBlockState(); + } + } + + return new NoiseColumn(minY, states); } + /* + * 这个不能关。 + * 结构真正把方块放进区块,需要这里执行。 + * + * 虽然它也会跑普通 placed feature,但因为区块里没有真实地表, + * 大多数树、花、矿石等普通特征不会成功放置。 + */ @Override - public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor heightAccessor, RandomState randomState) { - return this.generateNormal ? super.getBaseColumn(x, z, heightAccessor, randomState) : new NoiseColumn(0, new BlockState[0]); + public void applyBiomeDecoration( + WorldGenLevel level, + ChunkAccess chunk, + StructureManager structureManager + ) { + super.applyBiomeDecoration(level, chunk, structureManager); } @Override - public void addDebugScreenInfo(List info, RandomState randomState, BlockPos pos) { - if (this.generateNormal) { - super.addDebugScreenInfo(info, randomState, pos); - } + public void createStructures( + RegistryAccess registries, + ChunkGeneratorStructureState structureState, + StructureManager structureManager, + ChunkAccess chunk, + StructureTemplateManager templateManager + ) { + super.createStructures(registries, structureState, structureManager, chunk, templateManager); } @Override - public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { - if (this.generateNormal || this.allowBiomeDecoration) { - super.applyBiomeDecoration(level, chunk, structureManager); - } + public void createReferences( + WorldGenLevel level, + StructureManager structureManager, + ChunkAccess chunk + ) { + super.createReferences(level, structureManager, chunk); } + /* + * 主世界虚空不跑原版动物生成。 + */ @Override - public void createReferences(WorldGenLevel level, StructureManager structureManager, ChunkAccess chunk) { - if (this.generateNormal || hasStructures(level.registryAccess())) { - super.createReferences(level, structureManager, chunk); + public void spawnOriginalMobs(WorldGenRegion level) { + if (this.generateNormal) { + super.spawnOriginalMobs(level); } } @Override - public void createStructures(RegistryAccess registries, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess chunk, StructureTemplateManager templateManager) { - if (this.generateNormal || hasStructures(registries)) { - super.createStructures(registries, structureState, structureManager, chunk, templateManager); + public void addDebugScreenInfo( + List info, + RandomState randomState, + BlockPos pos + ) { + if (this.generateNormal) { + super.addDebugScreenInfo(info, randomState, pos); } } - private boolean hasStructures(RegistryAccess registries) { - return registries.registryOrThrow(Registries.STRUCTURE_SET).getTagOrEmpty(this.allowedStructureSets).iterator().hasNext(); - } - - private record FilteredLookup(HolderLookup parent, - TagKey allowedValues) implements HolderLookup { - @Override - public Optional> get(ResourceKey key) { - return this.parent.get(key).filter(holder -> holder.is(this.allowedValues)); - } + private static int clampVirtualSurfaceY(LevelHeightAccessor level) { + int minY = level.getMinBuildHeight(); + int maxY = level.getMaxBuildHeight(); - @Override - public Optional> get(TagKey tagKey) { - return this.parent.get(tagKey); + if (VIRTUAL_SURFACE_Y <= minY) { + return minY + 1; } - @Override - public Stream> listElements() { - return this.parent.listElements().filter(holder -> holder.is(this.allowedValues)); + if (VIRTUAL_SURFACE_Y > maxY) { + return maxY; } - @Override - public Stream> listTags() { - return this.parent.listTags(); - } + return VIRTUAL_SURFACE_Y; } -} +} \ No newline at end of file diff --git a/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json b/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json index 9d025cfa..14c482c3 100644 --- a/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json +++ b/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json @@ -8,8 +8,7 @@ "type": "minecraft:multi_noise", "preset": "minecraft:overworld" }, - "settings": "minecraft:overworld", - "allowed_structure_sets": "poopsky:overworld_void_structure_sets" + "settings": "minecraft:overworld" } }, "minecraft:the_end": { @@ -30,8 +29,7 @@ "type": "minecraft:multi_noise", "preset": "minecraft:nether" }, - "settings": "minecraft:nether", - "allowed_structure_sets": "poopsky:the_nether_void_structure_sets" + "settings": "minecraft:nether" } } } From 585f4102a7a2cc7aec2b26f7058dc8484aa9599e Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Thu, 11 Jun 2026 21:15:50 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E8=83=BD=E7=94=9F=E6=88=90=E4=B8=9C?= =?UTF-8?q?=E8=A5=BF=20=E4=B8=BB=E4=B8=96=E7=95=8C=E4=B8=80=E6=A0=B7?= =?UTF-8?q?=E7=9A=84=E5=9C=B0=E7=89=A9=E5=92=8C=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldgen/PSVoidChunkGenerator.java | 255 +++++++++++++----- .../worldgen/world_preset/poopsky.json | 6 +- 2 files changed, 192 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java b/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java index f289ca30..0a1ef2d3 100644 --- a/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java +++ b/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java @@ -3,9 +3,11 @@ import com.altnoir.poopsky.Config; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.WorldGenRegion; import net.minecraft.tags.TagKey; @@ -15,11 +17,16 @@ import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.biome.BiomeSource; +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.*; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.structure.StructureSet; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; @@ -27,26 +34,54 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; public class PSVoidChunkGenerator extends NoiseBasedChunkGenerator { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( BiomeSource.CODEC.fieldOf("biome_source").forGetter(generator -> generator.biomeSource), NoiseGeneratorSettings.CODEC.fieldOf("settings").forGetter(generator -> generator.settings), - TagKey.codec(Registries.STRUCTURE_SET).fieldOf("allowed_structure_sets").forGetter(generator -> generator.allowedStructureSets) + TagKey.codec(Registries.STRUCTURE_SET) + .optionalFieldOf("allowed_structure_sets") + .forGetter(generator -> generator.ignoredAllowedStructureSets) ).apply(instance, instance.stable(PSVoidChunkGenerator::new))); + /* + * 给结构算法看的“假地表高度”。 + * + * 不建议用 getMinBuildHeight() + 4,因为 1.18+ 主世界最低高度是 -64, + * 那样结构会出现在 -60 附近,你很容易以为没生成。 + * + * 这里放在 64,结构会在虚空中正常可见。 + */ + private static final int VIRTUAL_SURFACE_Y = 64; + private final Holder settings; - private final TagKey allowedStructureSets; + + /* + * 只为了兼容旧 JSON。 + * 不再使用这个字段过滤结构,否则标签不存在时结构会被清空。 + */ + private final Optional> ignoredAllowedStructureSets; + private final boolean generateNormal; - private final boolean allowBiomeDecoration; - public PSVoidChunkGenerator(BiomeSource biomeSource, Holder settings, TagKey allowedStructureSets) { + public PSVoidChunkGenerator( + BiomeSource biomeSource, + Holder settings, + Optional> ignoredAllowedStructureSets + ) { super(biomeSource, settings); this.settings = settings; - this.allowedStructureSets = allowedStructureSets; - this.generateNormal = (settings.is(ResourceLocation.parse("minecraft:nether")) && !Config.voidNetherGeneration); - this.allowBiomeDecoration = !settings.is(ResourceLocation.parse("minecraft:overworld")); + this.ignoredAllowedStructureSets = ignoredAllowedStructureSets; + + this.generateNormal = settings.is(ResourceLocation.parse("minecraft:nether")) + && !Config.voidNetherGeneration; + } + + public PSVoidChunkGenerator( + BiomeSource biomeSource, + Holder settings + ) { + this(biomeSource, settings, Optional.empty()); } @Override @@ -54,103 +89,193 @@ protected MapCodec codec() { return CODEC; } + /* + * 结构状态必须走原版。 + * 不要 FilteredLookup。 + */ @Override - public void applyCarvers(WorldGenRegion level, long seed, RandomState randomState, BiomeManager biomeManager, StructureManager structureManager, ChunkAccess chunk, GenerationStep.Carving step) { - if (this.generateNormal) { - super.applyCarvers(level, seed, randomState, biomeManager, structureManager, chunk, step); - } + public ChunkGeneratorStructureState createState( + HolderLookup lookup, + RandomState randomState, + long seed + ) { + return super.createState(lookup, randomState, seed); } + /* + * 主世界虚空:不生成洞穴。 + */ @Override - public ChunkGeneratorStructureState createState(HolderLookup lookup, RandomState randomState, long seed) { - return this.generateNormal - ? super.createState(lookup, randomState, seed) - : super.createState(new FilteredLookup(lookup, this.allowedStructureSets), randomState, seed); + public void applyCarvers( + WorldGenRegion level, + long seed, + RandomState randomState, + BiomeManager biomeManager, + StructureManager structureManager, + ChunkAccess chunk, + GenerationStep.Carving step + ) { + if (this.generateNormal) { + super.applyCarvers(level, seed, randomState, biomeManager, structureManager, chunk, step); + } } + /* + * 主世界虚空:不生成地表。 + */ @Override - public void buildSurface(WorldGenRegion level, StructureManager structureManager, RandomState randomState, ChunkAccess chunk) { + public void buildSurface( + WorldGenRegion level, + StructureManager structureManager, + RandomState randomState, + ChunkAccess chunk + ) { if (this.generateNormal) { super.buildSurface(level, structureManager, randomState, chunk); } } + /* + * 主世界虚空:不生成基础噪声地形。 + */ @Override - public void spawnOriginalMobs(WorldGenRegion level) { + public CompletableFuture fillFromNoise( + Blender blender, + RandomState randomState, + StructureManager structureManager, + ChunkAccess chunk + ) { if (this.generateNormal) { - super.spawnOriginalMobs(level); + return super.fillFromNoise(blender, randomState, structureManager, chunk); } + + return CompletableFuture.completedFuture(chunk); } + /* + * 给村庄、前哨站、神殿等结构判断地表高度。 + * 这里不是实际生成方块,只是告诉结构:地表在 Y=64。 + */ @Override - public CompletableFuture fillFromNoise(Blender blender, RandomState randomState, StructureManager structureManager, ChunkAccess chunk) { - return this.generateNormal - ? super.fillFromNoise(blender, randomState, structureManager, chunk) - : CompletableFuture.completedFuture(chunk); + public int getBaseHeight( + int x, + int z, + Heightmap.Types type, + LevelHeightAccessor level, + RandomState randomState + ) { + if (this.generateNormal) { + return super.getBaseHeight(x, z, type, level, randomState); + } + + return clampVirtualSurfaceY(level); } + /* + * 给结构算法看的假方块列。 + * 不会写入区块。 + */ @Override - public int getBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor level, RandomState randomState) { - return this.generateNormal ? super.getBaseHeight(x, z, type, level, randomState) : getMinY(); + public NoiseColumn getBaseColumn( + int x, + int z, + LevelHeightAccessor heightAccessor, + RandomState randomState + ) { + if (this.generateNormal) { + return super.getBaseColumn(x, z, heightAccessor, randomState); + } + + int minY = heightAccessor.getMinBuildHeight(); + int surfaceY = clampVirtualSurfaceY(heightAccessor); + + int count = Math.max(0, surfaceY - minY); + BlockState[] states = new BlockState[count]; + + for (int i = 0; i < count; i++) { + int y = minY + i; + + if (y == minY) { + states[i] = Blocks.BEDROCK.defaultBlockState(); + } else if (y == surfaceY - 1) { + states[i] = Blocks.GRASS_BLOCK.defaultBlockState(); + } else { + states[i] = Blocks.DIRT.defaultBlockState(); + } + } + + return new NoiseColumn(minY, states); } + /* + * 这个不能关。 + * 结构真正把方块放进区块,需要这里执行。 + * + * 虽然它也会跑普通 placed feature,但因为区块里没有真实地表, + * 大多数树、花、矿石等普通特征不会成功放置。 + */ @Override - public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor heightAccessor, RandomState randomState) { - return this.generateNormal ? super.getBaseColumn(x, z, heightAccessor, randomState) : new NoiseColumn(0, new BlockState[0]); + public void applyBiomeDecoration( + WorldGenLevel level, + ChunkAccess chunk, + StructureManager structureManager + ) { + super.applyBiomeDecoration(level, chunk, structureManager); } @Override - public void addDebugScreenInfo(List info, RandomState randomState, BlockPos pos) { - if (this.generateNormal) { - super.addDebugScreenInfo(info, randomState, pos); - } + public void createStructures( + RegistryAccess registries, + ChunkGeneratorStructureState structureState, + StructureManager structureManager, + ChunkAccess chunk, + StructureTemplateManager templateManager + ) { + super.createStructures(registries, structureState, structureManager, chunk, templateManager); } @Override - public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { - if (this.generateNormal || this.allowBiomeDecoration) { - super.applyBiomeDecoration(level, chunk, structureManager); - } + public void createReferences( + WorldGenLevel level, + StructureManager structureManager, + ChunkAccess chunk + ) { + super.createReferences(level, structureManager, chunk); } + /* + * 主世界虚空不跑原版动物生成。 + */ @Override - public void createReferences(WorldGenLevel level, StructureManager structureManager, ChunkAccess chunk) { - if (this.generateNormal || hasStructures(level.registryAccess())) { - super.createReferences(level, structureManager, chunk); + public void spawnOriginalMobs(WorldGenRegion level) { + if (this.generateNormal) { + super.spawnOriginalMobs(level); } } @Override - public void createStructures(RegistryAccess registries, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess chunk, StructureTemplateManager templateManager) { - if (this.generateNormal || hasStructures(registries)) { - super.createStructures(registries, structureState, structureManager, chunk, templateManager); + public void addDebugScreenInfo( + List info, + RandomState randomState, + BlockPos pos + ) { + if (this.generateNormal) { + super.addDebugScreenInfo(info, randomState, pos); } } - private boolean hasStructures(RegistryAccess registries) { - return registries.registryOrThrow(Registries.STRUCTURE_SET).getTagOrEmpty(this.allowedStructureSets).iterator().hasNext(); - } - - private record FilteredLookup(HolderLookup parent, - TagKey allowedValues) implements HolderLookup { - @Override - public Optional> get(ResourceKey key) { - return this.parent.get(key).filter(holder -> holder.is(this.allowedValues)); - } + private static int clampVirtualSurfaceY(LevelHeightAccessor level) { + int minY = level.getMinBuildHeight(); + int maxY = level.getMaxBuildHeight(); - @Override - public Optional> get(TagKey tagKey) { - return this.parent.get(tagKey); + if (VIRTUAL_SURFACE_Y <= minY) { + return minY + 1; } - @Override - public Stream> listElements() { - return this.parent.listElements().filter(holder -> holder.is(this.allowedValues)); + if (VIRTUAL_SURFACE_Y > maxY) { + return maxY; } - @Override - public Stream> listTags() { - return this.parent.listTags(); - } + return VIRTUAL_SURFACE_Y; } -} +} \ No newline at end of file diff --git a/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json b/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json index 9d025cfa..14c482c3 100644 --- a/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json +++ b/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json @@ -8,8 +8,7 @@ "type": "minecraft:multi_noise", "preset": "minecraft:overworld" }, - "settings": "minecraft:overworld", - "allowed_structure_sets": "poopsky:overworld_void_structure_sets" + "settings": "minecraft:overworld" } }, "minecraft:the_end": { @@ -30,8 +29,7 @@ "type": "minecraft:multi_noise", "preset": "minecraft:nether" }, - "settings": "minecraft:nether", - "allowed_structure_sets": "poopsky:the_nether_void_structure_sets" + "settings": "minecraft:nether" } } } From 8ed7020bbb507c31536d0e4875c3aea4a1430916 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Fri, 12 Jun 2026 13:54:59 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E7=A6=81=E7=94=A8=E5=9C=B0=E7=89=A9=20?= =?UTF-8?q?=E4=BF=9D=E7=95=99=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldgen/PSVoidChunkGenerator.java | 99 ++++++++++++++++++- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java b/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java index 0a1ef2d3..1f4a212b 100644 --- a/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java +++ b/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java @@ -3,14 +3,20 @@ import com.altnoir.poopsky.Config; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.CrashReport; +import net.minecraft.ReportedException; +import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.RegistryAccess; +import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.WorldGenRegion; import net.minecraft.tags.TagKey; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.NoiseColumn; import net.minecraft.world.level.StructureManager; @@ -27,13 +33,22 @@ import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.RandomSupport; +import net.minecraft.world.level.levelgen.WorldgenRandom; +import net.minecraft.world.level.levelgen.XoroshiroRandomSource; 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.templatesystem.StructureTemplateManager; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; +import java.util.stream.Collectors; public class PSVoidChunkGenerator extends NoiseBasedChunkGenerator { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( @@ -210,9 +225,6 @@ public NoiseColumn getBaseColumn( /* * 这个不能关。 * 结构真正把方块放进区块,需要这里执行。 - * - * 虽然它也会跑普通 placed feature,但因为区块里没有真实地表, - * 大多数树、花、矿石等普通特征不会成功放置。 */ @Override public void applyBiomeDecoration( @@ -220,7 +232,12 @@ public void applyBiomeDecoration( ChunkAccess chunk, StructureManager structureManager ) { - super.applyBiomeDecoration(level, chunk, structureManager); + if (this.generateNormal) { + super.applyBiomeDecoration(level, chunk, structureManager); + return; + } + + placeStructuresOnly(level, chunk, structureManager); } @Override @@ -278,4 +295,76 @@ private static int clampVirtualSurfaceY(LevelHeightAccessor level) { return VIRTUAL_SURFACE_Y; } -} \ No newline at end of file + + private void placeStructuresOnly( + WorldGenLevel level, + ChunkAccess chunk, + StructureManager structureManager + ) { + ChunkPos chunkPos = chunk.getPos(); + if (SharedConstants.debugVoidTerrain(chunkPos)) { + return; + } + + SectionPos sectionPos = SectionPos.of(chunkPos, level.getMinSection()); + BlockPos origin = sectionPos.origin(); + Registry registry = level.registryAccess().registryOrThrow(Registries.STRUCTURE); + Map> structuresByStep = registry.stream() + .collect(Collectors.groupingBy(structure -> structure.step().ordinal())); + WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); + long decorationSeed = random.setDecorationSeed(level.getSeed(), origin.getX(), origin.getZ()); + + try { + for (int step = 0; step < GenerationStep.Decoration.values().length; step++) { + int structureIndex = 0; + if (structureManager.shouldGenerateStructures()) { + for (Structure structure : structuresByStep.getOrDefault(step, Collections.emptyList())) { + random.setFeatureSeed(decorationSeed, structureIndex, step); + Supplier name = () -> registry.getResourceKey(structure) + .map(Object::toString) + .orElseGet(structure::toString); + + try { + level.setCurrentlyGenerating(name); + structureManager.startsForStructure(sectionPos, structure) + .forEach(start -> start.placeInChunk( + level, + structureManager, + this, + random, + getWritableArea(chunk), + chunkPos + )); + } catch (Exception exception) { + CrashReport report = CrashReport.forThrowable(exception, "Structure placement"); + report.addCategory("Structure").setDetail("Description", name::get); + throw new ReportedException(report); + } + + structureIndex++; + } + } + } + } catch (Exception exception) { + CrashReport report = CrashReport.forThrowable(exception, "Biome decoration"); + report.addCategory("Generation") + .setDetail("CenterX", chunkPos.x) + .setDetail("CenterZ", chunkPos.z) + .setDetail("Decoration Seed", decorationSeed); + throw new ReportedException(report); + } finally { + level.setCurrentlyGenerating(null); + } + } + + private static BoundingBox getWritableArea(ChunkAccess chunk) { + ChunkPos chunkPos = chunk.getPos(); + int minX = chunkPos.getMinBlockX(); + int minZ = chunkPos.getMinBlockZ(); + LevelHeightAccessor height = chunk.getHeightAccessorForGeneration(); + int minY = height.getMinBuildHeight() + 1; + int maxY = height.getMaxBuildHeight() - 1; + + return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15); + } +} From a4c760776c909c8dd6009c81e8b5d737e5cd60bd Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Fri, 12 Jun 2026 18:11:59 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E7=A6=81=E7=94=A8=E5=A5=96=E5=8A=B1?= =?UTF-8?q?=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mixin/CreateWorldScreenWorldTabMixin.java | 30 +++++++++++++++ .../mixin/WorldCreationUiStateMixin.java | 38 +++++++++++++++++++ .../com/altnoir/poopsky/util/ClientUtil.java | 15 ++++++++ src/main/resources/poopsky.mixins.json | 4 +- 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/altnoir/poopsky/mixin/CreateWorldScreenWorldTabMixin.java create mode 100644 src/main/java/com/altnoir/poopsky/mixin/WorldCreationUiStateMixin.java diff --git a/src/main/java/com/altnoir/poopsky/mixin/CreateWorldScreenWorldTabMixin.java b/src/main/java/com/altnoir/poopsky/mixin/CreateWorldScreenWorldTabMixin.java new file mode 100644 index 00000000..473b6866 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/mixin/CreateWorldScreenWorldTabMixin.java @@ -0,0 +1,30 @@ +package com.altnoir.poopsky.mixin; + +import com.altnoir.poopsky.util.ClientUtil; +import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +import java.util.function.BooleanSupplier; + +@Mixin(targets = "net.minecraft.client.gui.screens.worldselection.CreateWorldScreen$WorldTab") +public class CreateWorldScreenWorldTabMixin { + @Shadow + @Final + private CreateWorldScreen this$0; + + @ModifyArg( + method = "", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/gui/screens/worldselection/SwitchGrid$SwitchBuilder;withIsActiveCondition(Ljava/util/function/BooleanSupplier;)Lnet/minecraft/client/gui/screens/worldselection/SwitchGrid$SwitchBuilder;", + ordinal = 1 + ) + ) + private BooleanSupplier poopsky$disableBonusChestForPoopSky(BooleanSupplier original) { + return () -> original.getAsBoolean() && !ClientUtil.isPoopSkyWorldType(this.this$0.getUiState()); + } +} diff --git a/src/main/java/com/altnoir/poopsky/mixin/WorldCreationUiStateMixin.java b/src/main/java/com/altnoir/poopsky/mixin/WorldCreationUiStateMixin.java new file mode 100644 index 00000000..3fb092c5 --- /dev/null +++ b/src/main/java/com/altnoir/poopsky/mixin/WorldCreationUiStateMixin.java @@ -0,0 +1,38 @@ +package com.altnoir.poopsky.mixin; + +import com.altnoir.poopsky.util.ClientUtil; +import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(WorldCreationUiState.class) +public abstract class WorldCreationUiStateMixin { + @Shadow + private boolean bonusChest; + + @Inject(method = "setBonusChest", at = @At("HEAD"), cancellable = true) + private void poopsky$preventBonusChestForPoopSky(boolean bonusChest, CallbackInfo ci) { + if (bonusChest && ClientUtil.isPoopSkyWorldType((WorldCreationUiState)(Object)this)) { + this.bonusChest = false; + ci.cancel(); + } + } + + @Inject(method = "setWorldType", at = @At("HEAD")) + private void poopsky$clearBonusChestForPoopSky(WorldCreationUiState.WorldTypeEntry worldType, CallbackInfo ci) { + if (ClientUtil.isPoopSkyWorldType(worldType)) { + this.bonusChest = false; + } + } + + @Inject(method = "isBonusChest", at = @At("HEAD"), cancellable = true) + private void poopsky$hideBonusChestForPoopSky(CallbackInfoReturnable cir) { + if (ClientUtil.isPoopSkyWorldType((WorldCreationUiState)(Object)this)) { + cir.setReturnValue(false); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/altnoir/poopsky/util/ClientUtil.java b/src/main/java/com/altnoir/poopsky/util/ClientUtil.java index 00613f39..a2723015 100644 --- a/src/main/java/com/altnoir/poopsky/util/ClientUtil.java +++ b/src/main/java/com/altnoir/poopsky/util/ClientUtil.java @@ -1,10 +1,12 @@ package com.altnoir.poopsky.util; +import com.altnoir.poopsky.util.asm.ASMHooks; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import com.mojang.math.Axis; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; @@ -25,6 +27,8 @@ import org.joml.Matrix4fStack; import org.joml.Vector3f; +import java.util.Optional; + public class ClientUtil { private static final FluidState EMPTY = Fluids.EMPTY.defaultFluidState(); private static final BlockState AIR = Blocks.AIR.defaultBlockState(); @@ -143,4 +147,15 @@ public int getMinBuildHeight() { return 0; } } + + public static boolean isPoopSkyWorldType(WorldCreationUiState uiState) { + return isPoopSkyWorldType(uiState.getWorldType()); + } + + public static boolean isPoopSkyWorldType(WorldCreationUiState.WorldTypeEntry worldType) { + return Optional.ofNullable(worldType.preset()) + .flatMap(holder -> holder.unwrapKey()) + .filter(ASMHooks.POOPSKY::equals) + .isPresent(); + } } diff --git a/src/main/resources/poopsky.mixins.json b/src/main/resources/poopsky.mixins.json index 28a6b942..3c391dfa 100644 --- a/src/main/resources/poopsky.mixins.json +++ b/src/main/resources/poopsky.mixins.json @@ -12,7 +12,9 @@ "VillagerMixin" ], "client": [ - "ClientPacketListenerMixin" + "ClientPacketListenerMixin", + "CreateWorldScreenWorldTabMixin", + "WorldCreationUiStateMixin" ], "injectors": { "defaultRequire": 1 From af5596f6958498e3b229b07b835401194e343ef7 Mon Sep 17 00:00:00 2001 From: Wulian233 <1055917385@qq.com> Date: Fri, 12 Jun 2026 20:57:35 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../worldgen/PSVoidChunkGenerator.java | 402 +++++++++--------- .../worldgen/world_preset/poopsky.json | 6 +- 2 files changed, 197 insertions(+), 211 deletions(-) diff --git a/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java b/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java index 1f4a212b..7d90dac1 100644 --- a/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java +++ b/src/main/java/com/altnoir/poopsky/worldgen/PSVoidChunkGenerator.java @@ -1,6 +1,8 @@ package com.altnoir.poopsky.worldgen; import com.altnoir.poopsky.Config; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.CrashReport; @@ -8,11 +10,11 @@ import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.RegistryAccess; import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.WorldGenRegion; import net.minecraft.tags.TagKey; @@ -40,62 +42,71 @@ 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 java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import java.util.stream.Collectors; public class PSVoidChunkGenerator extends NoiseBasedChunkGenerator { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - BiomeSource.CODEC.fieldOf("biome_source").forGetter(generator -> generator.biomeSource), - NoiseGeneratorSettings.CODEC.fieldOf("settings").forGetter(generator -> generator.settings), - TagKey.codec(Registries.STRUCTURE_SET) - .optionalFieldOf("allowed_structure_sets") - .forGetter(generator -> generator.ignoredAllowedStructureSets) - ).apply(instance, instance.stable(PSVoidChunkGenerator::new))); - - /* - * 给结构算法看的“假地表高度”。 - * - * 不建议用 getMinBuildHeight() + 4,因为 1.18+ 主世界最低高度是 -64, - * 那样结构会出现在 -60 附近,你很容易以为没生成。 - * - * 这里放在 64,结构会在虚空中正常可见。 - */ private static final int VIRTUAL_SURFACE_Y = 64; - private final Holder settings; - - /* - * 只为了兼容旧 JSON。 - * 不再使用这个字段过滤结构,否则标签不存在时结构会被清空。 - */ - private final Optional> ignoredAllowedStructureSets; + private static final Codec>> STRUCTURE_SET_KEYS_CODEC = + Codec.either(ResourceLocation.CODEC.listOf(), ResourceLocation.CODEC).xmap( + either -> either.map( + locations -> locations.stream() + .map(location -> ResourceKey.create(Registries.STRUCTURE_SET, location)) + .toList(), + location -> List.of(ResourceKey.create(Registries.STRUCTURE_SET, location)) + ), + keys -> keys.size() == 1 + ? Either.right(keys.getFirst().location()) + : Either.left(keys.stream().map(ResourceKey::location).toList()) + ); + + private static final Codec ALLOWED_STRUCTURE_SETS_CODEC = + Codec.either( + TagKey.hashedCodec(Registries.STRUCTURE_SET), + STRUCTURE_SET_KEYS_CODEC + ).xmap( + either -> either.map( + AllowedStructureSets::ofTag, + AllowedStructureSets::ofKeys + ), + allowed -> allowed.tag() + ., List>>>map(Either::left) + .orElseGet(() -> Either.right(allowed.keys())) + ); + + public static final MapCodec CODEC = + RecordCodecBuilder.mapCodec(instance -> instance.group( + BiomeSource.CODEC.fieldOf("biome_source") + .forGetter(generator -> generator.biomeSource), + NoiseGeneratorSettings.CODEC.fieldOf("settings") + .forGetter(generator -> generator.settings), + ALLOWED_STRUCTURE_SETS_CODEC.optionalFieldOf("allowed_structure_sets") + .forGetter(generator -> generator.allowedStructureSets) + ).apply(instance, instance.stable(PSVoidChunkGenerator::new))); + private final Holder settings; + private final Optional allowedStructureSets; private final boolean generateNormal; - public PSVoidChunkGenerator( - BiomeSource biomeSource, - Holder settings, - Optional> ignoredAllowedStructureSets - ) { + public PSVoidChunkGenerator(BiomeSource biomeSource, Holder settings, Optional allowedStructureSets) { super(biomeSource, settings); this.settings = settings; - this.ignoredAllowedStructureSets = ignoredAllowedStructureSets; - - this.generateNormal = settings.is(ResourceLocation.parse("minecraft:nether")) - && !Config.voidNetherGeneration; + this.allowedStructureSets = allowedStructureSets; + this.generateNormal = settings.is(ResourceLocation.parse("minecraft:nether")) && !Config.voidNetherGeneration; } - public PSVoidChunkGenerator( - BiomeSource biomeSource, - Holder settings - ) { + public PSVoidChunkGenerator(BiomeSource biomeSource, Holder settings) { this(biomeSource, settings, Optional.empty()); } @@ -104,22 +115,21 @@ protected MapCodec codec() { return CODEC; } - /* - * 结构状态必须走原版。 - * 不要 FilteredLookup。 - */ - @Override - public ChunkGeneratorStructureState createState( - HolderLookup lookup, - RandomState randomState, - long seed - ) { - return super.createState(lookup, randomState, seed); + private record AllowedStructureSets(Optional> tag, List> keys) { + private static AllowedStructureSets ofTag(TagKey tag) { + return new AllowedStructureSets(Optional.of(tag), List.of()); + } + + private static AllowedStructureSets ofKeys(List> keys) { + return new AllowedStructureSets(Optional.empty(), List.copyOf(keys)); + } + + private boolean contains(Holder holder) { + return tag.map(holder::is) + .orElseGet(() -> holder.unwrapKey().filter(keys::contains).isPresent()); + } } - /* - * 主世界虚空:不生成洞穴。 - */ @Override public void applyCarvers( WorldGenRegion level, @@ -130,116 +140,54 @@ public void applyCarvers( ChunkAccess chunk, GenerationStep.Carving step ) { - if (this.generateNormal) { - super.applyCarvers(level, seed, randomState, biomeManager, structureManager, chunk, step); + if (generateNormal) { + super.applyCarvers( level, seed, randomState, biomeManager, structureManager, chunk, step); } } - /* - * 主世界虚空:不生成地表。 - */ @Override - public void buildSurface( - WorldGenRegion level, - StructureManager structureManager, - RandomState randomState, - ChunkAccess chunk - ) { - if (this.generateNormal) { + public void buildSurface(WorldGenRegion level, StructureManager structureManager, RandomState randomState, ChunkAccess chunk) { + if (generateNormal) { super.buildSurface(level, structureManager, randomState, chunk); } } - /* - * 主世界虚空:不生成基础噪声地形。 - */ @Override - public CompletableFuture fillFromNoise( - Blender blender, - RandomState randomState, - StructureManager structureManager, - ChunkAccess chunk - ) { - if (this.generateNormal) { - return super.fillFromNoise(blender, randomState, structureManager, chunk); - } - - return CompletableFuture.completedFuture(chunk); + public CompletableFuture fillFromNoise(Blender blender, RandomState randomState, StructureManager structureManager, ChunkAccess chunk) { + return generateNormal + ? super.fillFromNoise(blender, randomState, structureManager, chunk) + : CompletableFuture.completedFuture(chunk); } - /* - * 给村庄、前哨站、神殿等结构判断地表高度。 - * 这里不是实际生成方块,只是告诉结构:地表在 Y=64。 - */ @Override - public int getBaseHeight( - int x, - int z, - Heightmap.Types type, - LevelHeightAccessor level, - RandomState randomState - ) { - if (this.generateNormal) { - return super.getBaseHeight(x, z, type, level, randomState); - } - - return clampVirtualSurfaceY(level); + public int getBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor level, RandomState randomState) { + return generateNormal + ? super.getBaseHeight(x, z, type, level, randomState) + : virtualSurfaceY(level); } - /* - * 给结构算法看的假方块列。 - * 不会写入区块。 - */ @Override - public NoiseColumn getBaseColumn( - int x, - int z, - LevelHeightAccessor heightAccessor, - RandomState randomState - ) { - if (this.generateNormal) { - return super.getBaseColumn(x, z, heightAccessor, randomState); + public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor level, RandomState randomState) { + if (generateNormal) { + return super.getBaseColumn(x, z, level, randomState); } - int minY = heightAccessor.getMinBuildHeight(); - int surfaceY = clampVirtualSurfaceY(heightAccessor); - - int count = Math.max(0, surfaceY - minY); - BlockState[] states = new BlockState[count]; - - for (int i = 0; i < count; i++) { - int y = minY + i; - - if (y == minY) { - states[i] = Blocks.BEDROCK.defaultBlockState(); - } else if (y == surfaceY - 1) { - states[i] = Blocks.GRASS_BLOCK.defaultBlockState(); - } else { - states[i] = Blocks.DIRT.defaultBlockState(); - } + int minY = level.getMinBuildHeight(); + int surfaceY = virtualSurfaceY(level); + BlockState[] states = new BlockState[Math.max(0, surfaceY - minY)]; + + for (int index = 0; index < states.length; index++) { + int y = minY + index; + states[index] = y == minY + ? Blocks.BEDROCK.defaultBlockState() + : y == surfaceY - 1 + ? Blocks.GRASS_BLOCK.defaultBlockState() + : Blocks.DIRT.defaultBlockState(); } return new NoiseColumn(minY, states); } - /* - * 这个不能关。 - * 结构真正把方块放进区块,需要这里执行。 - */ - @Override - public void applyBiomeDecoration( - WorldGenLevel level, - ChunkAccess chunk, - StructureManager structureManager - ) { - if (this.generateNormal) { - super.applyBiomeDecoration(level, chunk, structureManager); - return; - } - - placeStructuresOnly(level, chunk, structureManager); - } - @Override public void createStructures( RegistryAccess registries, @@ -249,122 +197,156 @@ public void createStructures( StructureTemplateManager templateManager ) { super.createStructures(registries, structureState, structureManager, chunk, templateManager); + + if (allowedStructureSets.isEmpty()) { + return; + } + + Set allowedStructures = resolveAllowedStructures(registries); + Map filteredStarts = new HashMap<>(); + + chunk.getAllStarts().forEach((structure, start) -> { + if (allowedStructures.contains(structure)) { + filteredStarts.put(structure, start); + } + }); + + chunk.setAllStarts(filteredStarts); } @Override - public void createReferences( - WorldGenLevel level, - StructureManager structureManager, - ChunkAccess chunk + public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager ) { - super.createReferences(level, structureManager, chunk); + if (generateNormal) { + super.applyBiomeDecoration(level, chunk, structureManager); + } else { + placeStructuresOnly(level, chunk, structureManager); + } } - /* - * 主世界虚空不跑原版动物生成。 - */ @Override public void spawnOriginalMobs(WorldGenRegion level) { - if (this.generateNormal) { + if (generateNormal) { super.spawnOriginalMobs(level); } } @Override - public void addDebugScreenInfo( - List info, - RandomState randomState, - BlockPos pos - ) { - if (this.generateNormal) { + public void addDebugScreenInfo(List info, RandomState randomState, BlockPos pos) { + if (generateNormal) { super.addDebugScreenInfo(info, randomState, pos); } } - private static int clampVirtualSurfaceY(LevelHeightAccessor level) { - int minY = level.getMinBuildHeight(); - int maxY = level.getMaxBuildHeight(); - - if (VIRTUAL_SURFACE_Y <= minY) { - return minY + 1; + private Set resolveAllowedStructures(RegistryAccess registries) { + if (allowedStructureSets.isEmpty()) { + return Set.of(); } - if (VIRTUAL_SURFACE_Y > maxY) { - return maxY; - } + AllowedStructureSets allowed = allowedStructureSets.get(); + Registry registry = registries.registryOrThrow(Registries.STRUCTURE_SET); - return VIRTUAL_SURFACE_Y; + return registry.holders() + .filter(allowed::contains) + .flatMap(holder -> holder.value().structures().stream()) + .map(StructureSet.StructureSelectionEntry::structure) + .map(Holder::value) + .collect(Collectors.toUnmodifiableSet()); } - private void placeStructuresOnly( - WorldGenLevel level, - ChunkAccess chunk, - StructureManager structureManager - ) { + private void placeStructuresOnly( WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { ChunkPos chunkPos = chunk.getPos(); - if (SharedConstants.debugVoidTerrain(chunkPos)) { + + if (SharedConstants.debugVoidTerrain(chunkPos) || !structureManager.shouldGenerateStructures()) { return; } - SectionPos sectionPos = SectionPos.of(chunkPos, level.getMinSection()); + SectionPos sectionPos = SectionPos.bottomOf(chunk); BlockPos origin = sectionPos.origin(); - Registry registry = level.registryAccess().registryOrThrow(Registries.STRUCTURE); - Map> structuresByStep = registry.stream() - .collect(Collectors.groupingBy(structure -> structure.step().ordinal())); + RegistryAccess registries = level.registryAccess(); + Registry structureRegistry = registries.registryOrThrow(Registries.STRUCTURE); + + Set allowedStructures = allowedStructureSets.isPresent() + ? resolveAllowedStructures(registries) + : null; + + Map> structuresByStep = + structureRegistry.stream() + .filter(structure -> allowedStructures == null || allowedStructures.contains(structure)) + .collect(Collectors.groupingBy(structure -> structure.step().ordinal())); + WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); + long decorationSeed = random.setDecorationSeed(level.getSeed(), origin.getX(), origin.getZ()); try { for (int step = 0; step < GenerationStep.Decoration.values().length; step++) { - int structureIndex = 0; - if (structureManager.shouldGenerateStructures()) { - for (Structure structure : structuresByStep.getOrDefault(step, Collections.emptyList())) { - random.setFeatureSeed(decorationSeed, structureIndex, step); - Supplier name = () -> registry.getResourceKey(structure) - .map(Object::toString) - .orElseGet(structure::toString); - - try { - level.setCurrentlyGenerating(name); - structureManager.startsForStructure(sectionPos, structure) - .forEach(start -> start.placeInChunk( - level, - structureManager, - this, - random, - getWritableArea(chunk), - chunkPos - )); - } catch (Exception exception) { - CrashReport report = CrashReport.forThrowable(exception, "Structure placement"); - report.addCategory("Structure").setDetail("Description", name::get); - throw new ReportedException(report); - } - - structureIndex++; + List structures = structuresByStep.getOrDefault(step, Collections.emptyList()); + + for (int index = 0; index < structures.size(); index++) { + Structure structure = structures.get(index); + random.setFeatureSeed(decorationSeed, index, step); + + Supplier name = () -> + structureRegistry.getResourceKey(structure) + .map(Object::toString) + .orElseGet(structure::toString); + + try { + level.setCurrentlyGenerating(name); + + structureManager.startsForStructure(sectionPos, structure) + .stream() + .filter(StructureStart::isValid) + .forEach(start -> start.placeInChunk( + level, + structureManager, + this, + random, + writableArea(chunk), + chunkPos + )); + } catch (Exception exception) { + CrashReport report = CrashReport.forThrowable(exception, "Structure placement"); + + report.addCategory("Structure") + .setDetail("Description", name::get); + + throw new ReportedException(report); } } } + } catch (ReportedException exception) { + throw exception; } catch (Exception exception) { - CrashReport report = CrashReport.forThrowable(exception, "Biome decoration"); + CrashReport report = CrashReport.forThrowable(exception,"Void biome decoration"); + report.addCategory("Generation") .setDetail("CenterX", chunkPos.x) .setDetail("CenterZ", chunkPos.z) .setDetail("Decoration Seed", decorationSeed); + throw new ReportedException(report); } finally { level.setCurrentlyGenerating(null); } } - private static BoundingBox getWritableArea(ChunkAccess chunk) { - ChunkPos chunkPos = chunk.getPos(); - int minX = chunkPos.getMinBlockX(); - int minZ = chunkPos.getMinBlockZ(); + private static BoundingBox writableArea(ChunkAccess chunk) { + ChunkPos pos = chunk.getPos(); LevelHeightAccessor height = chunk.getHeightAccessorForGeneration(); - int minY = height.getMinBuildHeight() + 1; - int maxY = height.getMaxBuildHeight() - 1; - return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15); + return new BoundingBox( + pos.getMinBlockX(), + height.getMinBuildHeight() + 1, + pos.getMinBlockZ(), + pos.getMaxBlockX(), + height.getMaxBuildHeight() - 1, + pos.getMaxBlockZ() + ); + } + + private static int virtualSurfaceY(LevelHeightAccessor level) { + return Math.clamp(VIRTUAL_SURFACE_Y, level.getMinBuildHeight() + 1, level.getMaxBuildHeight()); } -} +} \ No newline at end of file diff --git a/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json b/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json index 14c482c3..60c53725 100644 --- a/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json +++ b/src/main/resources/data/poopsky/worldgen/world_preset/poopsky.json @@ -8,7 +8,11 @@ "type": "minecraft:multi_noise", "preset": "minecraft:overworld" }, - "settings": "minecraft:overworld" + "settings": "minecraft:overworld", + "allowed_structure_sets": [ + "minecraft:villages", + "minecraft:strongholds" + ] } }, "minecraft:the_end": {