tutorial:structures
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
features [2019/02/16 19:33] – removed accidental reference draylar | tutorial:structures [2022/08/16 20:48] – FME mineblock11 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | DELETEME //**For 1.18.2, 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, MyMainclass.myFeature); | + | |
- | | + | |
- | 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(MyModClass.myStructurePieceType, | + | |
- | 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) { | + | |
- | } | + | |
- | @Override | + | |
- | public boolean generate(IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1, | + | |
- | { | + | |
- | + | ||
- | } | + | |
- | } | + | |
- | + | ||
- | handleMetadata is where you look at data blocks within your structure | + | |
- | We set the StructurePieceType | + | We're now going to create |
+ | |||
+ | Override required methods, and add a constructor that takes in a '' | ||
+ | **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; | ||
+ | |||
+ | public MyPiece(StructureManager structureManager, | ||
+ | super(ExampleMod.MY_PIECE, | ||
+ | this.template = new Identifier(compoundTag.getString(" | ||
+ | this.rotation = BlockRotation.valueOf(compoundTag.getString(" | ||
+ | this.initializeStructureData(structureManager); | ||
+ | } | ||
+ | |||
+ | public MyPiece(StructureManager structureManager, | ||
+ | super(ExampleMod.MY_PIECE, | ||
+ | this.pos = pos; | ||
+ | this.rotation = rotation; | ||
+ | this.template = template; | ||
+ | |||
+ | this.initializeStructureData(structureManager); | ||
+ | } | ||
+ | |||
+ | private void initializeStructureData(StructureManager structureManager) { | ||
+ | Structure structure = structureManager.getStructureOrBlank(this.template); | ||
+ | StructurePlacementData placementData = (new StructurePlacementData()) | ||
+ | .setRotation(this.rotation) | ||
+ | .setMirror(BlockMirror.NONE) | ||
+ | .addProcessor(BlockIgnoreStructureProcessor.IGNORE_STRUCTURE_BLOCKS); | ||
+ | this.setStructureData(structure, | ||
+ | } | ||
+ | |||
+ | protected void toNbt(CompoundTag tag) { | ||
+ | super.toNbt(tag); | ||
+ | tag.putString(" | ||
+ | tag.putString(" | ||
+ | } | ||
@Override | @Override | ||
- | public boolean generate(IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1, ChunkPos chunkPos_1) | + | protected void handleMetadata(String metadata, BlockPos pos, ServerWorldAccess serverWorldAccess, Random random, |
- | { | + | |
- | int yHeight = iWorld_1.getTop(Heightmap.Type.WORLD_SURFACE_WG, this.pos.getX() + 8, this.pos.getZ() + 8); | + | |
- | | + | } |
- | | + | </ |
- | } | + | |
| | ||
- | In this case, we simply get the y position of the highest block in the middle of our chunk and generate the structure | + | '' |
+ | 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' | ||
+ | < | ||
- | The last step is to register our features. | + | We set the '' |
- | * StructurePieceType | + | ===== Registering Structures ===== |
- | * StructureFeature< | + | The last step is to register our structures. We're going to need to register: |
- | * StructureFeature<?> | + | |
- | We're also going to need to add the structure | + | * 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 | ||
+ | In this tutorial, we add our structure to all biomes. | ||
+ | |||
+ | <code java> | ||
+ | public class ExampleMod implements ModInitializer { | ||
+ | [...] | ||
+ | |||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | [...] | ||
- | Registering piece type: | + | BiomeModifications.addStructure(BiomeSelectors.all(), myConfigured); |
- | public static final StructurePieceType myPieceType = Registry.register(Registry.STRUCTURE_PIECE, | + | |
- | + | } | |
- | Registering feature: | + | </code> |
- | public static final StructureFeature< | + | |
- | + | ||
- | Registering structure: | + | |
- | | + | |
- | + | ||
- | To put your feature in the features list, you can use: | + | |
- | Feature.STRUCTURES.put(" | + | |
- | + | ||
- | For testing, it's a good idea to register your feature to every biome and set the spawn rate to 100% so you can be sure it's spawning and working. You probably don't want your structure floating in the water, so we'll also filter that out. Add it to every biome by iterating over the biome list and adding it as a feature and generation step: | + | |
- | for(Biome biome : Registry.BIOME) | + | ===== Result ===== |
- | { | + | You should be met with igloos. |
- | if(biome.getCategory() != Biome.Category.OCEAN && biome.getCategory() != Biome.Category.RIVER) | + | You can use below command to find your structure in the world. |
- | { | + | |
- | biome.addStructureFeature(myFeature, | + | |
- | biome.addFeature(GenerationStep.Feature.SURFACE_STRUCTURES, | + | |
- | } | + | |
- | } | + | |
- | ChanceDecoratorConfig' | + | < |
+ | /locate tutorial: | ||
+ | </ | ||
- | You need to add your structure as a feature so your biome knows it exists, and then as a generation step so it's actually generated. | + | {{tutorial: |
tutorial/structures.txt · Last modified: 2022/11/05 12:06 by jab125