# 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`