Minecraft Blogs / Tutorial

How Minecraft Java Generate Biome Feature

  • 1,027 views, 1 today
  • 4
  • 2
planc_'s Avatar planc_
Level 25 : Expert Ninja
9
Modding Minecraft with Fabric has taught me a lot about Minecraft source code style: they are confusing and can be difficult to trace at times. I am developing a mod that modifies Minecraft's default biome feature. A biome feature is a physical detail of the biome that includes whether or not the biome has a Stronghold, the biome can spawn tropical fish, and so on.

This blog serves as a note for my future self and also for all Minecraft modder that wish to learn more about Minecraft biome feature generation. Also, this blog is about Minecraft Java. I have zero idea about Minecraft Bedrock coding, and I don't even know if there will be source code of it out there too. Let's hop in.



Inspecting Minecraft Source Code

First, when you look at the class for Minecraft biome in net.minecraft.world.biome.Biomes, you will see the instantiation of all existing Minecraft biome. Each instantiation is of a class that extends Biome class as their base class. The only thing that they modify/override is the constructor, which they add in the features of the individual biome.

Quick tips: If you have no idea how to start your mod, you can always look at how Minecraft instantiate their existing object. Usually, they can be found at their class collection aka the plural of the class. e.g. Items contain all instantiation of default Item objects, and similarly, Biomes is for Biome objects.

We can observe that common features are added using methods in DefaultBiomeFeatures class, so let's look at DefaultBiomeFeatures.addDefaultOres method. This is the way the coal ore feature is added.


biome.addFeature(
GenerationStep.Feature.UNDERGROUND_ORES,
Feature.ORE.configure(
new OreFeatureConfig(OreFeatureConfig.Target.NATURAL_STONE, COAL_ORE, 17)
).createDecoratedFeature(
Decorator.COUNT_RANGE.configure(new RangeDecoratorConfig(20, 0, 0, 128))));


Oh no. What the heck is this. Well, cry no more, because I will break down to you the components that are responsible for the calculation of the block position and the placement of the feature.



Understanding Feature Generation

Feaures in a biome are stored in a map/dictionary. The key of the map is the type of the feature for the biome (e.g. Structure feature, Ore feature, etc), and the value of the map is an array of feature object that contains the method to calculate the feature position and to place the feature. Biome.addFeature method takes two arguments: the feature key, and the feature object.

The feature object is a ConfiguredFeature object containing the details of the feature (in this case, ore) generation. Here, it is done using a builder to define two things: the feature configuration, and the decorator. A feature builder contains the detail on what and how to put the features on a block position, and a decorator is to calculate the block position for the feature configuration to place the feature.

Alright, now that you have known the general idea, I will show you the flow of the method called starting from the chunk generator.

Part A: Feature generation invocation
1. ChunkGenerator.generateFeatures is called.
2. In the method, the biome object and a list of features is obtained.
3. The list is iterated to pass each feature into Biome.generateFeatureStep.

Part B: Decorator - to calculate block positions
4. The block position calculation is called in a chain of method calls. That is:
  a. ConfiguredFeature.generate
  b. DecoratedFeature <extends Feature>.generate
  c. ConfiguredDecorator.generate
  d. Decorator.generate
5. In Decorator.generate, Decorator.getPositions will be called, which will return a Stream<BlockPos> object for performing feature placement on each block position.

Part C: Feature Builder - to place features
6. In the Stream<BlockPos>, each block position will be passed to another ConfigureFeature.generate object of which this object contains the feature information.
7. The block placement is done similarly to the decorator. That is:
  a. ConfigureFeature.generate
  b. ? <extends Feature> generate (? = anything that is not a decorator feature object)
8. The last method uses the block position to perform any additional calculation and then uses the world object to place the feature.

Is that clear? No? Well, I suggest you go through this blog post and the source code at the same time.

Another additional information that I want to highlight is that a ConfiguredFeature object stores two things: a Feature object, and a FeatureConfig object.

In the decorator object, the Feature object is the decorator itself, and the FeatureConfig contains the ConfiguredFeature for the feature builder.

In the feature builder object, the Feature object is the builder itself, and the FeatureConfig contains the feature information.



Bottomline - Rant

I hope everything is sufficient to guide you as well as my future self on this topic. As for why Mojang (or Notch) designed the algorithm like this, I have no idea because I am not a game design major. But IMO, the design can be improved and decoupled. The purpose of the `decorator` is to calculate the feature spawning probability, and the `feature builder` is to place the feature in the world, which has totally a different semantic than a decorator. If I wrote a wrong information, please do notify me! Thank you for reading. If you enjoy post like this, please tell me too. I will post more in the future on Minecraft Fabric modding.
Tags

Create an account or sign in to comment.

Planet Minecraft

Website

© 2010 - 2024
www.planetminecraft.com

Welcome