# Minecraft Fabric Modding - 世界生成 ## 礦物生成 在模組路徑下的 `world/feature/ore` 建立檔案 `ModOreFeature.java`: ```java= public class ModOreFeatures { public static List<PlacementModifier> modifiers(PlacementModifier countModifier, PlacementModifier heightModifier) { return List.of(countModifier, SquarePlacementModifier.of(), heightModifier, BiomePlacementModifier.of()); } public static List<PlacementModifier> modifiersWithCount(int count, PlacementModifier heightModifier) { return modifiers(CountPlacementModifier.of(count), heightModifier); } public static List<PlacementModifier> modifiersWithRarity(int chance, PlacementModifier heightModifier) { return modifiers(RarityFilterPlacementModifier.of(chance), heightModifier); } } ``` 再來,於 `world/feature` 下建立檔案 `ModOreGeneration.java`: ```java= public class ModOreGeneration { public static void generateOres() { generator("lead_ore", new OreGenerationReference( List.of(new Pair<>(ModBlocks.LEAD_ORE, OreConfiguredFeatures.STONE_ORE_REPLACEABLES), new Pair<>(ModBlocks.LEAD_ORE, OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES)), BiomeSelectors.foundInOverworld(), GenerationStep.Feature.UNDERGROUND_ORES, 8, // Vein Size 7, // Veins Per Chunk HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-80), YOffset.aboveBottom(80)) // HeightRangePlacementModifier 有很多不同種的生成分布方式。像 trapezoid 就是梯形分布,上下最少,中間最多 )); } public record OreGenerationReference(List<Pair<Block, RuleTest>> BRP_list, Predicate<BiomeSelectionContext> biomeSelection, GenerationStep.Feature step, int veinSize, int veinsPerChunk, HeightRangePlacementModifier modifier) {} public static void generator(String name, OreGenerationReference ref) { List<OreFeatureConfig.Target> list = new ArrayList<>(); for (Pair<Block, RuleTest> p : ref.BRP_list) list.add(OreFeatureConfig.createTarget(p.getRight(), p.getLeft().getDefaultState())); RegistryEntry<ConfiguredFeature<OreFeatureConfig, ?>> ORE = ConfiguredFeatures.register(name, Feature.ORE, new OreFeatureConfig(list, ref.veinSize)); RegistryEntry<PlacedFeature> ORE_PLACED = PlacedFeatures.register(name + "_placed", ORE, ModOreFeatures.modifiersWithCount(ref.veinsPerChunk, ref.modifier)); BiomeModifications.addFeature(ref.biomeSelection, ref.step, ORE_PLACED.getKey().get()); } // utilities private static List<Pair<Block, RuleTest>> getOverworldList(Block block) { return List.of(new Pair<>(block, OreConfiguredFeatures.STONE_ORE_REPLACEABLES), new Pair<>(block, OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES)); } private static final HeightRangePlacementModifier huAll = HeightRangePlacementModifier.uniform(YOffset.BOTTOM, YOffset.TOP); } ``` ## 結構生成 ### .nbt 檔 在 Minecraft 中,我們常用 nbt 檔來存儲結構資料。nbt 檔是一種二進位檔,可用 [NBTExplorer](https://github.com/jaquadro/NBTExplorer/releases) 瀏覽其中資料。 ### 結構方塊與拼圖方塊 [Talon - How to Use Jigsaw Blocks](https://www.youtube.com/watch?v=5a4DAkWW3JQ&ab_channel=Talon) :::warning 以下皆以單結構方塊為主,也就是未使用拼圖方塊 ::: ### code 在模組 java 路徑下,新增 `world/structures`,並新增兩個檔案 `AltarStructure`、`ModStructures` ```java= public class AltarStructure extends StructureFeature<StructurePoolFeatureConfig> { public AltarStructure() { super(StructurePoolFeatureConfig.CODEC, AltarStructure::createPiecesGenerator, PostPlacementProcessor.EMPTY); } private static boolean isFeatureChunk(StructureGeneratorFactory.Context<StructurePoolFeatureConfig> context) { // Grabs the chunk position we are at ChunkPos chunkpos = context.chunkPos(); // Checks to make sure our structure does not spawn within 10 chunks of an Ocean Monument // to demonstrate how this method is good for checking extra conditions for spawning return !context.chunkGenerator().method_41053(StructureSetKeys.VILLAGES, context.seed(), chunkpos.x, chunkpos.z, 10); } public static Optional<StructurePiecesGenerator<StructurePoolFeatureConfig>> createPiecesGenerator(StructureGeneratorFactory.Context<StructurePoolFeatureConfig> context) { // Check if the spot is valid for our structure. This is just as another method for cleanness. // Returning an empty optional tells the game to skip this spot as it will not generate the structure. if (!AltarStructure.isFeatureChunk(context)) return Optional.empty(); // Turns the chunk coordinates into actual coordinates we can use. (Gets center of that chunk) BlockPos blockpos = context.chunkPos().getCenterAtY(0); // Find the top Y value of the land and then offset our structure to 60 blocks above that. int topLandY = context.chunkGenerator().getHeightOnGround(blockpos.getX(), blockpos.getZ(), Heightmap.Type.WORLD_SURFACE_WG, context.world()); Optional<StructurePiecesGenerator<StructurePoolFeatureConfig>> structurePiecesGenerator = StructurePoolBasedGenerator.generate( context, // Used for JigsawPlacement to get all the proper behaviors done. PoolStructurePiece::new, // Needed in order to create a list of jigsaw pieces when making the structure's layout. blockpos, // Position of the structure. Y value is ignored if last parameter is set to true. false, // Special boundary adjustments for villages. It's... hard to explain. Keep this false and make your pieces not be partially intersecting. // Either not intersecting or fully contained will make children pieces spawn just fine. It's easier that way. true // Place at heightmap (top land). Set this to false for structure to be place at the passed in blockpos's Y value instead. // Definitely keep this false when placing structures in the nether as otherwise, heightmap placing will put the structure on the Bedrock roof. ); /* * Note, you are always free to make your own JigsawPlacement class and implementation of how the structure * should generate. It is tricky but extremely powerful if you are doing something that vanilla's jigsaw system cannot do. * Such as for example, forcing 3 pieces to always spawn every time, limiting how often a piece spawns, or remove the intersection limitation of pieces. * * An example of a custom JigsawPlacement.addPieces in action can be found here (warning, it is using Mojmap mappings): * https://github.com/TelepathicGrunt/RepurposedStructures/blob/1.18.2/src/main/java/com/telepathicgrunt/repurposedstructures/world/structures/pieces/PieceLimitedJigsawManager.java */ if (structurePiecesGenerator.isPresent()) { System.out.println("Mod Structure"); } // Return the pieces generator that is now set up so that the game runs it when it needs to create the layout of structure pieces. return structurePiecesGenerator; } } ``` ```java= public class ModStructures { public static StructureFeature<?> ALTAR_STRUCTURE = new AltarStructure(); public static void registerStructureFeatures() { StructureFeatureAccessor.callRegister(More_Ores.MOD_ID + ":altar_structure", ALTAR_STRUCTURE, GenerationStep.Feature.SURFACE_STRUCTURES); } } ``` 最後再將 `registerStructureFeatures()` 加入主程式中即可 ### Mixin 以 `interface` 的形式加入檔案 `StructureFeatureAccessor.java` ```java= @Mixin(StructureFeature.class) public interface StructureFeatureAccessor { @Invoker static <F extends StructureFeature<?>> F callRegister(String name, F structureFeature, GenerationStep.Feature step) { throw new UnsupportedOperationException(); } } ``` 記得在 `<ModId>.mixin.json` 的 `mixins` 內加入 `"StructureFeatureAccessor"` ```json= { ... "mixins": [ ... "StructureFeatureAccessor" ], ... } ``` ### json 檔案位置: ``` resource └─ data └─ <ModId> ├─ structures │ └─ altar.nbt │ ├─ tags/worldgen/biome/has_structure │ └─ altar_biomes.json │ └─ worldgen ├─ configured_structure_feature │ └─ altar.json │ ├─ structure_set │ └─ altar.json │ └─ template_pool └─ altar_start_pool.json ``` #### `configured_structure_feature/altar.json`: ```json= { "type": "more_ores:altar_structure", "config": { "start_pool": "more_ores:altar_start_pool", "size": 1 }, "biomes": "#more_ores:has_structure/altar_biomes", "adapt_noise": true, "spawn_overrides": { } } ``` `type` 是根據你在 `ModStructures` 註冊之結構類型之名稱 `start_pool` 為 `start_pool` 的路徑 `size` 是在有拼圖方塊時之生成大小 `biomes` 是指定結構生成的生態域,自訂檔案位置如上 `adapt_noise` 若為真,會生成於地表附近 `spawn_overrides` 可指定甚麼生物可生成於此結構 :::danger ***spawn_overrides 為必要元素,請務必記得加入!!*** ::: #### `tags/worldgen/biome/has_structure/altar_biomes.json`: ```json= { "replace": false, "values": [ "#minecraft:desert", "#minecraft:forest", "#minecraft:taiga", "#minecraft:savanna", "#minecraft:jungle" ] } ``` #### `structure_set/altar.json`: ```json= { "structures": [ { "structure": "more_ores:altar", "weight": 1 } ], "placement": { "salt": 601684253, "spacing": 50, "separation": 20, "type": "minecraft:random_spread" } } ``` `structures` 中可放入數個結構,通常用於生成拼圖方塊結構 `salt` 修改結構種子,使兩結構不重疊。使其大而獨特! [質數產生器](https://bigprimes.org/) `spacing` 代表生成的平均距離(chunk) `seperation` 代表生成結構的最小間隔(chunk),**必須小於 spacing** `type` 為生成形式 #### `template_pool/altar_start_pool.json`: ```json= { "name": "more_ores:altar_start_pool", "fallback": "minecraft:empty", "elements": [ { "weight": 1, "element": { "location": "more_ores:altar", "processors": "minecraft:empty", "projection": "rigid", "element_type": "minecraft:single_pool_element" } } ] } ``` `location` 為結構 nbt 檔位置 `processors` 可隨機生成一些方塊於結構中,使結構更加自然,詳見[參考檔案](https://github.com/TelepathicGrunt/StructureTutorialMod/blob/1.18.2-Fabric-Jigsaw/src/main/resources/data/structure_tutorial/worldgen/processor_list/randomize_stonebrick.json) ## Feature ## ## 參考資料 [TelepathicGrunt - StructureTutorialMod](https://github.com/TelepathicGrunt/StructureTutorialMod/tree/1.18.0-Fabric-Jigsaw) --- ###### tags: `Minecraft Fabric Modding`