tutorial:trees
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| tutorial:trees [2021/06/20 17:08] – Add a note about the PR into Fabric API redgrapefruit | tutorial:trees [2022/12/21 01:40] (current) – 1.19.3 haykam | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ===== Adding Trees [1.17] (Advanced) ===== | + | ===== Adding Trees [1.19.2] (Advanced) ===== |
| - | + | It is recommended that you learn how to create a [[tutorial: | |
| - | It is recommended that you learn how to create a '' | + | |
| - | See [[https:// | + | |
| Trees are a great way to expand Minecraft' | Trees are a great way to expand Minecraft' | ||
| Beware that this topic is advanced and preferably you should have decent experience with modding world generation in Minecraft before starting. | Beware that this topic is advanced and preferably you should have decent experience with modding world generation in Minecraft before starting. | ||
| - | ==== API news ==== | + | Firstly, you need to understand that a '' |
| - | + | You need a feature, in our case '' | |
| - | Currently, '' | + | |
| - | + | ||
| - | I'm working on a [[https:// | + | |
| - | I hope everything works out and the pull request will be merged into Fabric API for all of us to use! | + | |
| ===== Creating a Simple Tree ===== | ===== Creating a Simple Tree ===== | ||
| ==== Architecture ==== | ==== Architecture ==== | ||
| - | + | Minecraft' | |
| - | Minecraft' | + | |
| Here's an overview: | Here's an overview: | ||
| + | - '' | ||
| + | - '' | ||
| - '' | - '' | ||
| - | | + | - '' |
| - | - '' | + | - '' |
| - | | + | |
| - | - '' | + | |
| - | - '' | + | |
| - | - '' | + | |
| - | The creation | + | You can create custom implementations |
| ==== Creating the ConfiguredFeature ==== | ==== Creating the ConfiguredFeature ==== | ||
| + | We don't need to create a new '' | ||
| + | Add this into your '' | ||
| - | We won't need to create a new '' | + | <code java> |
| - | Add this into your '' | + | public static final RegistryEntry< |
| - | + | // Configure | |
| - | <code java [enable_line_numbers=" | + | new TreeFeatureConfig.Builder( |
| - | + | | |
| - | public static final ConfiguredFeature< | + | new StraightTrunkPlacer(8, |
| - | // Reconfigure | + | BlockStateProvider.of(Blocks.DIAMOND_BLOCK), |
| - | | + | new BlobFoliagePlacer(ConstantIntProvider.create(5), |
| - | // The SimpleBlockStateProvider just returns what you passed in it. This provider contains the trunk of your tree | + | new TwoLayersFeatureSize(1, |
| - | new SimpleBlockStateProvider(Blocks.NETHERITE_BLOCK.getDefaultState()), | + | ).build())); |
| - | // The StraightTrunkPlacer places a straight trunk up in the air. | + | |
| - | | + | |
| - | // This provider contains the foliage | + | |
| - | new SimpleBlockStateProvider(Blocks.DIAMOND_BLOCK.getDefaultState()), | + | |
| - | // This provider | + | |
| - | new SimpleBlockStateProvider(RICH_SAPLING.getDefaultState()), | + | |
| - | // The BlobFoliagePlacer places a big blob of your foliage. | + | |
| - | // The ConstantIntProvider simply returns the integer that you gave it. | + | |
| - | // The first provider determines the radius of the blob | + | |
| - | // The second provider determines the offset of the foliage from the trunk | + | |
| - | // The third integer determines the height of your foliage | + | |
| - | new BlobFoliagePlacer(ConstantIntProvider.create(5), | + | |
| - | // The TwoLayersFeatureSize manages | + | |
| - | // The first integer is the layer limit | + | |
| - | // The second integer is the lower layer size | + | |
| - | // The third integer is the higher layer size | + | |
| - | // Most of the time, these values should not be edited | + | |
| - | | + | |
| - | ).build()) | + | |
| - | | + | |
| - | // Here we use the chance Decorator with a 30% chance | + | |
| - | .decorate(Decorator.CHANCE.configure(new ChanceDecoratorConfig(30))); | + | |
| - | + | ||
| - | </ | + | |
| - | + | ||
| - | Now we just register the '' | + | |
| - | + | ||
| - | <code java [enable_line_numbers=" | + | |
| - | + | ||
| - | static { | + | |
| - | RegistryKey< | + | |
| - | + | ||
| - | Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, | + | |
| - | + | ||
| - | // You should use VEGETAL_DECORATION step for trees | + | |
| - | BiomeModifications.addFeature(BiomeSelectors.all(), | + | |
| - | } | + | |
| </ | </ | ||
| ==== Creating the sapling ==== | ==== Creating the sapling ==== | ||
| - | |||
| A sapling is a special kind of block to grow trees that requires a '' | A sapling is a special kind of block to grow trees that requires a '' | ||
| === Creating the SaplingGenerator === | === Creating the SaplingGenerator === | ||
| - | |||
| A simple generator that takes your tree's '' | A simple generator that takes your tree's '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| public class RichSaplingGenerator extends SaplingGenerator { | public class RichSaplingGenerator extends SaplingGenerator { | ||
| - | private final ConfiguredFeature< | + | |
| - | + | @Override | |
| - | public RichSaplingGenerator(ConfiguredFeature<?, | + | protected |
| - | this.feature = (ConfiguredFeature< | + | return |
| - | } | + | } |
| - | + | ||
| - | | + | |
| - | @Override | + | |
| - | protected ConfiguredFeature< | + | |
| - | return | + | |
| - | } | + | |
| } | } | ||
| - | |||
| </ | </ | ||
| Line 112: | Line 57: | ||
| === Creating the SaplingBlock === | === Creating the SaplingBlock === | ||
| + | Creating the block itself requires you to extend '' | ||
| - | Creating the block itself requires you to extend from '' | + | <code java> |
| - | + | ||
| - | <code java [enable_line_numbers=" | + | |
| public class RichSaplingBlock extends SaplingBlock { | public class RichSaplingBlock extends SaplingBlock { | ||
| - | | + | |
| - | super(generator, | + | super(generator, |
| - | } | + | } |
| } | } | ||
| - | |||
| </ | </ | ||
| === Registering the SaplingBlock === | === Registering the SaplingBlock === | ||
| + | To register your sapling, follow the normal steps for registering a block (see [[tutorial: | ||
| - | To register your sapling, follow | + | Put this in the class you use for your blocks: |
| - | but pass in the instance of your generator with the '' | + | |
| - | Put this after your '' | + | <code java> |
| - | + | public static final RichSaplingBlock | |
| - | <code java [enable_line_numbers=" | + | |
| - | + | ||
| - | public static final RICH_SAPLING = new RichSaplingBlock(new RichSaplingGenerator(RICH_TREE), FabricBlockSettings.copyOf(Blocks.BIRCH_SAPLING.getDefaultState())); | + | |
| - | static { | + | public |
| - | | + | Registry.register(Registries.BLOCK, new Identifier(" |
| - | | + | Registry.register(Registries.ITEM, new Identifier(" |
| } | } | ||
| Line 144: | Line 83: | ||
| ===== Creating a TrunkPlacer ===== | ===== Creating a TrunkPlacer ===== | ||
| - | |||
| A '' | A '' | ||
| ==== Vanilla TrunkPlacers ==== | ==== Vanilla TrunkPlacers ==== | ||
| - | |||
| Before creating one, look at the reusable vanilla '' | Before creating one, look at the reusable vanilla '' | ||
| - | * '' | + | * '' |
| * '' | * '' | ||
| * '' | * '' | ||
| - | * '' | + | * '' |
| ==== Creating a TrunkPlacerType ==== | ==== Creating a TrunkPlacerType ==== | ||
| - | |||
| A '' | A '' | ||
| Line 168: | Line 104: | ||
| Here's our mixin, and don't forget to add it to your mixin config: | Here's our mixin, and don't forget to add it to your mixin config: | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| @Mixin(TrunkPlacerType.class) | @Mixin(TrunkPlacerType.class) | ||
| public interface TrunkPlacerTypeInvoker { | public interface TrunkPlacerTypeInvoker { | ||
| - | @Invoker | + | @Invoker(" |
| - | static <TTrunkPlacer | + | static <P extends TrunkPlacer> |
| - | throw new UnsupportedOperationException(); | + | throw new IllegalStateException(); |
| } | } | ||
| } | } | ||
| - | |||
| </ | </ | ||
| ==== Creating the TrunkPlacer ==== | ==== Creating the TrunkPlacer ==== | ||
| - | |||
| A '' | A '' | ||
| * A codec for serialization. Codecs are a topic of their own, here we'll just use the '' | * A codec for serialization. Codecs are a topic of their own, here we'll just use the '' | ||
| * A getter where you return your '' | * A getter where you return your '' | ||
| - | * The '' | + | * The '' |
| Our '' | Our '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| public class RichTrunkPlacer extends TrunkPlacer { | public class RichTrunkPlacer extends TrunkPlacer { | ||
| // Use the fillTrunkPlacerFields to create our codec | // Use the fillTrunkPlacerFields to create our codec | ||
| - | public static final Codec< | + | public static final Codec< |
| - | | + | |
| public RichTrunkPlacer(int baseHeight, int firstRandomHeight, | public RichTrunkPlacer(int baseHeight, int firstRandomHeight, | ||
| Line 209: | Line 142: | ||
| public List< | public List< | ||
| // Set the ground beneath the trunk to dirt | // Set the ground beneath the trunk to dirt | ||
| - | | + | setToDirt(world, |
| | | ||
| - | // Iterate until the trunk height limit and place two blocks using the built-in | + | // Iterate until the trunk height limit and place two blocks using the getAndSetState method |
| for (int i = 0; i < height; i++) { | for (int i = 0; i < height; i++) { | ||
| - | getAndSetState(world, | + | |
| - | getAndSetState(world, | + | |
| } | } | ||
| Line 223: | Line 156: | ||
| } | } | ||
| } | } | ||
| - | |||
| </ | </ | ||
| ==== Registering and using your TrunkPlacer ==== | ==== Registering and using your TrunkPlacer ==== | ||
| - | + | Using your invoker, create and register an instance of a '' | |
| - | Using your invoker, create and register an instance of a '' | + | |
| Put this into your '' | Put this into your '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| - | public static final TrunkPlacerType< | + | public static final TrunkPlacerType< |
| </ | </ | ||
| Now just replace your '' | Now just replace your '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| [...] | [...] | ||
| new RichTrunkPlacer(8, | new RichTrunkPlacer(8, | ||
| Line 243: | Line 174: | ||
| ===== Creating a FoliagePlacer ===== | ===== Creating a FoliagePlacer ===== | ||
| - | |||
| A '' | A '' | ||
| ==== Vanilla FoliagePlacers ==== | ==== Vanilla FoliagePlacers ==== | ||
| - | |||
| Before creating a '' | Before creating a '' | ||
| Line 255: | Line 184: | ||
| ==== Creating a FoliagePlacerType ==== | ==== Creating a FoliagePlacerType ==== | ||
| - | |||
| A '' | A '' | ||
| - | Similarly to the '' | + | Similarly to the '' |
| Our mixin will look almost exactly the same. Don't forget to add it to your mixin config! | Our mixin will look almost exactly the same. Don't forget to add it to your mixin config! | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| @Mixin(FoliagePlacerType.class) | @Mixin(FoliagePlacerType.class) | ||
| public interface FoliagePlacerTypeInvoker { | public interface FoliagePlacerTypeInvoker { | ||
| @Invoker | @Invoker | ||
| - | static <TFoliagePlacer | + | static <P extends FoliagePlacer> |
| - | throw new UnsupportedOperationException(); | + | throw new IllegalStateException(); |
| } | } | ||
| } | } | ||
| - | |||
| </ | </ | ||
| ==== Creating the FoliagePlacer ==== | ==== Creating the FoliagePlacer ==== | ||
| - | |||
| A '' | A '' | ||
| Line 285: | Line 210: | ||
| Our '' | Our '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| public class RichFoliagePlacer extends FoliagePlacer { | public class RichFoliagePlacer extends FoliagePlacer { | ||
| - | // Here we use the built-in fillFoliagePlacerFields for basic fields | ||
| - | // | ||
| // For the foliageHeight we use a codec generated by IntProvider.createValidatingCodec | // For the foliageHeight we use a codec generated by IntProvider.createValidatingCodec | ||
| - | // As the method' | + | // As the method' |
| - | // In fieldOf we put the name of the codec entry | + | |
| - | // In forGetter we return the value of the entry through a lambda expression | + | |
| - | // | + | |
| // To add more fields into your TrunkPlacer/ | // To add more fields into your TrunkPlacer/ | ||
| // | // | ||
| // For an example of creating your own type of codec, see the IntProvider.createValidatingCodec method' | // For an example of creating your own type of codec, see the IntProvider.createValidatingCodec method' | ||
| - | public static final Codec< | + | public static final Codec< |
| - | | + | fillFoliagePlacerFields(instance) |
| - | fillFoliagePlacerFields(instance) | + | .and(IntProvider.createValidatingCodec(1, |
| - | .and(IntProvider | + | .apply(instance, |
| - | | + | |
| - | | + | |
| - | | + | |
| - | .apply(instance, | + | |
| private final IntProvider foliageHeight; | private final IntProvider foliageHeight; | ||
| Line 313: | Line 229: | ||
| this.foliageHeight = foliageHeight; | this.foliageHeight = foliageHeight; | ||
| + | } | ||
| + | |||
| + | public IntProvider getFoliageHeight() { | ||
| + | return this.foliageHeight; | ||
| } | } | ||
| Line 325: | Line 245: | ||
| for ( | for ( | ||
| - | | + | |
| - | Vec3i vec = center.subtract(new Vec3i(radius, | + | Vec3i vec = center.subtract(new Vec3i(radius, |
| - | // End in X: center+radius | + | // End in X: center + radius |
| - | vec.compareTo(center.add(new Vec3i(radius, | + | vec.compareTo(center.add(new Vec3i(radius, |
| - | // Move by 1 each time | + | // Move by 1 each time |
| - | vec.add(1, 0, 0)) { | + | vec.add(1, 0, 0)) { |
| - | placeFoliageBlock(world, | + | |
| } | } | ||
| - | for ( | + | for (Vec3i vec = center.subtract(new Vec3i(0, radius, 0)); vec.compareTo(center.add(new Vec3i(0, radius, 0))) == 0; vec.add(0, 1, 0)) { |
| - | // Start from Y: center-radius | + | |
| - | | + | |
| - | // End in Y: center+radius | + | |
| - | | + | |
| - | // Move by 1 each time | + | |
| - | | + | |
| - | | + | |
| - | placeFoliageBlock(world, | + | |
| } | } | ||
| } | } | ||
| Line 360: | Line 273: | ||
| </ | </ | ||
| - | |||
| ==== Registering and using your FoliagePlacer ==== | ==== Registering and using your FoliagePlacer ==== | ||
| - | |||
| This process is almost exactly the same, just use your invoker to create and register the '' | This process is almost exactly the same, just use your invoker to create and register the '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| - | public static final FoliagePlacerType< | + | public static final FoliagePlacerType< |
| </ | </ | ||
| and replace the old '' | and replace the old '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| [...] | [...] | ||
| new RichFoliagePlacer(ConstantIntProvider.create(5), | new RichFoliagePlacer(ConstantIntProvider.create(5), | ||
| Line 378: | Line 289: | ||
| ===== Creating a TreeDecorator ===== | ===== Creating a TreeDecorator ===== | ||
| - | + | A '' | |
| - | A '' | + | |
| If you have a game development background, it's essentially a post-processor, | If you have a game development background, it's essentially a post-processor, | ||
| ==== Vanilla TreeDecorators ==== | ==== Vanilla TreeDecorators ==== | ||
| - | + | Almost none vanilla '' | |
| - | Almost none vanilla '' | + | |
| and '' | and '' | ||
| Line 390: | Line 299: | ||
| ==== Creating a TreeDecoratorType ==== | ==== Creating a TreeDecoratorType ==== | ||
| - | |||
| A '' | A '' | ||
| Line 397: | Line 305: | ||
| Our mixin will look almost exactly the same, don't forget to add it to your mixin config: | Our mixin will look almost exactly the same, don't forget to add it to your mixin config: | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| @Mixin(TreeDecoratorType.class) | @Mixin(TreeDecoratorType.class) | ||
| public interface TreeDecoratorTypeInvoker { | public interface TreeDecoratorTypeInvoker { | ||
| @Invoker | @Invoker | ||
| - | static <TTreeDecorator | + | static <P extends TreeDecorator> |
| - | throw new UnsupportedOperationException(); | + | throw new IllegalStateException(); |
| } | } | ||
| } | } | ||
| - | |||
| </ | </ | ||
| ==== Creating the TreeDecorator ==== | ==== Creating the TreeDecorator ==== | ||
| - | |||
| A '' | A '' | ||
| Line 419: | Line 324: | ||
| Our '' | Our '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| public class RichTreeDecorator extends TreeDecorator { | public class RichTreeDecorator extends TreeDecorator { | ||
| public static final RichTreeDecorator INSTANCE = new RichTreeDecorator(); | public static final RichTreeDecorator INSTANCE = new RichTreeDecorator(); | ||
| - | // Our constructor doesn' | + | // Our constructor doesn' |
| public static final Codec< | public static final Codec< | ||
| + | | ||
| + | private RichTreeDecorator() {} | ||
| @Override | @Override | ||
| Line 432: | Line 338: | ||
| @Override | @Override | ||
| - | public void generate(TestableWorld world, BiConsumer< | + | public void generate(TreeDecorator.Generator generator) { |
| // Iterate through block positions | // Iterate through block positions | ||
| - | | + | |
| - | // Pick a value from 0 to 100 and if it' | + | Random random = generator.getRandom(); |
| + | // Pick a value from 0 (inclusive) | ||
| // This is the chance for spawning the gold block | // This is the chance for spawning the gold block | ||
| - | if (random.nextInt(100) <= 25) { | + | if (random.nextInt(4) == 0) { |
| - | // Pick a random value from 0 to 3 and determine the side where the gold block will be placed using it | + | // Pick a random value from 0 to 4 and determine the side where the gold block will be placed using it |
| int sideRaw = random.nextInt(4); | int sideRaw = random.nextInt(4); | ||
| Direction side = switch (sideRaw) { | Direction side = switch (sideRaw) { | ||
| Line 445: | Line 352: | ||
| case 2 -> Direction.EAST; | case 2 -> Direction.EAST; | ||
| case 3 -> Direction.WEST; | case 3 -> Direction.WEST; | ||
| - | default -> throw new ArithmeticException(" | + | default -> throw new ArithmeticException(" |
| }; | }; | ||
| Line 455: | Line 362: | ||
| replacer.accept(targetPosition, | replacer.accept(targetPosition, | ||
| } | } | ||
| - | } | + | }); |
| } | } | ||
| } | } | ||
| - | |||
| </ | </ | ||
| ==== Registering and using your TreeDecorator ==== | ==== Registering and using your TreeDecorator ==== | ||
| - | |||
| First, create your '' | First, create your '' | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| - | public static final TreeDecoratorType< | + | public static final TreeDecoratorType< |
| </ | </ | ||
| Then, between the creation of your '' | Then, between the creation of your '' | ||
| - | <code java [enable_line_numbers=" | + | |
| + | <code java> | ||
| [...] | [...] | ||
| .decorators(Collections.singletonList(RichTreeDecorator.INSTANCE)) | .decorators(Collections.singletonList(RichTreeDecorator.INSTANCE)) | ||
| Line 477: | Line 383: | ||
| ===== Creating an advanced SaplingGenerator ===== | ===== Creating an advanced SaplingGenerator ===== | ||
| - | + | So, remember how I told you that '' | |
| - | So, remember how I told you that '' | + | |
| Here's an example of that - we create several vanilla trees instead of the actual trees depending on the chance: | Here's an example of that - we create several vanilla trees instead of the actual trees depending on the chance: | ||
| - | <code java [enable_line_numbers=" | + | <code java> |
| public class RichSaplingGenerator extends SaplingGenerator { | public class RichSaplingGenerator extends SaplingGenerator { | ||
| - | private final ConfiguredFeature< | ||
| - | |||
| - | public RichSaplingGenerator(ConfiguredFeature<?, | ||
| - | this.feature = (ConfiguredFeature< | ||
| - | } | ||
| - | |||
| @Nullable | @Nullable | ||
| @Override | @Override | ||
| - | protected ConfiguredFeature< | + | protected |
| int chance = random.nextInt(100); | int chance = random.nextInt(100); | ||
| | | ||
| - | // With a 10% chance, an oak tree will be created | + | // Each tree has a 10% chance |
| - | | + | |
| - | | + | case 10 -> TreeConfiguredFeatures.OAK; |
| + | case 20 -> TreeConfiguredFeatures.BIRCH; | ||
| + | case 30 -> TreeConfiguredFeatures.MEGA_SPRUCE; | ||
| + | case 40 -> TreeConfiguredFeatures.PINE; | ||
| + | case 50 -> TreeConfiguredFeatures.MEGA_PINE; | ||
| + | case 60 -> TreeConfiguredFeatures.MEGA_JUNGLE_TREE; | ||
| + | default -> Tutorial.RICH | ||
| } | } | ||
| - | // With a 20% chance, a birch tree will be created | + | } |
| - | if (chance <= 20) { | + | |
| - | return ConfiguredFeatures.BIRCH; | + | |
| - | } | + | |
| - | // With a 30% chance, a spruce tree will be created | + | |
| - | if (chance <= 30) { | + | |
| - | return ConfiguredFeatures.SPRUCE; | + | |
| - | } | + | |
| - | // With a 40% chance, a mega spruce tree will be created | + | |
| - | if (chance <= 40) { | + | |
| - | return ConfiguredFeatures.MEGA_SPRUCE; | + | |
| - | } | + | |
| - | // With a 50% chance, a pine tree will be created | + | |
| - | if (chance <= 50) { | + | |
| - | return ConfiguredFeatures.PINE; | + | |
| - | } | + | |
| - | // With a 60% chance, a mega pine tree will be created | + | |
| - | if (chance <= 60) { | + | |
| - | return ConfiguredFeatures.MEGA_PINE; | + | |
| - | } | + | |
| - | // With a 70% chance, a jungle tree will be created | + | |
| - | if (chance <= 70) { | + | |
| - | return ConfiguredFeatures.MEGA_JUNGLE_TREE; | + | |
| - | } | + | |
| - | + | ||
| - | // If none of that happened (the chance is between 71 and 99 percents), create the actual tree | + | |
| - | return feature; | + | |
| - | } | + | |
| } | } | ||
| - | |||
| </ | </ | ||
| - | This isn't a very practical, but it shows what you can achieve using '' | + | This isn't a very practical |
| ===== Extra settings for your tree ===== | ===== Extra settings for your tree ===== | ||
| - | |||
| Using the extra '' | Using the extra '' | ||
| ==== dirtProvider ==== | ==== dirtProvider ==== | ||
| - | + | Sets the '' | |
| - | Sets the '' | + | |
| Example: | Example: | ||
| - | |||
| <code java> | <code java> | ||
| [...] | [...] | ||
| - | .dirtProvider(new SimpleBlockStateProvider(Blocks.IRON_BLOCK.getDefaultState())) | + | .dirtProvider(BlockStateProvider.of(Blocks.IRON_BLOCK)) |
| [...] | [...] | ||
| </ | </ | ||
| ==== decorators ==== | ==== decorators ==== | ||
| - | + | Used to add '' | |
| - | Used to add '' | + | This was briefly |
| - | Briefly | + | If you want, you can add multiple '' |
| - | If you want, you can add **multiple '' | + | |
| Example: | Example: | ||
| Line 568: | Line 440: | ||
| ==== ignoreVines ==== | ==== ignoreVines ==== | ||
| - | |||
| Makes the tree generation ignore vines stuck in the way. | Makes the tree generation ignore vines stuck in the way. | ||
| Line 580: | Line 451: | ||
| ==== forceDirt ==== | ==== forceDirt ==== | ||
| - | + | Forces the '' | |
| - | Forces the '' | + | |
| Example: | Example: | ||
| Line 592: | Line 462: | ||
| ===== Creating a BlockStateProvider ===== | ===== Creating a BlockStateProvider ===== | ||
| - | |||
| - | Coming soon. | ||
| - | |||
| - | ===== Creating an IntProvider ===== | ||
| - | |||
| Coming soon. | Coming soon. | ||
tutorial/trees.1624208916.txt.gz · Last modified: 2021/06/20 17:08 by redgrapefruit