What this document contains:
- Overview of the World Generation Process
- The process of creating a new World Generator
- A list of not yet implemented features
As you are probably aware each World
is split into a number of Chunk
s (technically chunk columns) each one of which defines an area of the world which is 16x256x16 blocks (by default see below) and these chunks may be loaded in any order. It is therefore one of the primary requirements of any WorldGenerator
that it must generate the world on a chunk-by-chunk basis without any requirement for a specific order.
The assumption as made above that each
Chunk
has a size of 16x256x16 however this is not always the case as other implementations could theoretically modify the structure and dimensions of chunks. At runtime the size of a chunk may be determined withChunk.getBlockSize()
.
The generation process is thus split into two distinct phases. First there is the generation phase during which the more intensive aspects of generation are performed into a BlockBuffer
. A buffer is used so that many of the World functions that happen when a block is changed such as updating neighbouring blocks and calculating lighting are not performed. Some examples of processes that occur during this phase is the generation of caves, ravines, and many structures such as villages and strongholds.
Secondly, and this can happen immediately after or some time in the future, the chunk is populated. It is a requirement that before a chunk is populated every chunk immediately surrounding it will at least have passed the generation phase (so the base terrain will at least exist, if not the additional features), this is to allow objects placed by populators to overlap the chunk boundaries and extend into other surrounding chunks. In this phase the smaller and/or biome-based features such as flowers, trees, and ores. The ground cover (such as dirt and grass) is also placed in at this time.
The structure of the WorldGenerator
is set up in oder to reflect this two phase approach. All operations performed by world generators are performed via populators, of which there are two types. GeneratorPopulator
s are for the generation phase and operate upon a BlockBuffer
. Populator
s are the corresponding populaotr but act instead upon a Chunk
object. Within the WorldGenerator
there is also a special generator populator which is what creates the base terrain shape at the start of the generation phase.
Biomes also have their own set of generator populators and populators which are applied during the appropriate phases depending on what biomes are within the chunk. Additionally biomes also define an ordered list of GroundCoverLayer
s these are the biome-dependant layers of blocks that are applied to the top of the generated world, things like the layer of grass and the 4ish layers of dirt under them.
As a current implementation limitation the generator will apply only the biome specific generator populators and populators for the biome defined at
Chunk.getBlockMin().add(16, 0, 16)
. However this may be solved in the future.
The final order of operations for generation is this: The generation phase:
- Create the chunk buffer
- Refer to the
BiomeGenerator
in order to get what biomes exist for each block in the chunk's 2d plane - Call the base GeneratorPopulator to create the base terrain shape
- for each column in the chunk run through the list of
GroundCoverLayer
s in the column's biome and place them starting at the top most block - Call each of the GeneratorPopulators registered to the biome at
(x+16, 0, z+16)
- Call each of the GeneratorPopulators registered to the generator
- Build the chunk object from the chunk buffer
The population phase:
- Validate surrounding chunks
- Pass the chunk to each of the populators registered to the biome at
(x+16, 0, z+16)
- Pass the chunk to each of the populators registered to the generator
So now that we have a handle on what process the server takes at generating these chunks lets look at how we can modify it. All modifications to world generators are done by implementing and registering a WorldGeneratorModifier
. When a world is loaded it fetches from its configuration which modifiers should be applied to its generator and runs through them.
At this time the config for these modifiers is not yet implemented, as a temporary solution while testing you can listen for the
WorldLoadEvent
of your target world and call your modifier yourself.
From a WorldGeneratorModifier you are able to modify or replace all aspects of the world generation process. by iterating through the lists of generator populators and populators you should see the vanilla populators which may be modified through their interfaces within org.spongepowered.api.world.gen.populators
or removed.
In addition of you are creating an entirely new generator and wish to add new instances of the vanilla populators you can build the vanilla populators by getting a copy of the PopulatorFactory
from the game registry and getting the appropriate builder.
On the topic of populators and generator populators you can refer to the follwoing which is a copy of my current expansion plan which I am in the process of implementing currently.
World Generation API expansion plan: phase 1: Populators
- Create better utilities for VariableAmounts and WeightedRandomObjects
- Update populator interfaces to make better use of PopulatorObjects, VariableAmounts, and WeightedRandomObjects.
- Allow overwriting biome populators for particular world generators
- Implement populator interfaces onto WorldGen* NMS classes
- Overwrite vanilla populators in ChunkProvider* and BiomeDecorator to use our populator lists
phase 2: GenPop
- Create GenPop interfaces
- expose Noise generators? (flow noise?)
- Implement GenPop interfaces onto MapGen* NMS classes
- Overwrite vanilla to use our genpop lists
phase 3: Structures
- Create interfaces for structures
- Create some sort of StructureBuilder (?) for creating new structures
phase 4: Custom everything
- Allow creation and registration of custom biomes
- Allow control of animals/monsters that spawn in certain biomes
Another currently unimplemented piece is the creation of config in order to define, from a server admin's perspective, which WorldGeneratorModifiers will be applied to a world.
There is the requirement for proper control over a world's generation that you are able to modify the populators and other options of a biome within a world generator independantly of the rest of the world. It is therefore planned (in phase 1 above) to allow the overridng of this information from the context of a world generator.
You may have noticed that not once have I mentioned GeneratorType. GeneratorType is from an earlier stage of development and I need to take a bit of time to review its form and either modify it or remove it. It is possibly that I may define a generator type to be a collection of WorldGeneratorModifiers and in the earlier mentioned configuration for server admins they would be defining a generator type per world rather than a set of modifiers per world (or maybe both).
Finally, it should go without saying that world generation is a large and somewhat complicated system and any input on this system is always welcome.
TL;DR write a world gen api they said...it'll be simple and easy they said...