tutorial:structures
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
features [2019/02/16 19:15] – added start of structure tutorial draylar | tutorial:structures [2022/11/05 12:06] (current) – jab125 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | :!: //**For versions 1.18 and beyond, the Fabric Structure API no longer exists and structures can be done entirely in datapacks. [[https:// | ||
+ | |||
+ | ====== Adding Structure Features [1.16.3] ====== | ||
We’re going to look at registering and placing structures in your world. | We’re going to look at registering and placing structures in your world. | ||
- | To view examples of 1.14 vanilla structures in action, | + | To view examples of vanilla structures in action, |
- | You are going to need a Feature | + | You are going to need a feature |
+ | The feature handles the process of registering the structure and loading it in when the world is generating. | ||
+ | The generator handles the placement of blocks or loading in a structure file if you choose to do so. | ||
- | To create a basic feature, we recommend creating a class that extends AbstractTempleFeature< | + | Note that this tutorial depends on [[https:// |
+ | If the API doesn' | ||
- | * shouldStartAt: | + | ===== Creating a Feature ===== |
- | * getName: name of your structure | + | To create a basic feature, we recommend creating |
- | * method_14021 [getRadius]: | + | Various vanilla structures, such as shipwrecks, igloos, and temples, use '' |
- | * method_13774 [getSeed]: | + | |
- | You can pass DefaultFeatureConfig:: | + | You will have to override '' |
+ | For '' | ||
- | For getStructureStartFactory, | + | <code java> |
+ | public | ||
+ | public MyFeature(Codec< | ||
+ | super(codec); | ||
+ | } | ||
- | | + | |
- | { | + | public StructureFeature.StructureStartFactory<DefaultFeatureConfig> getStructureStartFactory() { |
- | | + | |
- | | + | } |
- | | + | |
- | } | + | public |
- | | + | public Start(StructureFeature<DefaultFeatureConfig> feature, int chunkX, int chunkZ, |
- | public | + | long seed) { |
- | { | + | super(feature, chunkX, chunkZ, box, references, seed); |
- | DefaultFeatureConfig defaultFeatureConfig = chunkGenerator.getStructureConfig(biome, BattleTowers.battleTowerFeature); | + | |
- | | + | |
- | int z = chunkZ * 16; | + | |
- | BlockPos startingPos = new BlockPos(x, 0, z); | + | |
- | | + | |
- | | + | |
- | this.setBoundingBoxFromChildren(); | + | |
- | } | + | |
} | } | ||
- | | ||
- | This is called when the world attempts to spawn in a new structure, and is the gap between your Feature and Generator. You can return this in getStructureStartFactory with return MyStructureStart:: | ||
- | This is where structure files and generating straight from a generate method part ways. If you want, you can simply override generate in your Feature class and use setBlockState to place blocks directly in the world. This is a valid option | + | // Called when the world attempts to spawn in a new structure, and is the gap between your feature |
+ | public void init(DynamicRegistryManager registryManager, ChunkGenerator chunkGenerator, | ||
+ | int chunkZ, Biome biome, DefaultFeatureConfig config) { | ||
+ | int x = chunkX * 16; | ||
+ | int z = chunkZ * 16; | ||
+ | int y = chunkGenerator.getHeight(x, | ||
+ | BlockPos pos = new BlockPos(x, y, z); | ||
+ | BlockRotation rotation = BlockRotation.random(this.random); | ||
+ | MyGenerator.addPieces(manager, | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
- | As you have probably noticed, we need to create a generator. | + | ===== Creating a Generator ===== |
+ | As you have probably noticed, we need to create a generator. | ||
- | * An Identifier that points to your structure file; use " | + | This is where structure files and generating straight from a '' |
- | * Some sort of setup method-- addParts | + | |
- | | + | |
- | List< | + | * Use structure files. These are rather powerful at this point and are highly recommended. |
- | + | ||
- | } | + | |
- | + | ||
- | In your addParts method, you can choose which structure pieces are added to your generation process. You can add a piece like this: | + | |
- | list_1.add(new MyGenerator.Piece(structureManager_1, identifier, blockPos, rotation_1)); | + | In this tutorial, we'll use a structure file. |
- | | + | It doesn' |
- | where the identifier | + | * An identifier |
+ | | ||
- | We're now going to create the Piece we just referenced; make a class called Piece that extends SimpleStructurePiece | + | <code java> |
+ | public | ||
+ | private static final Identifier IGLOO_TOP = new Identifier(" | ||
- | Override required methods. A basic template would be: | + | public static void addPieces(StructureManager manager, BlockPos pos, BlockRotation rotation, List< |
+ | pieces.add(new MyPiece(manager, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
- | public static class Piece extends SimpleStructurePiece { | + | In your '' |
- | private final Rotation rotation; | + | |
- | private final Identifier template; | + | |
- | public Piece(StructureManager structureManager_1, Identifier identifier_1, | + | |
- | super(StructurePieceType.VILLAGE, 0); | + | |
- | this.pos = blockPos_1; | + | |
- | this.rotation = rotation_1; | + | |
- | this.template = identifier_1; | + | |
- | this.method_14837(structureManager_1); | + | |
- | } | + | |
- | @Override | + | |
- | protected void toNbt(CompoundTag compoundTag_1) { | + | |
- | super.toNbt(compoundTag_1); | + | |
- | compoundTag_1.putString(" | + | |
- | compoundTag_1.putString(" | + | |
- | } | + | |
- | private void method_14837(StructureManager structureManager_1) { | + | |
- | Structure structure_1 = structureManager_1.getStructureOrBlank(this.template); | + | |
- | StructurePlacementData structurePlacementData_1 = (new StructurePlacementData()).setRotation(this.rotation).setMirrored(Mirror.NONE).setPosition(MyGenerator.startingPos).addProcessor(BlockIgnoreStructureProcessor.IGNORE_STRUCTURE_BLOCKS); | + | |
- | this.setStructureData(structure_1, | + | |
- | } | + | |
- | @Override | + | |
- | protected void handleMetadata(String string_1, BlockPos blockPos_1, IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1) { | + | |
- | } | + | We're now going to create the piece we just referenced; make a class called '' |
- | @Override | + | |
- | public | + | Override |
- | { | + | **toNbt isn't required but is available if you need it**. |
- | + | We're also implementing '' | |
- | } | + | We also have 2 constructors: |
- | } | + | A basic template would be: |
+ | |||
+ | <code java> | ||
+ | public static class MyPiece extends SimpleStructurePiece { | ||
+ | private final BlockRotation rotation; | ||
+ | private final Identifier template; | ||
+ | |||
+ | | ||
+ | super(ExampleMod.MY_PIECE, compoundTag); | ||
+ | this.template = new Identifier(compoundTag.getString(" | ||
+ | this.rotation = BlockRotation.valueOf(compoundTag.getString(" | ||
+ | this.initializeStructureData(structureManager); | ||
+ | } | ||
+ | |||
+ | public MyPiece(StructureManager structureManager, BlockPos pos, Identifier template, BlockRotation rotation) { | ||
+ | | ||
+ | this.pos = pos; | ||
+ | this.rotation = rotation; | ||
+ | this.template = template; | ||
+ | |||
+ | this.initializeStructureData(structureManager); | ||
+ | } | ||
+ | |||
+ | private void initializeStructureData(StructureManager structureManager) | ||
+ | | ||
+ | StructurePlacementData placementData = (new StructurePlacementData()) | ||
+ | .setRotation(this.rotation) | ||
+ | .setMirror(BlockMirror.NONE) | ||
+ | .addProcessor(BlockIgnoreStructureProcessor.IGNORE_STRUCTURE_BLOCKS); | ||
+ | this.setStructureData(structure, | ||
+ | } | ||
+ | |||
+ | protected void toNbt(CompoundTag tag) { | ||
+ | | ||
+ | tag.putString(" | ||
+ | tag.putString(" | ||
+ | | ||
+ | |||
+ | @Override | ||
+ | protected void handleMetadata(String metadata, BlockPos pos, ServerWorldAccess serverWorldAccess, | ||
+ | BlockBox boundingBox) { | ||
+ | } | ||
+ | } | ||
+ | </ | ||
| | ||
+ | '' | ||
+ | This can be good for dynamic stuff such as placing certain mobs based on what mod is on and so on. | ||
+ | |||
+ | In vanilla structures, data blocks are placed above chests so they can be filled with loot in this method. | ||
+ | HOWEVER, you do not need to use datablocks to place chests with loot. Instead, use this command to set a north facing chest with a loottable. | ||
+ | Save this chest into your structure' | ||
+ | < | ||
+ | |||
+ | We set the '' | ||
+ | |||
+ | ===== Registering Structures ===== | ||
+ | The last step is to register our structures. We're going to need to register: | ||
+ | |||
+ | * structure | ||
+ | * piece | ||
+ | * configured structure | ||
+ | |||
+ | <code java> | ||
+ | public class ExampleMod implements ModInitializer { | ||
+ | public static final StructurePieceType MY_PIECE = MyGenerator.MyPiece:: | ||
+ | private static final StructureFeature< | ||
+ | private static final ConfiguredStructureFeature<?, | ||
+ | |||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | Registry.register(Registry.STRUCTURE_PIECE, | ||
+ | FabricStructureBuilder.create(new Identifier(" | ||
+ | .step(GenerationStep.Feature.SURFACE_STRUCTURES) | ||
+ | .defaultConfig(32, | ||
+ | .adjustsSurface() | ||
+ | .register(); | ||
+ | |||
+ | RegistryKey< | ||
+ | new Identifier(" | ||
+ | BuiltinRegistries.add(BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE, | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Adding a configured feature to biomes ===== | ||
+ | In this tutorial, we add our structure to all biomes. | ||
+ | |||
+ | <code java> | ||
+ | public class ExampleMod implements ModInitializer { | ||
+ | [...] | ||
+ | |||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | [...] | ||
+ | |||
+ | BiomeModifications.addStructure(BiomeSelectors.all(), | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Result ===== | ||
+ | You should be met with igloos. | ||
+ | You can use below command to find your structure in the world. | ||
+ | |||
+ | < | ||
+ | /locate tutorial: | ||
+ | </ | ||
+ | {{tutorial: | ||
tutorial/structures.1550344526.txt.gz · Last modified: 2019/02/16 19:15 by draylar