User Tools

Site Tools


tutorial:structures

This is an old revision of the document!


We’re going to look at registering and placing structures in your world.

To view examples of 1.14 vanilla structures in action, IglooGenerator & IglooFeature are a good place to start.

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.

To create a basic feature, we recommend creating a class that extends AbstractTempleFeature<DefaultFeatureConfig>. Various vanilla structures, such as Shipwrecks, Igloos, and Temples, use AbstracTempleFeature as a base. You will have to override the following methods:

  • shouldStartAt: return true for testing purposes.
  • getName: name of your structure
  • method_14021 [getRadius]: radius of your structure, used for placement
  • method_13774 [getSeed]: a seed to use for generation, put 0 for testing

You can pass DefaultFeatureConfig::deserialize into your constructor for testing.

For getStructureStartFactory, most vanilla structures make a class that extends StructureStart inside their Feature class:

public static class MyStructureStart extends StructureStart
  {
      public MyStructureStart (StructureFeature<?> structureFeature_1, int int_1, int int_2, Biome biome_1, MutableIntBoundingBox mutableIntBoundingBox_1, int int_3, long long_1)
      {
          super(structureFeature_1, int_1, int_2, biome_1, mutableIntBoundingBox_1, int_3, long_1);
      }
      @Override
      public void initialize(ChunkGenerator<?> chunkGenerator, StructureManager structureManager, int chunkX, int chunkZ, Biome biome)
      {
          DefaultFeatureConfig defaultFeatureConfig = chunkGenerator.getStructureConfig(biome, BattleTowers.battleTowerFeature);
          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, rotation, this.children, this.random, defaultFeatureConfig);
          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::new.

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 and was popular pre-1.13, but structure files are simply more powerful at this point and are highly recommended.

As you have probably noticed, we need to create a generator. We'll name it MyGenerator, and it's referenced in the initialize method of your StructureStart class. It doesn't need to override anything, but does require the following:

  • An Identifier that points to your structure file; use “igloo/top” if you need an example.
  • Some sort of setup method– addParts is a good name:
public static void addParts(StructureManager structureManager_1, BlockPos blockPos_1, Rotation rotation_1, 
List<StructurePiece> list_1, Random random_1, DefaultFeatureConfig featureConfig)
  
  }
  

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)); 

where the identifier is the path we created recently.

We're now going to create the Piece we just referenced; make a class called Piece that extends SimpleStructurePiece within your generator class.

Override required methods. A basic template would be:

public static class Piece extends SimpleStructurePiece {
      private final Rotation rotation;
      private final Identifier template;
      public Piece(StructureManager structureManager_1, Identifier identifier_1, BlockPos blockPos_1, Rotation rotation_1) {
          super(MyModClass.myStructurePieceType, 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("Template", this.template.toString());
          compoundTag_1.putString("Rot", this.rotation.name());
      }
      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, this.pos, structurePlacementData_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, ChunkPos chunkPos_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:

@Override
      public boolean generate(IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1, ChunkPos chunkPos_1)
      {
          int yHeight = iWorld_1.getTop(Heightmap.Type.WORLD_SURFACE_WG, this.pos.getX() + 8, this.pos.getZ() + 8);
          this.pos = this.pos.add(0, yHeight - 1, 0);
          return  super.generate(iWorld_1, random_1, mutableIntBoundingBox_1, chunkPos_1);
      }
  

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.

The last step is to register our features.

tutorial/structures.1550344861.txt.gz · Last modified: 2019/02/16 19:21 by draylar