2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "net.minecraft.world.level.h"
# include "net.minecraft.world.level.storage.h"
# include "net.minecraft.world.level.biome.h"
# include "net.minecraft.world.level.newbiome.layer.h"
# include "System.h"
# include "BiomeSource.h"
# include "..\Minecraft.Client\Minecraft.h"
# include "..\Minecraft.Client\ProgressRenderer.h"
// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3
void BiomeSource : : _init ( )
2026-03-02 15:53:32 +07:00
{
2026-03-01 12:16:08 +08:00
layer = nullptr ;
zoomedLayer = nullptr ;
2026-03-02 15:53:32 +07:00
2026-03-01 12:16:08 +08:00
cache = new BiomeCache ( this ) ;
playerSpawnBiomes . push_back ( Biome : : forest ) ;
playerSpawnBiomes . push_back ( Biome : : taiga ) ;
// 4J-PB - Moving forward plains as a spawnable biome (mainly for the Superflat world)
playerSpawnBiomes . push_back ( Biome : : plains ) ;
playerSpawnBiomes . push_back ( Biome : : taigaHills ) ;
playerSpawnBiomes . push_back ( Biome : : forestHills ) ;
playerSpawnBiomes . push_back ( Biome : : jungle ) ;
playerSpawnBiomes . push_back ( Biome : : jungleHills ) ;
}
2026-03-02 15:53:32 +07:00
void BiomeSource : : _init ( int64_t seed , LevelType * generator )
2026-03-01 12:16:08 +08:00
{
_init ( ) ;
LayerArray layers = Layer : : getDefaultLayers ( seed , generator ) ;
layer = layers [ 0 ] ;
zoomedLayer = layers [ 1 ] ;
delete [ ] layers . data ;
}
BiomeSource : : BiomeSource ( )
{
_init ( ) ;
}
// 4J added
2026-03-02 15:53:32 +07:00
BiomeSource : : BiomeSource ( int64_t seed , LevelType * generator )
2026-03-01 12:16:08 +08:00
{
_init ( seed , generator ) ;
}
// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3
BiomeSource : : BiomeSource ( Level * level )
{
_init ( level - > getSeed ( ) , level - > getLevelData ( ) - > getGenerator ( ) ) ;
}
BiomeSource : : ~ BiomeSource ( )
{
delete cache ;
}
Biome * BiomeSource : : getBiome ( ChunkPos * cp )
{
return getBiome ( cp - > x < < 4 , cp - > z < < 4 ) ;
}
Biome * BiomeSource : : getBiome ( int x , int z )
{
return cache - > getBiome ( x , z ) ;
}
float BiomeSource : : getDownfall ( int x , int z ) const
{
return cache - > getDownfall ( x , z ) ;
}
// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only.
floatArray BiomeSource : : getDownfallBlock ( int x , int z , int w , int h ) const
{
floatArray downfalls ;
getDownfallBlock ( downfalls , x , z , w , h ) ;
return downfalls ;
}
// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only.
// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3
void BiomeSource : : getDownfallBlock ( floatArray & downfalls , int x , int z , int w , int h ) const
{
IntCache : : releaseAll ( ) ;
//if (downfalls == NULL || downfalls->length < w * h)
if ( downfalls . data = = NULL | | downfalls . length < w * h )
{
if ( downfalls . data ! = NULL ) delete [ ] downfalls . data ;
downfalls = floatArray ( w * h ) ;
}
intArray result = zoomedLayer - > getArea ( x , z , w , h ) ;
for ( int i = 0 ; i < w * h ; i + + )
{
float d = ( float ) Biome : : biomes [ result [ i ] ] - > getDownfallInt ( ) / 65536.0f ;
if ( d > 1 ) d = 1 ;
downfalls [ i ] = d ;
}
}
BiomeCache : : Block * BiomeSource : : getBlockAt ( int x , int y )
{
return cache - > getBlockAt ( x , y ) ;
}
float BiomeSource : : getTemperature ( int x , int y , int z ) const
{
return scaleTemp ( cache - > getTemperature ( x , z ) , y ) ;
}
// 4J - brought forward from 1.2.3
float BiomeSource : : scaleTemp ( float temp , int y ) const
{
return temp ;
}
floatArray BiomeSource : : getTemperatureBlock ( int x , int z , int w , int h ) const
{
floatArray temperatures ;
getTemperatureBlock ( temperatures , x , z , w , h ) ;
return temperatures ;
}
// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only.
// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3
void BiomeSource : : getTemperatureBlock ( floatArray & temperatures , int x , int z , int w , int h ) const
{
IntCache : : releaseAll ( ) ;
//if (temperatures == null || temperatures.length < w * h) {
if ( temperatures . data = = NULL | | temperatures . length < w * h )
{
if ( temperatures . data ! = NULL ) delete [ ] temperatures . data ;
temperatures = floatArray ( w * h ) ;
}
intArray result = zoomedLayer - > getArea ( x , z , w , h ) ;
for ( int i = 0 ; i < w * h ; i + + )
{
float t = ( float ) Biome : : biomes [ result [ i ] ] - > getTemperatureInt ( ) / 65536.0f ;
if ( t > 1 ) t = 1 ;
temperatures [ i ] = t ;
}
}
BiomeArray BiomeSource : : getRawBiomeBlock ( int x , int z , int w , int h ) const
{
BiomeArray biomes ;
getRawBiomeBlock ( biomes , x , z , w , h ) ;
return biomes ;
}
// 4J added
void BiomeSource : : getRawBiomeIndices ( intArray & biomes , int x , int z , int w , int h ) const
{
IntCache : : releaseAll ( ) ;
intArray result = layer - > getArea ( x , z , w , h ) ;
for ( int i = 0 ; i < w * h ; i + + )
{
biomes [ i ] = result [ i ] ;
}
}
void BiomeSource : : getRawBiomeBlock ( BiomeArray & biomes , int x , int z , int w , int h ) const
{
IntCache : : releaseAll ( ) ;
//if (biomes == null || biomes.length < w * h)
if ( biomes . data = = NULL | | biomes . length < w * h )
{
if ( biomes . data ! = NULL ) delete [ ] biomes . data ;
biomes = BiomeArray ( w * h ) ;
}
intArray result = layer - > getArea ( x , z , w , h ) ;
for ( int i = 0 ; i < w * h ; i + + )
{
biomes [ i ] = Biome : : biomes [ result [ i ] ] ;
# ifndef _CONTENT_PACKAGE
if ( biomes [ i ] = = NULL )
{
app . DebugPrintf ( " Tried to assign null biome %d \n " , result [ i ] ) ;
__debugbreak ( ) ;
}
# endif
}
}
BiomeArray BiomeSource : : getBiomeBlock ( int x , int z , int w , int h ) const
{
if ( w = = 16 & & h = = 16 & & ( x & 0xf ) = = 0 & & ( z & 0xf ) = = 0 )
{
return cache - > getBiomeBlockAt ( x , z ) ;
}
BiomeArray biomes ;
getBiomeBlock ( biomes , x , z , w , h , true ) ;
return biomes ;
}
// 4J - caller is responsible for deleting biomes array
void BiomeSource : : getBiomeBlock ( BiomeArray & biomes , int x , int z , int w , int h , bool useCache ) const
{
IntCache : : releaseAll ( ) ;
//if (biomes == null || biomes.length < w * h)
if ( biomes . data = = NULL | | biomes . length < w * h )
{
if ( biomes . data ! = NULL ) delete [ ] biomes . data ;
biomes = BiomeArray ( w * h ) ;
}
if ( useCache & & w = = 16 & & h = = 16 & & ( x & 0xf ) = = 0 & & ( z & 0xf ) = = 0 )
{
BiomeArray tmp = cache - > getBiomeBlockAt ( x , z ) ;
System : : arraycopy ( tmp , 0 , & biomes , 0 , w * h ) ;
delete tmp . data ; // MGH - added, the caching creates this array from the indices now.
//return biomes;
}
intArray result = zoomedLayer - > getArea ( x , z , w , h ) ;
for ( int i = 0 ; i < w * h ; i + + )
{
biomes [ i ] = Biome : : biomes [ result [ i ] ] ;
}
}
byteArray BiomeSource : : getBiomeIndexBlock ( int x , int z , int w , int h ) const
{
if ( w = = 16 & & h = = 16 & & ( x & 0xf ) = = 0 & & ( z & 0xf ) = = 0 )
{
return cache - > getBiomeIndexBlockAt ( x , z ) ;
}
byteArray biomeIndices ;
getBiomeIndexBlock ( biomeIndices , x , z , w , h , true ) ;
return biomeIndices ;
}
// 4J - caller is responsible for deleting biomes array
void BiomeSource : : getBiomeIndexBlock ( byteArray & biomeIndices , int x , int z , int w , int h , bool useCache ) const
{
IntCache : : releaseAll ( ) ;
//if (biomes == null || biomes.length < w * h)
if ( biomeIndices . data = = NULL | | biomeIndices . length < w * h )
{
if ( biomeIndices . data ! = NULL ) delete [ ] biomeIndices . data ;
biomeIndices = byteArray ( w * h ) ;
}
if ( useCache & & w = = 16 & & h = = 16 & & ( x & 0xf ) = = 0 & & ( z & 0xf ) = = 0 )
{
byteArray tmp = cache - > getBiomeIndexBlockAt ( x , z ) ;
System : : arraycopy ( tmp , 0 , & biomeIndices , 0 , w * h ) ;
//return biomes;
}
intArray result = zoomedLayer - > getArea ( x , z , w , h ) ;
for ( int i = 0 ; i < w * h ; i + + )
{
biomeIndices [ i ] = ( byte ) result [ i ] ;
}
}
/**
* Checks if an area around a block contains only the specified biomes .
* Useful for placing elements like towns .
2026-03-02 15:53:32 +07:00
*
2026-03-01 12:16:08 +08:00
* This is a bit of a rough check , to make it as fast as possible . To ensure
* NO other biomes , add a margin of at least four blocks to the radius
*/
bool BiomeSource : : containsOnly ( int x , int z , int r , vector < Biome * > allowed )
{
int x0 = ( ( x - r ) > > 2 ) ;
int z0 = ( ( z - r ) > > 2 ) ;
int x1 = ( ( x + r ) > > 2 ) ;
int z1 = ( ( z + r ) > > 2 ) ;
int w = x1 - x0 + 1 ;
int h = z1 - z0 + 1 ;
intArray biomes = layer - > getArea ( x0 , z0 , w , h ) ;
for ( int i = 0 ; i < w * h ; i + + )
{
Biome * b = Biome : : biomes [ biomes [ i ] ] ;
if ( find ( allowed . begin ( ) , allowed . end ( ) , b ) = = allowed . end ( ) ) return false ;
}
return true ;
}
/**
* Checks if an area around a block contains only the specified biome .
* Useful for placing elements like towns .
2026-03-02 15:53:32 +07:00
*
2026-03-01 12:16:08 +08:00
* This is a bit of a rough check , to make it as fast as possible . To ensure
* NO other biomes , add a margin of at least four blocks to the radius
*/
bool BiomeSource : : containsOnly ( int x , int z , int r , Biome * allowed )
{
int x0 = ( ( x - r ) > > 2 ) ;
int z0 = ( ( z - r ) > > 2 ) ;
int x1 = ( ( x + r ) > > 2 ) ;
int z1 = ( ( z + r ) > > 2 ) ;
2026-03-02 15:53:32 +07:00
2026-03-01 12:16:08 +08:00
int w = x1 - x0 ;
int h = z1 - z0 ;
int biomesCount = w * h ;
intArray biomes = layer - > getArea ( x0 , z0 , w , h ) ;
for ( unsigned int i = 0 ; i < biomesCount ; i + + )
{
Biome * b = Biome : : biomes [ biomes [ i ] ] ;
if ( allowed ! = b ) return false ;
}
return true ;
}
/**
* Finds the specified biome within the radius . This will return a random
* position if several are found . This test is fairly rough .
2026-03-02 15:53:32 +07:00
*
2026-03-01 12:16:08 +08:00
* Returns null if the biome wasn ' t found
*/
TilePos * BiomeSource : : findBiome ( int x , int z , int r , Biome * toFind , Random * random )
{
int x0 = ( ( x - r ) > > 2 ) ;
int z0 = ( ( z - r ) > > 2 ) ;
int x1 = ( ( x + r ) > > 2 ) ;
int z1 = ( ( z + r ) > > 2 ) ;
int w = x1 - x0 + 1 ;
int h = z1 - z0 + 1 ;
intArray biomes = layer - > getArea ( x0 , z0 , w , h ) ;
TilePos * res = NULL ;
int found = 0 ;
int biomesCount = w * h ;
for ( unsigned int i = 0 ; i < biomesCount ; i + + )
{
int xx = x0 + i % w ;
int zz = z0 + i / w ;
Biome * b = Biome : : biomes [ biomes [ i ] ] ;
if ( b = = toFind )
{
if ( res = = NULL | | random - > nextInt ( found + 1 ) = = 0 )
{
res = new TilePos ( xx , 0 , zz ) ;
found + + ;
}
}
}
return res ;
}
/**
* Finds one of the specified biomes within the radius . This will return a
* random position if several are found . This test is fairly rough .
2026-03-02 15:53:32 +07:00
*
2026-03-01 12:16:08 +08:00
* Returns null if the biome wasn ' t found
*/
TilePos * BiomeSource : : findBiome ( int x , int z , int r , vector < Biome * > allowed , Random * random )
{
int x0 = ( ( x - r ) > > 2 ) ;
int z0 = ( ( z - r ) > > 2 ) ;
int x1 = ( ( x + r ) > > 2 ) ;
int z1 = ( ( z + r ) > > 2 ) ;
int w = x1 - x0 + 1 ;
int h = z1 - z0 + 1 ;
MemSect ( 50 ) ;
intArray biomes = layer - > getArea ( x0 , z0 , w , h ) ;
TilePos * res = NULL ;
int found = 0 ;
int biomesCount = w * h ;
for ( unsigned int i = 0 ; i < biomesCount ; i + + )
{
int xx = ( x0 + i % w ) < < 2 ;
int zz = ( z0 + i / w ) < < 2 ;
Biome * b = Biome : : biomes [ biomes [ i ] ] ;
if ( find ( allowed . begin ( ) , allowed . end ( ) , b ) ! = allowed . end ( ) )
{
if ( res = = NULL | | random - > nextInt ( found + 1 ) = = 0 )
{
delete res ;
res = new TilePos ( xx , 0 , zz ) ;
found + + ;
}
}
}
MemSect ( 0 ) ;
return res ;
}
void BiomeSource : : update ( )
{
cache - > update ( ) ;
}
//#define DEBUG_SEEDS 50
// 4J added - find a seed for this biomesource that matches certain criteria
# ifdef __PSVITA__
2026-03-02 15:53:32 +07:00
int64_t BiomeSource : : findSeed ( LevelType * generator , bool * pServerRunning ) // MGH - added pRunning, so we can early out of this on Vita as it can take up to 60 secs
2026-03-01 12:16:08 +08:00
# else
2026-03-02 15:53:32 +07:00
int64_t BiomeSource : : findSeed ( LevelType * generator )
2026-03-01 12:16:08 +08:00
# endif
{
2026-03-02 15:53:32 +07:00
int64_t bestSeed = 0 ;
2026-03-01 12:16:08 +08:00
ProgressRenderer * mcprogress = Minecraft : : GetInstance ( ) - > progressRenderer ;
mcprogress - > progressStage ( IDS_PROGRESS_NEW_WORLD_SEED ) ;
# ifndef _CONTENT_PACKAGE
if ( app . DebugSettingsOn ( ) & & app . GetGameSettingsDebugMask ( ProfileManager . GetPrimaryPad ( ) ) & ( 1L < < eDebugSetting_EnableHeightWaterBiomeOverride ) )
{
// Do nothing
}
else
# endif
{
# ifdef DEBUG_SEEDS
for ( int k = 0 ; k < DEBUG_SEEDS ; k + + )
# endif
{
// Try and genuinely random this search up
Random * pr = new Random ( System : : nanoTime ( ) ) ;
// Raw biome data has one result per 4x4 group of tiles.
// Removing a border of 8 from each side since we'll be doing special things at the edge to turn our world into an island, and so don't want to count things
// in the edge region in case they later get removed
static const int biomeWidth = ( 54 * 4 ) - 16 ; // Should be even so we can offset evenly
static const int biomeOffset = - ( biomeWidth / 2 ) ;
// Storage for our biome indices
intArray indices = intArray ( biomeWidth * biomeWidth ) ;
// Storage for the fractional amounts of each biome that will be calculated
float toCompare [ Biome : : BIOME_COUNT ] ;
bool matchFound = false ;
int tryCount = 0 ;
// Just keeping trying to generate seeds until we find one that matches our criteria
do
{
2026-03-02 15:53:32 +07:00
int64_t seed = pr - > nextLong ( ) ;
2026-03-01 12:16:08 +08:00
BiomeSource * biomeSource = new BiomeSource ( seed , generator ) ;
biomeSource - > getRawBiomeIndices ( indices , biomeOffset , biomeOffset , biomeWidth , biomeWidth ) ;
getFracs ( indices , toCompare ) ;
matchFound = getIsMatch ( toCompare ) ;
if ( matchFound ) bestSeed = seed ;
delete biomeSource ;
tryCount + + ;
mcprogress - > progressStagePercentage ( tryCount % 100 ) ;
# ifdef __PSVITA__
} while ( ! matchFound & & * pServerRunning ) ;
# else
} while ( ! matchFound ) ;
# endif
// Clean up
delete pr ;
delete indices . data ;
# ifdef DEBUG_SEEDS
app . DebugPrintf ( " %d: %d tries taken, seed used is %lld \n " , k , tryCount , bestSeed ) ;
BiomeSource * biomeSource = new BiomeSource ( bestSeed ) ;
BiomeArray biomes = biomeSource - > getBiomeBlock ( - 27 * 16 , - 27 * 16 , 54 * 16 , 54 * 16 ) ;
unsigned int * pixels = new unsigned int [ 54 * 16 * 54 * 16 ] ;
for ( int i = 0 ; i < 54 * 16 * 54 * 16 ; i + + )
2026-03-02 15:53:32 +07:00
{
2026-03-01 12:16:08 +08:00
int id = biomes [ i ] - > id ;
// Create following colours:
// 0 ocean 0000 black
// 1 plains 0001 pastel cyan
// 2 desert 0010 green
// 3 extreme hills 0011 yellow
// 4 forest 0100 blue
// 5 taiga 0101 magenta
// 6 swamps 0110 cyan
// 7 river 0111 white
// 8 hell 1000 grey
// 9 end biome 1001 white
// 10 frozen ocean 1010 pastel green
// 11 frozen river 1011 pastel yellow
// 12 ice flats 1100 pastel blue
// 13 ice mountains 1101 pastel magenta
// 14 mushroom island 1110 red
// 15 mushroom shore 1111 pastel red
if ( id = = 1 ) id = 14 ;
else if ( id = = 14 ) id = 1 ;
else if ( id = = 9 ) id = 15 ;
else if ( id = = 15 ) id = 9 ;
pixels [ i ] = 0xff000000 ;
if ( id & 1 ) pixels [ i ] | = 0x00ff0000 ;
if ( id & 2 ) pixels [ i ] | = 0x0000ff00 ;
if ( id & 4 ) pixels [ i ] | = 0x000000ff ;
if ( id & 8 ) pixels [ i ] | = 0x00808080 ;
}
D3DXIMAGE_INFO srcInfo ;
srcInfo . Format = D3DFMT_LIN_A8R8G8B8 ;
srcInfo . ImageFileFormat = D3DXIFF_BMP ;
srcInfo . Width = 54 * 16 ;
srcInfo . Height = 54 * 16 ;
char buf [ 256 ] ;
sprintf ( buf , " GAME: \\ BiomeTest%d.bmp " , k ) ;
RenderManager . SaveTextureData ( buf , & srcInfo , ( int * ) pixels ) ;
delete [ ] pixels ;
delete biomes . data ;
delete biomeSource ;
# endif
}
}
return bestSeed ;
}
// 4J added - get the fractional amounts of each biome type in the given indices
void BiomeSource : : getFracs ( intArray indices , float * fracs )
{
for ( int i = 0 ; i < Biome : : BIOME_COUNT ; i + + )
{
fracs [ i ] = 0.0f ;
}
for ( int i = 0 ; i < indices . length ; i + + )
{
fracs [ indices [ i ] ] + = 1.0f ;
}
for ( int i = 0 ; i < Biome : : BIOME_COUNT ; i + + )
{
fracs [ i ] / = ( float ) ( indices . length ) ;
}
}
// 4J added - determine if this particular set of fractional amounts of biome types matches are requirements
bool BiomeSource : : getIsMatch ( float * frac )
{
// A true for a particular biome type here marks it as one that *has* to be present
2026-03-02 15:53:32 +07:00
static const bool critical [ Biome : : BIOME_COUNT ] = {
2026-03-01 12:16:08 +08:00
true , // ocean
true , // plains
true , // desert
false , // extreme hills
true , // forest
true , // taiga
true , // swamps
false , // river
false , // hell
false , // end biome
false , // frozen ocean
false , // frozen river
false , // ice flats
false , // ice mountains
true , // mushroom island / shore
false , // mushroom shore (combined with above)
false , // beach
false , // desert hills (combined with desert)
false , // forest hills (combined with forest)
false , // taiga hills (combined with taga)
false , // small extreme hills
true , // jungle
false , // jungle hills (combined with jungle)
} ;
// Don't want more than 15% ocean
if ( frac [ 0 ] > 0.15f )
{
return false ;
}
// Consider mushroom shore & islands as the same by finding max
frac [ 14 ] = ( ( frac [ 15 ] > frac [ 14 ] ) ? frac [ 15 ] : frac [ 14 ] ) ;
// Merge desert and desert hills
frac [ 2 ] = ( ( frac [ 17 ] > frac [ 2 ] ) ? frac [ 17 ] : frac [ 2 ] ) ;
// Merge forest and forest hills
frac [ 4 ] = ( ( frac [ 18 ] > frac [ 4 ] ) ? frac [ 18 ] : frac [ 4 ] ) ;
// Merge taiga and taiga hills
frac [ 5 ] = ( ( frac [ 19 ] > frac [ 5 ] ) ? frac [ 19 ] : frac [ 5 ] ) ;
// Merge jungle and jungle hills
frac [ 21 ] = ( ( frac [ 22 ] > frac [ 21 ] ) ? frac [ 22 ] : frac [ 21 ] ) ;
// Loop through all biome types, and:
// (1) count them
// (2) give up if one of the critical ones is missing
int typeCount = 0 ;
for ( int i = 0 ; i < Biome : : BIOME_COUNT ; i + + )
{
// We want to skip some where we have merged with another type
if ( i = = 15 | | i = = 17 | | i = = 18 | | i = = 19 | | i = = 22 ) continue ;
// Consider 0.1% as being "present" - this equates an area of about 3 chunks
if ( frac [ i ] > 0.001f )
{
typeCount + + ;
}
else
{
// If a critical biome is missing, just give up
if ( critical [ i ] )
{
return false ;
}
}
}
// Consider as suitable if we've got all the critical ones, and in total 9 or more - currently there's 8 critical so this just forces at least 1 more others
return ( typeCount > = 9 ) ;
}