SPONGE-2: World Generation API


I am nobody important, this document is not official in any way. It contains my ideas for the World Generator API for the Sponge project. Please yell at me if anything is incorrect. Also I would love to hear your suggestions and input too!

If I’m stepping on anyone’s toes, let me know and I’ll take this down ASAP!

NB: It’s important we keep this simple, otherwise it’ll be impossible to maintain.

YouTrack Issue



I’ve stated poking at this in a fork, nowhere near perfect yet, but it’s taking shape I think.



  • Complete customization of block placement during world generation
  • Complete customization of biome distribution during world generation
  • Ability to get/set blocks in the world after generation
  • Ability to get/set biomes in the world after generation
  • Ability to add/remove structures from the world after generation (w.r.t. the regions which are defined, not the actual blocks)
  • Ability to list available generators
  • Ability to get/set generators for each world
  • Avoid unnecessary coupling while maintaining acceptable performance
  • Focus on server-side only modifications
    • But keep in mind custom blocks and custom biomes, which client-side mods can add


  • Mix-and-match generation parts between plugins/mods and vanilla
  • Additional API for retroactive world generation (maybe?)
  • Add or remove village buildings (maybe?)

Out of Scope

  • Anything which cannot be controlled from server side (e.g. atmosphere color)
  • Multi-threaded chunk generation, or other major refactorings
  • Modifying the vanilla generator at a finer level. For example, more configuration of cave size and frequency. This should be done by plugins which can emulate the behavior of the vanilla FeatureGenerators but with more configuration.
  • Multi-layered biome generators. This could be handled by plugins.
  • Support for schematics. This could be handled by plugins.

Interfaces - Implemented by Sponge

(only showing methods relevant to world generation)


public interface World {
    Dimension getDimension();
    String getName();
    long getSeed();

    // NOTE: no setter for WorldGenerator, every world has exactly one
    // It only exists to remove clutter from the World interface
    WorldGenerator getGenerator();

    Block getBlock(int x, int y, int z)
    Block getBlock(Vector3i location);
    void setBlock(int x, int y, int z, Block block);
    void setBlock(Vector3i location, Block block);

    Biome getBiome(int x, int z);
    Biome getBiome(Vector2i location);
    void setBiome(int x, int z, Biome biome);
    void setBiome(Vector2i location, Biome biome);

    Set<Structure> getStructures();
    void addStructure(Structure structure);
    void removeStructure(Structure structure);


public interface WorldGenerator {
    BiomeGenerator getBiomeGenerator();
    void setBiomeGenerator(BiomeGenerator biomeGenerator);

    TerrainGenerator getTerrainGenerator();
    void setTerrainGenerator(TerrainGenerator terrainGenerator);

    List<FeatureGenerator> getFeatureGenerators();
    void addFeatureGenerator(FeatureGenerator featureGenerator);
    void removeFeatureGenerator(FeatureGenerator featureGenerator);

    List<StructureGenerator> getStructureGenerators();
    void addStructureGenerator(StructureGenerator structureGenerator);
    void removeStructureGenerator(StructureGenerator structureGenerator);


See discussion on Custom Biomes below.

public interface Biome {
    int getId();
    String getName();
    float getTemperature();
    float getRainfall();
    boolean canRain();
    boolean canSnow();
    float getMinHeight();
    float getMaxHeight();
    Set<CreatureType> getSpawnableCreatures();


Types: Mineshaft, Village, Fortress, Stronghold, Temple

public interface Structure {
    Vector3i getMinBounds();
    Vector3i getMaxBounds();
    Set<CreatureType> getSpawnableCreatures();


Needs to be one of these provided by Sponge for each dimension.

public interface Dimension {
    BiomeGenerator getVanillaBiomeGenerator();
    TerrainGenerator getVanillaTerrainGenerator();
    FeatureGenerator getVanillaFeatureGenerator();
    StructureGenerator getVanillaStructureGenerator();

All the returned objects should allow for these settings: Custom – Minecraft Wiki. So there is no need for indiviual vanilla feature and structure generators, they can be aggregated.

Interfaces - Implemented by Plugins

The following interfaces should be provided by the API. It should be possible for plugins to implement any or all of these interfaces to influence the world generation.

It should be possible to use a BiomeGenerator from one plugin, a TerrainGenerator from another and a collection of Feature and Structure generators from various plugins all for the same world.

It should also be possible to mix with vanilla generators, though the granularity of this is still to be determined.

This configuration of generators (for each world) should be able to be configured programmatically from plugins or by server owners using a config file.

Used for distributing the biomes in the world. Implemented in vanilla by (MCP::net.minecraft.world.biome.WorldChunkManager) and subclass, or possibly down to the level of (MCP::net.minecraft.world.gen.layer.GenLayer).

public interface BiomeGenerator {
    // Either this
    Biome generateBiome(World world, int x, int z);

    // Or if we need more efficiency it could be this
    Biome[] generateBiomes(World world, ChunkPosition chunkPosition);

During terrain generation, the vanilla TerrainGenerator requests a Biome[10*10] for an 4x unzoomed view of a 40x40 block area of the world. It may be worth the extra efficiency to allow plugins to implement this also. Though I feel that’s over coupled to the implementation.


Used for placing the bulk of the blocks in each chunk. Implemented in vanilla by (MCP::net.minecraft.world.gen.ChunkProviderGenerate) and subclasses .

public interface TerrainGenerator {
    Block[] generateTerrain(World world, ChunkPosition chunkPosition);

Can access the biome data from world if required.


Used to create trees, lakes, ore veins, etc. in each chunk. Called after a chunk has been generated by the TerrainGenerator if the 3 adjacent chunks on the negative (?) x and z sides are already generated. Implemented in vanilla by subclasses of (MCP::net.minecraft.world.gen.feature.WorldGenerator).

public interface FeatureGenerator {
    void generateFeature(World world, Random random, int x, int y, int z);

Calls mutating methods on the world to place blocks. Can access the biome and block data from world if required.


Used to create Strongholds, Villages, Nether Fortresses, Desert Temples, Jungle Temples, Ocean Monuments, Witch Huts, etc. These structures are stored in the world file. They can control mob spawns for example. Implemented in vanilla by subclasses of (MCP::net.minecraft.world.gen.structure.MapGenStructure).

public interface StructureGenerator {
    void generateStructure(World world, Random random, ChunkPosition chunkPosition)

Calls mutating methods on the world to place blocks and add structures. Can access the biome and block data from world if required.

Things to Think About

There are many trade-offs between performance and not exposing implementation details. For terrain generation performance is quite an important concern. Though less so when/if this happens:

FeatureGenerator and StructureGenerator could be combined as they have close to the same interface. The critical difference is that Structures are stored in the world files, but this does not effect the interface. In vanilla, structure generators are not passed a y-coordinate, but this is easy to work around.

The custom terrain in the nether and end is determined by the “dimension” of the world. I think this should be handled by having both a BiomeGenerator for that world which returns a constant biome for all locations, and a TerrainGenerator which describes the shape of the terrain for each vanilla dimension.

I have been asked about ore generation (and feature generation in general) after world generation. For example if a server owner adds a plugin which doubles diamond ore frequency, and they want it to apply to existing chunks in an existing world. This is a tricky topic, and I would love to hear ideas. Basically it would involve the feature generator running on chunks which that feature generator has not been run on as they’re loaded. Usually feature generators are not added after world creation and so it’s sufficient to store a single boolean per chunk to remember if it has been populated. However if this was extended to be a boolean per feature generator per chunk, it would be possible to run feature generators which were not present during world generation, during later chunk loading. This behavior may not always be desired though, and it’s difficult to foresee issues that could occur.

Custom Bimes

Biomes in vanilla are currently identified by integer IDs, this may change as most magic-numbers in the game are being replaced with Strings.

BiomeGenerators should be capable of handling custom biomes. These require client-side modifications to work (e.g. Biomes O’ Plenty or Enhanced Biomes). Biome should not be an Enum as it should be extendable. Something akin to Forge’s BiomeDictionary should be used. This way, biome generators can look up the available biomes, if there are no client-side mods, only vanilla biomes will be available.

Possible implementation idea for custom biomes:

public interface Biome {
    VanillaBiome getVanillaFallback();

public enum VanillaBiome implements Biome {
    public VanillaBiome getVanillaFallback() {
        return this;

So when they’re sent to the client, it would use the “vanilla fallback”, but when used internally the custom biome can be used. Biomes are stored on disk as a byte id, so if we want to allow for more than 256 biomes, we would need extra storage.


Chunk data packet format: Chunk Format - wiki.vg
Shows that only 256 bytes are sent per chunk for biomes. Atmospheric color is not sent.

Generated structures data file format: Generated structures data file format – Minecraft Wiki

Biomes O’ Plenty: Biomes O' Plenty - Over 50 new biomes, with new trees, plants, mobs, and more! - Minecraft Mods - Mapping and Modding: Java Edition - Minecraft Forum - Minecraft Forum

Enhanced Biomes: Enhanced Biomes 2.5 - Nearly 100 new biomes (20/10/14) - Minecraft Mods - Mapping and Modding: Java Edition - Minecraft Forum - Minecraft Forum

Forge’s BiomeDictionary: How to use the Forge BiomeDictionary - Mapping and Modding Tutorials - Mapping and Modding: Java Edition - Minecraft Forum - Minecraft Forum

Change History

12 September, 2014

  • Removed ability to change biome atmosphere color. Unfortunately this is controlled client-side.
  • Added some links
  • Re-orgainised some points
  • Moved some things to “Out of Scope”
  • Added vanilla class names for the curious

It would be good if we have terrain generation like the plugin Terrain Control :slight_smile:
So we can define what spawns, where, and so on. Mostly the feature that you can change how high the possibility is, that blocks generate at a specific high(level) ^^

1 Like

Maybe the world generation can be a extra/seperated part of the api …
Like I suggest it for the Entity-System …

The reason I firmly believe that at least the fundamentals of the world generation should be part of the API is because the back-end of it is heavily dependent on NMS. The whole purpose of the API is to abstract NMS so that plugins have a stable API to work on. It would be a real shame if all world generation plugins broke with every update until some other plugin is updated.

Exact that is the reason to outsource it …
Entities, WorldGeneration depend heavily on NMS, Forge.
We can only publicate a SpongeAPI if all components of the API are updated.
Maybe plugin is the wrong word/name for the oursource, maybe module is better.
So all WorldGeneration stuff need the “SpongeWorldModule” loaded (that hide the NMS calls).
Similar to apache components system.
So server admins that does not need the world stuff can update earlier.
Maybe it is also a performance boost, if you have not all SpongeModules loaded …

One of the Terrain Control developers here. I’m very happy with these ideas. An API like this would allow for Terrain Control to be implemented without the many hacks we currently have in the Bukkit version. I have two comments for this document:

The virtual biomes in Terrain Control are mostly a hack to get around the low id limit. They aren’t even saved to the map files, as that too uses the same id limit. I doubt such a hack should be part of the API. In my opinion, the best thing to do would be to just extend the id limit serverside, and add a method (getClientId()?) to the biome interface to change the id presented to the client.

Edit: to clarify this: the server could store for example ids in the range 0-1023 and send ids to the client based on the getClientId(). For real biomes getClientId() == getId().

The Minecraft client has always ignored custom biomes - it defaults to plains whenever it encounters an invalid biome id. As long as it’s OK to use the colors/weather of the plains biome, no modifications are needed to the vanilla client to support custom biomes.

Terrain generation definitely needs to happen on a chunk scale. If it happens on a block scale every non-trivial (as in: uses some noise function) chunk generator would need to implement its own cache. I would recommend agains using chunkX, chunkZ parameters directly - the meaning of those is not always clear. 16, 16 could for example both mean the chunk containing blocks with x and z in the range 256 to 271 or the chunk containing blocks with x and z in the range 16 to 31. In Terrain Control we use a class called ChunkCoordinate for this, maybe Sponge could create a class with a similar API?


Thank you very much for your reply …
I think most devs (include me) are not familiar with the world generation …
If we integrate it into SpongeAPI, who maintain it?
That is my big reason for the SpongeWorldAPI …
One central API for WorldGeneration …

Does any of what you describe here match how vanilla works? Any solution that requires modifying how vanilla’s world generation classes work is almost certainly not worth doing. That is some of the hairiest code in the server and its hard to actually tell when you’ve broken it unless you’ve done something really bad to it. Imagine people generating a world and playing on it for a week before discovering you broke placing melons in jungles or something.


My reply was mostly to @hoqhuuep, as he asked me to comment on this topic.

I’ll try to give an overview in this post of Minecraft’s terrain generation, and where it’s possible to add hooks without dramatically increasing maintance costs. The post turned out to be very long, so feel free to skip to the conclusion.

Chunk generation

Minecraft’s generation is split into two steps, chunk generation and block population. Chunk generation returns an array of blocks for that single chunk. (In Minecraft 1.6 this array was a byte[] array, in Minecraft 1.7 it became a Block[] array (Block as in Minecraft’s material type, not as in Bukkit’s x+y+z+Material object) plus a byte[] array for block data. In Minecraft 1.8 both arrays are abstracted away by a new class.)

Replacing the chunk generator is easy, you just need to replace the ChunkProvider implementation with one that calls the plugin-provided chunk generator. CraftBukkit has already done this.

Terrain population

For terrain population something else happens. All populators in Minecraft implement an interface that has a method to populate a single chunk. In addition, Minecraft guarantees that the chunks (chunkX+1,chunkZ), (chunkX,chunkZ+1), (chunkX+1,chunkZ+1) are loaded. This has been the case since infinite worlds were implemented in 2010. Those three neighbour chunks do not have to be populated yet.

So in total, 2x2 chunks are loaded. Why does Minecraft do this? The answer is simple: things like trees can now cross chunk boundaries. When you place a tree with the trunk a block x at chunkX*16+8+random(16) and block z at chunkZ*16+8+random(16) (where random(16) returns a random number between 0 and 15, inclusive) you’re guaranteed to have 8 blocks of room on all sides from the trunk of the trees, enough for all trees in Minecraft.

Adding a populator to Minecraft is easy: the implementation would just call all added populators. CraftBukkit can already do this.

From what I understand a List<Populator> is being proposed (@hoqhuuep called it FeatureGenerator instead of Populator) as the backing of populators. This list contains both the vanilla and plugin-made populators. Unfortunatily that’s not how vanilla populators work. Minecraft doesn’t store a list of populators, it just calls all of them sequentially. Rewriting this to a list is difficult, as this list has to be updated on each Minecraft update. This is probably why CraftBukkit doesn’t do this. Minecraft 1.8 wraps each populator call into an if statement (for the Customized world type), but that doesn’t really help us to expose it as a list. (Keep in mind that lists can be reordered, lines of code inside a method cannot.)

I’m not joking about this: Terrain Control maintains a List<Resource> for each biome, that includes all vanilla resources. It was a pain to update everything to match vanilla 1.7.

However, it should be possible to disable all vanilla populators at once (CraftBukkit automatically does this when using a custom chunk generator). This can be implemented by simply not calling the populate method on ChunkProviderGenerate.


Structures (mineshafts, villages, temples, nether fortresses, strongholds and now also ocean monuments) are a kind of special populator. First of all, they are much larger than 2x2 chunks. However, their individual parts (rooms/houses) fit nicely into the 2x2 chunk limit. Second, they influence mob spawns. Third, the locations of the structures and their components are saved. Minecraft does a lot of planning to determine which part of a structure should spawn where. The actual spawning of individual parts is just another terrain populator.

Adding custom structures is entirely possible - Forge already provides methods for this. Extending the native structure class is of course not possible in Sponge (Minecraft classes cannot be part of the API), but a wrapper similar to chunk generators and other block populators could be added. Removing single vanilla structures and exposing them as a list is not really possible for the same reasons mentioned as for terrain populators.

Biome id limit, client biomes and virtual biomes

Raising the biome id limit is possible - another array (char[] or byte[] + Nibble) would need to be stored in each chunk with the ids of those biomes. From the getClientId() methods in all biomes a byte[] array is constructed. This array is sent to clients and saved to the map files too - in case the world is loaded by vanilla, it at least has some biome information.

One could argue that byte getClientId() shouldn’t be exposed at all, as it’s just a magic value that should not be decided upon by mods. However, it could simply be changed to Biome getClientBiome(), this shouldn’t really affect anything.

One could also argue that the whole concept of different ids on the client and the server is an ugly hack made to make biomes look a little better on vanilla clients and to extend the maximum amount of biomes. In that case, this whole method should just be left out. It is still possible to register more than 100 different biomes. As long as ids are automatically assigned, this shouldn’t be too much of a problem.

Biome layout generator

The biome layout generator is the generator that decides where each biome is placed. Replacing the biome layout generator is easy, the WorldChunkManager class would have to be subclassed to return biomes of a plugin. Terrain Control already does this.

Adding a biome to the vanilla biome layout generator is also possible, Forge allows you to do this. Sponge can just wrap this method. Removing a biome from the vanilla biome layout generator is also possible as of 1.7. Special-cased biomes like Ocean, Hell (Nether) and Sky (End) are not possible to remove.

The lists of the biomes that the vanilla biome layout generator generates should to generate should somehow be exposed to custom biome layout generators, so that they can react on changed lists to for example spawn custom biomes.


Should definitely be implemented on a plugin level.

Schematics are not made for terrain generation. They lack features like spawn conditions (trees for example need to have a wood block touching the ground), size restrictions and don’t differentiate between air and nothing (air replaces the previous block, nothing leaves the original block intact).

Finally, the format needs to be updated eventually as it uses block ids/data with no table that specifies which id represents which block.


If the API is properly designed (with attention for maintainability) it could be added to Sponge. Nice-to-have features like disabling individual populators require much more maintance, especially if they are implemented as a List<Populator>. I would recommend against adding them.

It is always an option to start out small (with something similar to Bukkit’s generation API, but then with a VoxelVolume instead of byte[]) and expand on that later. For the time being, plugins can just call Forge methods. Almost all Minecraft methods are deobfuscated by MCP, so calling them isn’t as dangerous as it was to call methods on the nms classes in CraftBukkit. It either works, or crashes with a NoMethodDefFoundError. Later on, an API could be added to add structures, to add and remove individual biomes and maybe to extend the id limit.


Then use a wrapper object for the populator problem (List<Populator>) into something like a interface:

public interface PopulatorList {
  public void perform();
  public void add(Populator item);

And simply provide a class for vanilla population:

public class PopulatorVanilla implements PopulatorList {
  public void perform() {
    ... standardclass ... .populate();
  public void add(Populator item) {
    throw new UnsupportedOpperationException();  
    // Or add it to a list and execute them after the vanilla populator

PS: sorry for the edit, i failed with the new forum

That would work, but then it’s no longer a List, it’s just called a List. :smile: My point was that it is very difficult to turn the vanilla populators/structures into a ‘real’ Java List, that supports getting, inserting, removing and reordering populators.

But you gave me an idea: what if all vanilla populators are expressed as a single FeatureGenerator? A List<FeatureGenerator> could have contents like this:

[MyPluginFeatureGenerator, VanillaFeatureGenerator, OtherPluginFeatureGenerator]

With VanillaFeatureGenerator implemented like this:

public void place(World world, ChunkPos pos) {
    originalChunkProviderGenerateInstance.populate(world, pos.getChunkX(), pos.getChunkZ())

Populating would then be as simple as calling place on each FeatureGenerator. Removing the vanilla populator would be very simple too for plugins. I think it won’t be too difficult to maintain this.


Yes, and this would make the world generator more dynamic.

One could so easily make the Vanilla generator region based:

public void place(World world, ChunkPos pos) {
   if(pos.getChunkX() > 0 && pos.getChunkY() > 0) {
      WorldGenerator.getVanillaFeatureGenerator().place(World world, ChunkPos pos);

@hoqhuuep References to important Objects in World Generator :wink:

Not sure if this matters but in sponge instead of getBlock() i think we getVoxel() because it can getBlock() and getVector3i()

@rutgerkok Thanks for coming over! Your input is absolutely invaluable! :slight_smile:

I’ve been working my way through reverse engineering the vanilla generator so I can keep up!

Vanilla Features and Structures

I see exactly what you mean about the cost of implementing the vanilla feature and structure generators as lists. The implementation is tantalizingly close, but not close enough. As you say the maintainability of that is too high for Sponge.

I think your suggestion is the best middle-ground. Have Sponge provide a single VanillaFeatureGenerator which generates all vanilla features, and a single VanillaStructureGenerator which generates all vanilla structures. These could still be mixed in with the other generators but would be atomic.

This also leaves a nice gap for Terrain Control to fill, it could for example provide VanillaCaveGenerator and VanillaDiamondOreGenerator, etc. so that server owners who want more control can get it.

Custom Biomes

After reading your post and doing my own research, I think it would be unwise to support custom biomes without client-side mods. I think we should still have something like BiomeDictionary, but adding biomes to it should only be possible by mods which are client-side as well. Your idea of sending a “vanilla fallback” is clever, but I still worry about having to modify the world format to save the data, or limiting to 256 total biomes. Also, I feel we should only generate worlds which could be read by a vanilla server. What does a vanilla server do if it encounters an invalid biome id?

1 Like

I find this idea really well, although it would be directly integrated into Spong.

My English is very poor lament, I’m French ^^

1 Like

Hello, Maki.
Instead of “lament”, try the word “sorry”. Lament is an older word, which made me smile. It was cute. :smile:

1 Like

Maybe some kind of testing with given seeds could work?

Hey all, OreVeins developer here.

I have a few critiques of the Bukkit API which may be instrumental in reforming things to the Sponge API to get around some of the pitfalls I encountered. First, let me explain my plugin a bit:

  • it creates very large distributions of fractal-like veins across multiple chunks (up to 300-500 blocks long sometimes) that cannot be created by some sort of noise generator
  • it is meant to operate on a chunk that is devoid of ores
  • and I would like to create some custom aboveground mining structures oriented along the dip and strike of said vein, keeping the look and feel of these structures as vanilla as possible.

So now for the pitfalls I encountered with the Bukkit API:

  1. There was no good way to ensure that vanilla ores were completely removed. I tried using a block populator, hooking onto the onGenerate() , onChunkPopulate() (or something like that), and onChunkLoad() event handlers; in each case ore depopulation of a chunk was incomplete and sometimes never happened.
  2. No straightforward ways to make, save, or load structures to/from file. I would love to implement things like aboveground mineshafts, but their feel and look should be editable and controlled by me. I suppose I could have done a standard IO but the objects on file looked like a complete mess of ASCII characters.
  3. Simple custom block mechanics not available in Bukkit. I know this has been beaten to death in some other topic threads, but I would have liked to put simple blocks like kimberlite, or quartz veins. Even if there was a client mod requirement I feel like this would complete the OreVeins Experience

So here’s some suggestions I have:

  • be able to call in default block populators from vanilla, or remove them from the default world generator. Ex: getPopulators(world), addPopulator(world, boolean (for at world init or after the fact) ), and removePopulator(world, enum/string) with populators having an enum or string describing what they are
  • A basic building architecture class (or similar) that can be instantiated with different vanilla buildings, or a constructor that would allow you to define your own. Included with it should be methods to place it at a location and provide some support to the base (like cobble under villages or wood under mineshafts). Also I could never get the wood stairs to align right on the roofs so something to deal with block metadata?
  • an abstract block class that will allow you to define your own blocks, potential metadata they may have, and the .png files that show how they look. Maybe also an easy way to make resources for the client so they can view and use these blocks.

thats all I can think of for now. Glad to be part of this community now!

1 Like

I’d like very much to have something a little less heavy for minor changes to the world generator rather than wholesale replacement. For example, I’d like to say “Hey, replace all ocean chunks with forest chunks” without having to author my own world generator.

Maybe your proposed design accounts for this - apologies if it does. I’m having trouble following the description, probably due to my unfamiliarity with Bukkit/Mojang’s generation strategy.

What ever we come up with for Sponge, you should definitely be able to use the new world customization settings that were added to vanilla in 1.8, regardless of whether it’s through the API:

Yeah, I’m still working on understanding how the vanilla structures are generated. Also rutgerkok mentioned that Forge provides methods for custom structures, so I’ll have to look at that too :wink:

Whilst this should not be exposed at the Sponge API layer, if you drop down to the Forge layer it should be trivial.

@rutgerkok raised concerns about being able to add/remove individual vanilla populators in the same way as adding/removing plugin provided ones. However it should be entirely possible to add/remove all vanilla populators together using the plugin method, and due to the customization settings I mentioned above, you should be able to turn on/off individual vanilla populators.

Hopefully Sponge will have a good way of dealing with this. The metadata should be encapsulated in the Block type somehow.

As mentioned, you’d have to drop down to the Forge layer for this.

I’d like to see this too. I think the easiest way to do it would be:

public class ReplaceOceanWithForest extends VanillaBiomeGenerator {
    Biome[] generateBiomes(World world, ChunkPosition chunkPosition) {
        // Get the biomes that the vanilla generator would have made
        Biome[] biomes = super.generateBiomes(world, chunkPosition);

        // Replace all ocean with forest
        for (int i = 0; i < biomes.length; ++i) {
            if (biomes[i] == VanillaBiome.OCEAN) {
                biomes[i] = VanillaBiome.FOREST;

        // Return the modified biomes
        return biomes;

EDIT: although, I don’t think we could expose VanillaBiomeGenerator as a class that you could extend. Instead it would probably have to be more like this:

public class ReplaceOceanWithForest implements BiomeGenerator {
    private BiomeGenerator vanillaBiomeGenerator;

    public ReplaceOceanWithForest(Game game) {
        vanillaBiomeGenerator = game.getVanillaBiomeGenerator();

    Biome[] generateBiomes(World world, ChunkPosition chunkPosition) {
        // Get the biomes that the vanilla generator would have made
        Biome[] biomes = vanillaBiomeGenerator.generateBiomes(world, chunkPosition);

        // Replace all ocean with forest
        for (int i = 0; i < biomes.length; ++i) {
            if (biomes[i] == VanillaBiome.OCEAN) {
                biomes[i] = VanillaBiome.FOREST;

        // Return the modified biomes
        return biomes;