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 | ||
tutorial:structures [2019/02/16 20:58] – asie | tutorial:structures [2020/09/11 06:47] – 1.16.2 siglong | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Generating Structures ====== | ====== Generating Structures ====== | ||
+ | We’re going to look at registering and placing structures in your world. | ||
- | ===== Features ===== | + | To view examples of vanilla structures in action, '' |
- | All code used in this tutorial | + | You are going to need a feature and generator for the most basic structure. |
+ | 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 | ||
- | ==== Introduction | + | ===== Creating a Feature |
+ | To create a basic feature, we recommend creating a class that extends '' | ||
+ | Various vanilla structures, such as shipwrecks, igloos, and temples, use '' | ||
- | We’re going to look at registering and placing | + | You will have to override '' |
+ | For '' | ||
- | To view examples of 1.14 vanilla structures in action, IglooGenerator & IglooFeature are a good place to start. | + | <code java> |
+ | public class MyFeature extends StructureFeature< | ||
+ | public MyFeature(Codec< | ||
+ | super(codec); | ||
+ | } | ||
- | You are going to need a Feature and Generator for the most basic structure. The feature handles the process of registering the structure and loading it in when the world is generating-- it answers questions such as ‘should I spawn here?’ and ‘what is my name?’ The generator handles the placement of blocks or loading in a structure file if you choose to do so. | + | @Override |
+ | public StructureFeature.StructureStartFactory< | ||
+ | return Start:: | ||
+ | } | ||
- | ==== Creating a Feature ==== | + | public static class Start extends StructureStart< |
+ | public Start(StructureFeature< | ||
+ | long seed) { | ||
+ | super(feature, | ||
+ | } | ||
- | To create | + | // Called when the world attempts to spawn in a new structure, and is the gap between your feature |
+ | public void init(DynamicRegistryManager registryManager, | ||
+ | int chunkZ, Biome biome, DefaultFeatureConfig | ||
+ | int x = chunkX * 16; | ||
+ | int z = chunkZ * 16; | ||
+ | int y = chunkGenerator.getHeight(x, z, Heightmap.Type.WORLD_SURFACE_WG); | ||
+ | BlockPos pos = new BlockPos(x, y, z); | ||
+ | BlockRotation rotation = BlockRotation.random(this.random); | ||
+ | MyGenerator.addPieces(manager, | ||
+ | this.setBoundingBoxFromChildren(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
- | * shouldStartAt: | + | ===== Creating a Generator ===== |
- | * getName: name of your structure | + | As you have probably noticed, we need to create |
- | * method_14021 [getRadius]: | + | |
- | * method_13774 [getSeed]: | + | |
- | You can pass DefaultFeatureConfig:: | + | This is where structure files and generating straight from a '' |
- | For getStructureStartFactory, most vanilla structures make a class that extends StructureStart inside their Feature class: | + | * If you want, you can simply override '' |
+ | * Use structure files. These are rather powerful at this point and are highly recommended. | ||
- | public static class MyStructureStart extends StructureStart | + | In this tutorial, we'll use a structure |
- | { | + | It doesn' |
- | public MyStructureStart (StructureFeature<?> | + | * An Identifier that points to your structure file; use " |
- | { | + | * Some sort of setup method - '' |
- | super(structureFeature_1, | + | |
- | } | + | |
- | @Override | + | |
- | public void initialize(ChunkGenerator<?> | + | |
- | { | + | |
- | DefaultFeatureConfig defaultFeatureConfig = chunkGenerator.getStructureConfig(biome, | + | |
- | int x = chunkX * 16; | + | |
- | int z = chunkZ * 16; | + | |
- | BlockPos startingPos = new BlockPos(x, 0, z); | + | |
- | Rotation rotation = Rotation.values()[this.random.nextInt(Rotation.values().length)]; | + | |
- | MyGenerator.addParts(structureManager, startingPos, | + | |
- | this.setBoundingBoxFromChildren(); | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | This is called when the world attempts to spawn in a new structure, and is the gap between your Feature and Generator. The reference to the variable in your main class doesn' | + | |
- | This is where structure files and generating straight from a generate method part ways. There are two ways to go about this: | + | <code java> |
+ | public class MyGenerator { | ||
+ | private static final Identifier IGLOO_TOP = new Identifier(" | ||
- | * 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 and was popular pre-1.13. | + | public static void addPieces(StructureManager manager, BlockPos pos, BlockRotation rotation, List< |
- | * Use structure files and a Generator. These are rather powerful at this point and are highly recommended. | + | |
+ | } | ||
+ | } | ||
+ | </ | ||
- | ==== Creating a Generator ==== | + | In your '' |
- | As you have probably noticed, we need to create a generator. We'll name it MyGenerator, | + | We're now going to create |
- | * An Identifier | + | Override required methods, and add a constructor |
- | * Some sort of setup method - addParts | + | **toNbt isn't required but is available |
+ | We're also implementing '' | ||
+ | We also have 2 constructors: | ||
+ | A basic template would be: | ||
- | | + | <code java> |
- | | + | public static |
- | + | | |
- | } | + | |
- | + | ||
- | In your addParts method, you can choose which structure pieces are added to your generation process. You can add a piece like this: | + | |
- | | + | |
- | + | super(ExampleMod.MY_PIECE, | |
- | where the identifier is the path we created recently. | + | this.template = new Identifier(compoundTag.getString(" |
+ | | ||
+ | this.initializeStructureData(structureManager); | ||
+ | } | ||
- | We're now going to create the Piece we just referenced; make a class called Piece that extends SimpleStructurePiece //within your generator class//. | + | public MyPiece(StructureManager structureManager, |
+ | super(ExampleMod.MY_PIECE, | ||
+ | this.pos = pos; | ||
+ | this.rotation = rotation; | ||
+ | this.template = template; | ||
- | Override required methods, and add a constructor that takes in a StructureManager, | + | this.initializeStructureData(structureManager); |
+ | } | ||
- | | + | private void initializeStructureData(StructureManager structureManager) { |
- | | + | Structure |
- | private Identifier template; | + | StructurePlacementData |
- | + | | |
- | public Piece(StructureManager structureManager_1, | + | this.setStructureData(structure, this.pos, |
- | super(MyModClass.myStructurePieceType, | + | } |
- | + | ||
- | this.pos = blockPos_1; | + | |
- | this.rotation = rotation_1; | + | |
- | this.template = identifier_1; | + | |
- | + | ||
- | this.setStructureData(structureManager_1); | + | |
- | } | + | |
- | + | ||
- | public Piece(StructureManager structureManager_1, | + | |
- | super(MyModClass.myStructurePieceType, | + | |
- | this.identifier = new Identifier(compoundTag_1.getString(" | + | |
- | this.rotation = Rotation.valueOf(compoundTag_1.getString(" | + | |
- | this.setStructureData(structureManager_1); | + | |
- | } | + | |
- | + | ||
- | @Override | + | |
- | protected | + | |
- | super.toNbt(compoundTag_1); | + | |
- | compoundTag_1.putString(" | + | |
- | compoundTag_1.putString(" | + | |
- | } | + | |
- | + | ||
- | public void setStructureData(StructureManager structureManager) | + | |
- | | + | |
- | Structure | + | |
- | StructurePlacementData | + | |
- | this.setStructureData(structure_1, this.pos, | + | |
- | } | + | |
- | + | ||
- | @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 and do tasks based on what you find. In vanilla structures, data blocks are placed above chests so they can be filled with loot in this method. | + | |
- | We set the StructurePieceType to MyModClass.myStructurePiece type; this is the variable that holds your registered structure piece. We'll handle that after we finish the generate function, which sets the position of your structure and generates it: | + | 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 off that position. | + | '' |
+ | In vanilla structures, data blocks are placed above chests so they can be filled with loot in this method. | ||
- | ==== Registering Features ==== | + | We set the '' |
- | The last step is to register our features. We're going to need to register: | + | ===== Registering Structures ===== |
+ | The last step is to register our structures. We're going to need to register: | ||
- | * StructurePieceType | + | * structure |
- | * StructureFeature< | + | * pieces |
- | * StructureFeature<?> | + | * configured structure |
- | We're also going to need to add the structure to the STRUCTURES list and add it to each biome as a feature and generation step. | + | <code java> |
+ | public class ExampleMod implements ModInitializer { | ||
+ | public static final StructurePieceType MY_PIECE = MyGenerator.MyPiece:: | ||
+ | private static final StructureFeature< | ||
+ | public static final ConfiguredStructureFeature<?, | ||
- | Registering piece type: | + | @Override |
- | public | + | public |
- | + | | |
- | Registering feature: | + | |
- | public static final StructureFeature< | + | .step(GenerationStep.Feature.SURFACE_STRUCTURES) |
- | + | .defaultConfig(32, 8, 12345) | |
- | Registering structure: | + | |
- | public static final StructureFeature<?> | + | .register(); |
- | + | ||
- | To put your feature in the features list, you can use: | + | |
- | Feature.STRUCTURES.put("My Awesome Feature", | + | |
- | + | ||
- | 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) | + | BuiltinRegistries.add(BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE, new Identifier(" |
- | { | + | MY_CONFIGURED); |
- | if(biome.getCategory() != Biome.Category.OCEAN && biome.getCategory() != Biome.Category.RIVER) | + | } |
- | { | + | } |
- | biome.addStructureFeature(myFeature, new DefaultFeatureConfig()); | + | </ |
- | biome.addFeature(GenerationStep.Feature.SURFACE_STRUCTURES, Biome.configureFeature(myFeature, | + | |
- | } | + | |
- | } | + | |
- | ChanceDecoratorConfig's argument is basically how many chunks it will skip over before generating. 0 is every chunk, 1 is every other, and 100 is every 100. | + | ===== Adding a configured feature to a biome ===== |
+ | Vanilla features generated in the plain biomes are listed in '' | ||
+ | We modify this method to add our structure to the plain biomes. | ||
- | 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. | + | <code java> |
+ | @Mixin(DefaultBiomeFeatures.class) | ||
+ | public class DefaultBiomeFeaturesMixin { | ||
+ | @Inject(method = " | ||
+ | private static void addPlainsFeatures(GenerationSettings.Builder builder, CallbackInfo ci) { | ||
+ | builder.structureFeature(ExampleMod.MY_CONFIGURED); | ||
+ | } | ||
+ | } | ||
- | Load into your world, and if all went well, you should be met with a //lot// of Igloos. | + | </code> |
+ | ===== Result ===== | ||
+ | You should be met with igloos in the plain biomes. | ||
+ | You can use below command to find your structure in the world. | ||
+ | < | ||
+ | /locate tutorial: | ||
+ | </ | ||
+ | {{tutorial: | ||
tutorial/structures.txt · Last modified: 2022/11/05 12:06 by jab125