map generation framework under development... not quite operational at this point.

This commit is contained in:
Perttu Ahola 2011-01-30 01:44:54 +02:00
parent 8788fffec0
commit be851871cd
13 changed files with 965 additions and 372 deletions

@ -76,21 +76,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// is very low // is very low
#define BLOCK_SEND_DISABLE_LIMITS_MAX_D 1 #define BLOCK_SEND_DISABLE_LIMITS_MAX_D 1
// The fps limiter will leave this much free time
//#define FREETIME_RATIO 0.15
//#define FREETIME_RATIO 0.0
#define FREETIME_RATIO 0.05
#define PLAYER_INVENTORY_SIZE (8*4) #define PLAYER_INVENTORY_SIZE (8*4)
#define SIGN_TEXT_MAX_LENGTH 50 #define SIGN_TEXT_MAX_LENGTH 50
// Whether to catch all std::exceptions. // Whether to catch all std::exceptions.
// Assert will be called on such an event. // Assert will be called on such an event.
#ifdef DEBUG // In debug mode, leave these for the debugger and don't catch them.
#define CATCH_UNHANDLED_EXCEPTIONS 0 #ifdef NDEBUG
#else
#define CATCH_UNHANDLED_EXCEPTIONS 1 #define CATCH_UNHANDLED_EXCEPTIONS 1
#else
#define CATCH_UNHANDLED_EXCEPTIONS 0
#endif #endif
/* /*

@ -49,8 +49,8 @@ void set_default_settings()
g_settings.setDefault("max_simultaneous_block_sends_per_client", "1"); g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");
//g_settings.setDefault("max_simultaneous_block_sends_per_client", "2"); //g_settings.setDefault("max_simultaneous_block_sends_per_client", "2");
g_settings.setDefault("max_simultaneous_block_sends_server_total", "4"); g_settings.setDefault("max_simultaneous_block_sends_server_total", "4");
g_settings.setDefault("max_block_send_distance", "6"); g_settings.setDefault("max_block_send_distance", "7");
g_settings.setDefault("max_block_generate_distance", "6"); g_settings.setDefault("max_block_generate_distance", "7");
g_settings.setDefault("time_send_interval", "20"); g_settings.setDefault("time_send_interval", "20");
g_settings.setDefault("time_speed", "96"); g_settings.setDefault("time_speed", "96");
g_settings.setDefault("server_unload_unused_sectors_timeout", "60"); g_settings.setDefault("server_unload_unused_sectors_timeout", "60");

@ -168,7 +168,7 @@ TODO: Make fetching sector's blocks more efficient when rendering
TODO: Flowing water animation TODO: Flowing water animation
FIXME: The new texture stuff is slow on wine FIXME(FIXED): The new texture stuff is slow on wine
- A basic grassy ground block takes 20-40ms - A basic grassy ground block takes 20-40ms
- A bit more complicated block can take 270ms - A bit more complicated block can take 270ms
- On linux, a similar one doesn't take long at all (14ms) - On linux, a similar one doesn't take long at all (14ms)
@ -182,6 +182,15 @@ FIXME: The new texture stuff is slow on wine
is fast to compare, which refers to a cached string, or is fast to compare, which refers to a cached string, or
* Make TextureSpec for using instead of strings * Make TextureSpec for using instead of strings
FIXME(FIXED): A lock condition is possible:
1) MapBlock::updateMesh() is called from client asynchronously:
- AsyncProcessData() -> Map::updateMeshes()
2) Asynchronous locks m_temp_mods_mutex
3) MapBlock::updateMesh() is called from client synchronously:
- Client::step() -> Environment::step()
4) Synchronous starts waiting for m_temp_mods_mutex
5) Asynchronous calls getTexture, which starts waiting for main thread
Configuration: Configuration:
-------------- --------------
@ -255,6 +264,20 @@ Map:
NOTE: There are some lighting-related todos and fixmes in NOTE: There are some lighting-related todos and fixmes in
ServerMap::emergeBlock. And there always will be. 8) ServerMap::emergeBlock. And there always will be. 8)
TODO: Mineral and ground material properties
- This way mineral ground toughness can be calculated with just
some formula, as well as tool strengths
TODO: Change AttributeList to split the area into smaller sections so
that searching won't be as heavy.
TODO: Remove HMParams
TODO: Flowing water to actually contain flow direction information
TODO: Remove duplicate lighting implementation from Map (leave
VoxelManipulator, which is faster)
FEATURE: Map generator version 2 FEATURE: Map generator version 2
- Create surface areas based on central points; a given point's - Create surface areas based on central points; a given point's
area type is given by the nearest central point area type is given by the nearest central point
@ -269,6 +292,11 @@ FEATURE: Map generator version 2
FEATURE: The map could be generated procedually: FEATURE: The map could be generated procedually:
- This would need the map to be generated in larger pieces - This would need the map to be generated in larger pieces
- How large? How do they connect to each other? - How large? How do they connect to each other?
- It has to be split vertically also
- Lighting would not have to be necessarily calculated until
the blocks are actually needed - it would be quite fast
- Something like 64*64*16 MapBlocks?
- TODO: Separate lighting and block generation
* Make the stone level with a heightmap * Make the stone level with a heightmap
* Carve out stuff in the stone * Carve out stuff in the stone
* Dump dirt all around, and simulate it falling off steep * Dump dirt all around, and simulate it falling off steep
@ -283,20 +311,13 @@ FEATURE: The map could be generated procedually:
parameter field is free for this. parameter field is free for this.
- Simulate rock falling from cliffs when water has removed - Simulate rock falling from cliffs when water has removed
enough solid rock from the bottom enough solid rock from the bottom
TODO: Lazy lighting updates:
TODO: Mineral and ground material properties - Set updateLighting to ignore MapBlocks with expired lighting,
- This way mineral ground toughness can be calculated with just except the blocks specified to it
some formula, as well as tool strengths - When a MapBlock is generated, lighting expires in all blocks
touching it (26 blocks + self)
TODO: Change AttributeList to split the area into smaller sections so - When a lighting-wise valid MapBlock is needed and lighting of it
that searching won't be as heavy. has expired, what to do?
TODO: Remove HMParams
TODO: Flowing water to actually contain flow direction information
TODO: Remove duplicate lighting implementation from Map (leave
VoxelManipulator, which is faster)
Doing now: Doing now:
---------- ----------
@ -1522,6 +1543,16 @@ int main(int argc, char *argv[])
srand(time(0)); srand(time(0));
mysrand(time(0)); mysrand(time(0));
/*
Pre-initialize some stuff with a dummy irrlicht wrapper.
These are needed for unit tests at least.
*/
IIrrlichtWrapper irrlicht_dummy;
init_mapnode(&irrlicht_dummy);
/* /*
Run unit tests Run unit tests
*/ */
@ -1684,7 +1715,7 @@ int main(int argc, char *argv[])
skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0)); skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
/* /*
Preload some textures Preload some textures and stuff
*/ */
init_content_inventory_texture_paths(); init_content_inventory_texture_paths();
@ -2131,20 +2162,6 @@ int main(int argc, char *argv[])
dtime_jitter1_max_fraction dtime_jitter1_max_fraction
= dtime_jitter1_max_sample / (dtime_avg1+0.001); = dtime_jitter1_max_sample / (dtime_avg1+0.001);
jitter1_max = 0.0; jitter1_max = 0.0;
/*
Control freetime ratio
*/
/*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
{
if(g_freetime_ratio < FREETIME_RATIO_MAX)
g_freetime_ratio += 0.01;
}
else
{
if(g_freetime_ratio > FREETIME_RATIO_MIN)
g_freetime_ratio -= 0.01;
}*/
} }
} }

@ -67,10 +67,8 @@ Map::~Map()
} }
} }
MapSector * Map::getSectorNoGenerate(v2s16 p) MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
{ {
JMutexAutoLock lock(m_sector_mutex);
if(m_sector_cache != NULL && p == m_sector_cache_p){ if(m_sector_cache != NULL && p == m_sector_cache_p){
MapSector * sector = m_sector_cache; MapSector * sector = m_sector_cache;
// Reset inactivity timer // Reset inactivity timer
@ -79,11 +77,9 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
} }
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p); core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
// If sector doesn't exist, throw an exception
if(n == NULL) if(n == NULL)
{ return NULL;
throw InvalidPositionException();
}
MapSector *sector = n->getValue(); MapSector *sector = n->getValue();
@ -91,13 +87,27 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
m_sector_cache_p = p; m_sector_cache_p = p;
m_sector_cache = sector; m_sector_cache = sector;
//MapSector * ref(sector);
// Reset inactivity timer // Reset inactivity timer
sector->usage_timer = 0.0; sector->usage_timer = 0.0;
return sector; return sector;
} }
MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
{
JMutexAutoLock lock(m_sector_mutex);
return getSectorNoGenerateNoExNoLock(p);
}
MapSector * Map::getSectorNoGenerate(v2s16 p)
{
MapSector *sector = getSectorNoGenerateNoEx(p);
if(sector == NULL)
throw InvalidPositionException();
return sector;
}
MapBlock * Map::getBlockNoCreate(v3s16 p3d) MapBlock * Map::getBlockNoCreate(v3s16 p3d)
{ {
v2s16 p2d(p3d.X, p3d.Z); v2s16 p2d(p3d.X, p3d.Z);
@ -631,6 +641,8 @@ void Map::updateLighting(enum LightBank bank,
//bool debug=true; //bool debug=true;
//u32 count_was = modified_blocks.size(); //u32 count_was = modified_blocks.size();
core::map<v3s16, MapBlock*> blocks_to_update;
core::map<v3s16, bool> light_sources; core::map<v3s16, bool> light_sources;
core::map<v3s16, u8> unlight_from; core::map<v3s16, u8> unlight_from;
@ -650,6 +662,8 @@ void Map::updateLighting(enum LightBank bank,
v3s16 pos = block->getPos(); v3s16 pos = block->getPos();
modified_blocks.insert(pos, block); modified_blocks.insert(pos, block);
blocks_to_update.insert(pos, block);
/* /*
Clear all light from block Clear all light from block
*/ */
@ -699,10 +713,12 @@ void Map::updateLighting(enum LightBank bank,
} }
else if(bank == LIGHTBANK_NIGHT) else if(bank == LIGHTBANK_NIGHT)
{ {
// For night lighting, sunlight is not propagated
break; break;
} }
else else
{ {
// Invalid lighting bank
assert(0); assert(0);
} }
@ -710,7 +726,7 @@ void Map::updateLighting(enum LightBank bank,
<<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid" <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
<<std::endl;*/ <<std::endl;*/
// Else get the block below and loop to it // Bottom sunlight is not valid; get the block and loop to it
pos.Y--; pos.Y--;
try{ try{
@ -737,13 +753,6 @@ void Map::updateLighting(enum LightBank bank,
dstream<<"unspreadLight modified "<<diff<<std::endl; dstream<<"unspreadLight modified "<<diff<<std::endl;
} }
// TODO: Spread light from propagated sunlight?
// Yes, add it to light_sources... somehow.
// It has to be added at somewhere above, in the loop.
// TODO
// NOTE: This actually works fine without doing so
// - Find out why it works
{ {
TimeTaker timer("spreadLight"); TimeTaker timer("spreadLight");
spreadLight(bank, light_sources, modified_blocks); spreadLight(bank, light_sources, modified_blocks);
@ -760,16 +769,43 @@ void Map::updateLighting(enum LightBank bank,
{ {
//MapVoxelManipulator vmanip(this); //MapVoxelManipulator vmanip(this);
// Make a manual voxel manipulator and load all the blocks
// that touch the requested blocks
ManualMapVoxelManipulator vmanip(this); ManualMapVoxelManipulator vmanip(this);
core::map<v3s16, MapBlock*>::Iterator i; core::map<v3s16, MapBlock*>::Iterator i;
i = a_blocks.getIterator(); i = blocks_to_update.getIterator();
for(; i.atEnd() == false; i++) for(; i.atEnd() == false; i++)
{ {
MapBlock *block = i.getNode()->getValue(); MapBlock *block = i.getNode()->getValue();
v3s16 p = block->getPos(); v3s16 p = block->getPos();
// Add all surrounding blocks
vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1)); vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
/*
Add all surrounding blocks that have up-to-date lighting
NOTE: This doesn't quite do the job (not everything
appropriate is lighted)
*/
/*for(s16 z=-1; z<=1; z++)
for(s16 y=-1; y<=1; y++)
for(s16 x=-1; x<=1; x++)
{
v3s16 p(x,y,z);
MapBlock *block = getBlockNoCreateNoEx(p);
if(block == NULL)
continue;
if(block->isDummy())
continue;
if(block->getLightingExpired())
continue;
vmanip.initialEmerge(p, p);
}*/
// Lighting of block will be updated completely
block->setLightingExpired(false);
} }
{ {
//TimeTaker timer("unSpreadLight"); //TimeTaker timer("unSpreadLight");
vmanip.unspreadLight(bank, unlight_from, light_sources); vmanip.unspreadLight(bank, unlight_from, light_sources);
@ -1407,6 +1443,8 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
u32 loopcount = 0; u32 loopcount = 0;
u32 initial_size = m_transforming_liquid.size(); u32 initial_size = m_transforming_liquid.size();
//dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
while(m_transforming_liquid.size() != 0) while(m_transforming_liquid.size() != 0)
{ {
/* /*
@ -1682,6 +1720,12 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
Map(dout_server), Map(dout_server),
m_heightmap(NULL) m_heightmap(NULL)
{ {
//m_chunksize = 64;
//m_chunksize = 16;
//m_chunksize = 8;
m_chunksize = 2;
/* /*
Experimental and debug stuff Experimental and debug stuff
*/ */
@ -1917,18 +1961,6 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
dstream<<DTIME<<"Initializing new map."<<std::endl; dstream<<DTIME<<"Initializing new map."<<std::endl;
// Create master heightmap // Create master heightmap
/*ValueGenerator *maxgen =
ValueGenerator::deSerialize(hmp.randmax);
ValueGenerator *factorgen =
ValueGenerator::deSerialize(hmp.randfactor);
ValueGenerator *basegen =
ValueGenerator::deSerialize(hmp.base);
m_heightmap = new UnlimitedHeightmap
(hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
/*m_heightmap = new UnlimitedHeightmap
(hmp.blocksize, &m_padb);*/
m_heightmap = new UnlimitedHeightmap m_heightmap = new UnlimitedHeightmap
(32, &m_padb); (32, &m_padb);
@ -1966,28 +1998,133 @@ ServerMap::~ServerMap()
if(m_heightmap != NULL) if(m_heightmap != NULL)
delete m_heightmap; delete m_heightmap;
/*
Free all MapChunks
*/
core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
for(; i.atEnd() == false; i++)
{
MapChunk *chunk = i.getNode()->getValue();
delete chunk;
}
} }
MapSector * ServerMap::emergeSector(v2s16 p2d) MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos)
{
// Return if chunk already exists
MapChunk *chunk = getChunk(chunkpos);
if(chunk)
return chunk;
/*
Add all sectors
*/
dstream<<"generateChunkRaw(): "
<<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
<<std::endl;
TimeTaker timer("generateChunkRaw()");
v2s16 sectorpos_base = chunk_to_sector(chunkpos);
core::map<v3s16, MapBlock*> changed_blocks;
core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
u32 generated_block_count = 0;
for(s16 y=0; y<m_chunksize; y++)
{
/*dstream<<"Generating sectors "
<<"("<<sectorpos_base.X<<"..."
<<(sectorpos_base.X+m_chunksize-1)
<<", "<<y<<")"
<<std::endl;*/
// With caves_amount attribute fetch: ~90ms (379ms peaks)
// Without: ~38ms (396ms peaks)
//TimeTaker timer("Chunk sector row");
for(s16 x=0; x<m_chunksize; x++)
{
v2s16 sectorpos = sectorpos_base + v2s16(x,y);
/*dstream<<"Generating sector "
<<"("<<sectorpos.X<<","<<sectorpos.Y<<")"
<<std::endl;*/
// Generate sector
ServerMapSector *sector = generateSector(sectorpos);
/*
Generate main blocks of sector
*/
s16 d = 8;
for(s16 y2=-d/2; y2<d/2; y2++)
{
v3s16 p(x,y2,y);
// Check that the block doesn't exist already
if(sector->getBlockNoCreateNoEx(y2))
continue;
generateBlock(p, NULL, sector, changed_blocks,
lighting_invalidated_blocks);
generated_block_count++;
}
}
}
dstream<<"generateChunkRaw generated "<<generated_block_count
<<" blocks"<<std::endl;
{
TimeTaker timer2("generateChunkRaw() lighting");
// Update lighting
core::map<v3s16, MapBlock*> lighting_modified_blocks;
updateLighting(lighting_invalidated_blocks, lighting_modified_blocks);
}
// Add chunk meta information
chunk = new MapChunk();
m_chunks.insert(chunkpos, chunk);
return chunk;
}
MapChunk* ServerMap::generateChunk(v2s16 chunkpos)
{
/*
Generate chunk and neighbors
*/
for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++)
{
generateChunkRaw(chunkpos + v2s16(x,y));
}
/*
Get chunk
*/
MapChunk *chunk = getChunk(chunkpos);
assert(chunk);
// Set non-volatile
chunk->setIsVolatile(false);
// Return it
return chunk;
}
ServerMapSector * ServerMap::generateSector(v2s16 p2d)
{ {
DSTACK("%s: p2d=(%d,%d)", DSTACK("%s: p2d=(%d,%d)",
__FUNCTION_NAME, __FUNCTION_NAME,
p2d.X, p2d.Y); p2d.X, p2d.Y);
// Check that it doesn't exist already
try{
return getSectorNoGenerate(p2d);
}
catch(InvalidPositionException &e)
{
}
/* // Check that it doesn't exist already
Try to load the sector from disk. ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
*/ if(sector != NULL)
if(loadSectorFull(p2d) == true) return sector;
{
return getSectorNoGenerate(p2d);
}
/* /*
If there is no master heightmap, throw. If there is no master heightmap, throw.
@ -2016,7 +2153,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
// Heightmap side width // Heightmap side width
s16 hm_d = MAP_BLOCKSIZE / hm_split; s16 hm_d = MAP_BLOCKSIZE / hm_split;
ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split); sector = new ServerMapSector(this, p2d, hm_split);
// Sector position on map in nodes // Sector position on map in nodes
v2s16 nodepos2d = p2d * MAP_BLOCKSIZE; v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
@ -2068,7 +2205,9 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
Get local attributes Get local attributes
*/ */
float local_plants_amount = 0.0; float local_plants_amount = 0.5;
#if 0
{ {
//dstream<<"emergeSector(): Reading point attribute lists"<<std::endl; //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
//TimeTaker attrtimer("emergeSector() attribute fetch"); //TimeTaker attrtimer("emergeSector() attribute fetch");
@ -2081,6 +2220,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
local_plants_amount = local_plants_amount =
palist->getInterpolatedFloat(nodepos2d); palist->getInterpolatedFloat(nodepos2d);
} }
#endif
/* /*
Generate sector heightmap Generate sector heightmap
@ -2201,90 +2341,103 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
/* /*
Insert to container Insert to container
*/ */
JMutexAutoLock lock(m_sector_mutex);
m_sectors.insert(p2d, sector); m_sectors.insert(p2d, sector);
return sector; return sector;
} }
MapBlock * ServerMap::emergeBlock( MapSector * ServerMap::emergeSector(v2s16 p2d)
{
DSTACK("%s: p2d=(%d,%d)",
__FUNCTION_NAME,
p2d.X, p2d.Y);
/*
Check if it exists already in memory
*/
MapSector *sector = getSectorNoGenerateNoEx(p2d);
if(sector != NULL)
return sector;
/*
Try to load it from disk
*/
if(loadSectorFull(p2d) == true)
{
MapSector *sector = getSectorNoGenerateNoEx(p2d);
if(sector == NULL)
{
dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
throw InvalidPositionException("");
}
return sector;
}
/*
Check chunk status
*/
v2s16 chunkpos = sector_to_chunk(p2d);
bool chunk_exists = false;
MapChunk *chunk = getChunk(chunkpos);
if(chunk && chunk->getIsVolatile() == false)
chunk_exists = true;
/*
If chunk is not generated, generate chunk
*/
if(chunk_exists == false)
{
// Generate chunk and neighbors
generateChunk(chunkpos);
}
/*
Return sector if it exists now
*/
sector = getSectorNoGenerateNoEx(p2d);
if(sector != NULL)
return sector;
/*
generateChunk should have generated the sector
*/
assert(0);
/*
Generate directly
*/
//return generateSector();
}
MapBlock * ServerMap::generateBlock(
v3s16 p, v3s16 p,
bool only_from_disk, MapBlock *original_dummy,
ServerMapSector *sector,
core::map<v3s16, MapBlock*> &changed_blocks, core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
) )
{ {
DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d", DSTACK("%s: p=(%d,%d,%d)",
__FUNCTION_NAME, __FUNCTION_NAME,
p.X, p.Y, p.Z, only_from_disk); p.X, p.Y, p.Z);
/*dstream<<"ServerMap::emergeBlock(): " /*dstream<<"generateBlock(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")" <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<", only_from_disk="<<only_from_disk<<std::endl;*/ <<std::endl;*/
MapBlock *block = original_dummy;
v2s16 p2d(p.X, p.Z); v2s16 p2d(p.X, p.Z);
s16 block_y = p.Y; s16 block_y = p.Y;
/*
This will create or load a sector if not found in memory.
If block exists on disk, it will be loaded.
NOTE: On old save formats, this will be slow, as it generates
lighting on blocks for them.
*/
ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
assert(sector->getId() == MAPSECTOR_SERVER);
// Try to get a block from the sector
MapBlock *block = NULL;
bool not_on_disk = false;
try{
block = sector->getBlockNoCreate(block_y);
if(block->isDummy() == true)
not_on_disk = true;
else
return block;
}
catch(InvalidPositionException &e)
{
not_on_disk = true;
}
/*
If block was not found on disk and not going to generate a
new one, make sure there is a dummy block in place.
*/
if(not_on_disk && only_from_disk)
{
if(block == NULL)
{
// Create dummy block
block = new MapBlock(this, p, true);
// Add block to sector
sector->insertBlock(block);
}
// Done.
return block;
}
//dstream<<"Not found on disk, generating."<<std::endl;
// 0ms
//TimeTaker("emergeBlock() generate");
/* /*
Do not generate over-limit Do not generate over-limit
*/ */
if(blockpos_over_limit(p)) if(blockpos_over_limit(p))
throw InvalidPositionException("emergeBlock(): pos. over limit"); {
dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
/* throw InvalidPositionException("generateBlock(): pos. over limit");
OK; Not found. }
Go on generating the block.
TODO: If a dungeon gets generated so that it's side gets
revealed to the outside air, the lighting should be
recalculated.
*/
/* /*
If block doesn't exist, create one. If block doesn't exist, create one.
@ -2318,7 +2471,7 @@ MapBlock * ServerMap::emergeBlock(
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++) for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
{ {
//dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl; //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
float surface_y_f = sector->getGroundHeight(v2s16(x0,z0)); float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
//assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE); //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
@ -2451,23 +2604,25 @@ MapBlock * ServerMap::emergeBlock(
Get local attributes Get local attributes
*/ */
//dstream<<"emergeBlock(): Getting local attributes"<<std::endl; //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
float caves_amount = 0; float caves_amount = 0.5;
#if 0
{ {
/* /*
NOTE: BEWARE: Too big amount of attribute points slows verything NOTE: BEWARE: Too big amount of attribute points slows verything
down by a lot. down by a lot.
1 interpolation from 5000 points takes 2-3ms. 1 interpolation from 5000 points takes 2-3ms.
*/ */
//TimeTaker timer("emergeBlock() local attribute retrieval"); //TimeTaker timer("generateBlock() local attribute retrieval");
v2s16 nodepos2d = p2d * MAP_BLOCKSIZE; v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
PointAttributeList *list_caves_amount = m_padb.getList("caves_amount"); PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d); caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
} }
#endif
//dstream<<"emergeBlock(): Done"<<std::endl; //dstream<<"generateBlock(): Done"<<std::endl;
/* /*
Generate dungeons Generate dungeons
@ -2617,10 +2772,10 @@ continue_generating:
do_generate_dungeons = false; do_generate_dungeons = false;
} }
// Don't generate if mostly underwater surface // Don't generate if mostly underwater surface
else if(mostly_underwater_surface) /*else if(mostly_underwater_surface)
{ {
do_generate_dungeons = false; do_generate_dungeons = false;
} }*/
// Partly underground = cave // Partly underground = cave
else if(!completely_underground) else if(!completely_underground)
{ {
@ -2723,7 +2878,7 @@ continue_generating:
/* /*
This is used for guessing whether or not the block should This is used for guessing whether or not the block should
receive sunlight from the top if the top block doesn't exist receive sunlight from the top if the block above doesn't exist
*/ */
block->setIsUnderground(completely_underground); block->setIsUnderground(completely_underground);
@ -3075,7 +3230,7 @@ continue_generating:
} }
else else
{ {
dstream<<"ServerMap::emergeBlock(): " dstream<<"ServerMap::generateBlock(): "
"Invalid heightmap object" "Invalid heightmap object"
<<std::endl; <<std::endl;
} }
@ -3099,30 +3254,6 @@ continue_generating:
objects->remove(*i); objects->remove(*i);
} }
/*
Initially update sunlight
*/
{
core::map<v3s16, bool> light_sources;
bool black_air_left = false;
bool bottom_invalid =
block->propagateSunlight(light_sources, true,
&black_air_left, true);
// If sunlight didn't reach everywhere and part of block is
// above ground, lighting has to be properly updated
if(black_air_left && some_part_underground)
{
lighting_invalidated_blocks[block->getPos()] = block;
}
if(bottom_invalid)
{
lighting_invalidated_blocks[block->getPos()] = block;
}
}
/* /*
Translate sector's changed blocks to global changed blocks Translate sector's changed blocks to global changed blocks
*/ */
@ -3136,21 +3267,158 @@ continue_generating:
changed_blocks.insert(block->getPos(), block); changed_blocks.insert(block->getPos(), block);
} }
block->setLightingExpired(true);
#if 0
/* /*
Debug information Debug information
*/ */
if(0) dstream
<<"lighting_invalidated_blocks.size()"
<<", has_dungeons"
<<", completely_ug"
<<", some_part_ug"
<<" "<<lighting_invalidated_blocks.size()
<<", "<<has_dungeons
<<", "<<completely_underground
<<", "<<some_part_underground
<<std::endl;
#endif
return block;
}
MapBlock * ServerMap::emergeBlock(
v3s16 p,
bool only_from_disk,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
)
{
DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
__FUNCTION_NAME,
p.X, p.Y, p.Z, only_from_disk);
/*dstream<<"emergeBlock(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;*/
v2s16 p2d(p.X, p.Z);
s16 block_y = p.Y;
/*
This will create or load a sector if not found in memory.
If block exists on disk, it will be loaded.
NOTE: On old save formats, this will be slow, as it generates
lighting on blocks for them.
*/
ServerMapSector *sector;
try{
sector = (ServerMapSector*)emergeSector(p2d);
assert(sector->getId() == MAPSECTOR_SERVER);
}
/*catch(InvalidPositionException &e)
{ {
dstream dstream<<"emergeBlock: emergeSector() failed"<<std::endl;
<<"lighting_invalidated_blocks.size()" throw e;
<<", has_dungeons" }*/
<<", completely_ug" catch(std::exception &e)
<<", some_part_ug" {
<<" "<<lighting_invalidated_blocks.size() dstream<<"emergeBlock: emergeSector() failed: "
<<", "<<has_dungeons <<e.what()<<std::endl;
<<", "<<completely_underground throw e;
<<", "<<some_part_underground }
<<std::endl;
/*
Try to get a block from the sector
*/
bool does_not_exist = false;
bool lighting_expired = false;
MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
if(block == NULL)
{
does_not_exist = true;
}
else if(block->isDummy() == true)
{
does_not_exist = true;
}
else if(block->getLightingExpired())
{
lighting_expired = true;
}
else
{
// Valid block
//dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
return block;
}
/*
If block was not found on disk and not going to generate a
new one, make sure there is a dummy block in place.
*/
if(only_from_disk && (does_not_exist || lighting_expired))
{
//dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
if(block == NULL)
{
// Create dummy block
block = new MapBlock(this, p, true);
// Add block to sector
sector->insertBlock(block);
}
// Done.
return block;
}
//dstream<<"Not found on disk, generating."<<std::endl;
// 0ms
//TimeTaker("emergeBlock() generate");
//dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
/*
If the block doesn't exist, generate the block.
*/
if(does_not_exist)
{
block = generateBlock(p, block, sector, changed_blocks,
lighting_invalidated_blocks);
}
if(lighting_expired)
{
lighting_invalidated_blocks.insert(p, block);
}
/*
Initially update sunlight
*/
{
core::map<v3s16, bool> light_sources;
bool black_air_left = false;
bool bottom_invalid =
block->propagateSunlight(light_sources, true,
&black_air_left, true);
// If sunlight didn't reach everywhere and part of block is
// above ground, lighting has to be properly updated
//if(black_air_left && some_part_underground)
if(black_air_left)
{
lighting_invalidated_blocks[block->getPos()] = block;
}
if(bottom_invalid)
{
lighting_invalidated_blocks[block->getPos()] = block;
}
} }
/* /*

105
src/map.h

@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapsector.h" #include "mapsector.h"
#include "constants.h" #include "constants.h"
#include "voxel.h" #include "voxel.h"
#include "mapchunk.h"
#define MAPTYPE_BASE 0 #define MAPTYPE_BASE 0
#define MAPTYPE_SERVER 1 #define MAPTYPE_SERVER 1
@ -86,8 +87,13 @@ public:
); );
} }
//bool sectorExists(v2s16 p); // On failure returns NULL
MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
// On failure returns NULL
MapSector * getSectorNoGenerateNoEx(v2s16 p2d);
// On failure throws InvalidPositionException
MapSector * getSectorNoGenerate(v2s16 p2d); MapSector * getSectorNoGenerate(v2s16 p2d);
/* /*
This is overloaded by ClientMap and ServerMap to allow This is overloaded by ClientMap and ServerMap to allow
their differing fetch methods. their differing fetch methods.
@ -318,9 +324,90 @@ public:
} }
/* /*
Forcefully get a sector from somewhere Map generation
*/
// Returns the position of the chunk where the sector is in
v2s16 sector_to_chunk(v2s16 sectorpos)
{
sectorpos.X += m_chunksize / 2;
sectorpos.Y += m_chunksize / 2;
v2s16 chunkpos = getContainerPos(sectorpos, m_chunksize);
return chunkpos;
}
// Returns the position of the (0,0) sector of the chunk
v2s16 chunk_to_sector(v2s16 chunkpos)
{
v2s16 sectorpos(
chunkpos.X * m_chunksize,
chunkpos.Y * m_chunksize
);
sectorpos.X -= m_chunksize / 2;
sectorpos.Y -= m_chunksize / 2;
return sectorpos;
}
/*
Get a chunk.
*/
MapChunk *getChunk(v2s16 chunkpos)
{
core::map<v2s16, MapChunk*>::Node *n;
n = m_chunks.find(chunkpos);
if(n == NULL)
return NULL;
return n->getValue();
}
/*
Generate a chunk.
All chunks touching this one can be altered also.
Doesn't update lighting.
*/
MapChunk* generateChunkRaw(v2s16 chunkpos);
/*
Generate a chunk and its neighbors so that it won't be touched
anymore.
Doesn't update lighting.
*/
MapChunk* generateChunk(v2s16 chunkpos);
/*
Generate a sector.
This is mainly called by generateChunkRaw.
*/
ServerMapSector * generateSector(v2s16 p);
/*
Get a sector from somewhere.
- Check memory
- Check disk
- Generate chunk
*/ */
MapSector * emergeSector(v2s16 p); MapSector * emergeSector(v2s16 p);
MapBlock * generateBlock(
v3s16 p,
MapBlock *original_dummy,
ServerMapSector *sector,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
MapBlock * emergeBlock(
v3s16 p,
bool only_from_disk,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
#if 0
/* /*
Forcefully get a block from somewhere. Forcefully get a block from somewhere.
@ -346,7 +433,12 @@ public:
core::map<v3s16, MapBlock*> &changed_blocks, core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
); );
#endif
/*
Misc. helper functions for fiddling with directory and file
names when saving
*/
void createDir(std::string path); void createDir(std::string path);
void createSaveDir(); void createSaveDir();
// returns something like "xxxxxxxx" // returns something like "xxxxxxxx"
@ -396,8 +488,17 @@ private:
std::string m_savedir; std::string m_savedir;
bool m_map_saving_enabled; bool m_map_saving_enabled;
// Chunk size in MapSectors
s16 m_chunksize;
// Chunks
core::map<v2s16, MapChunk*> m_chunks;
}; };
/*
ClientMap stuff
*/
#ifndef SERVER #ifndef SERVER
struct MapDrawControl struct MapDrawControl

@ -293,7 +293,8 @@ void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
Gets node tile from any place relative to block. Gets node tile from any place relative to block.
Returns TILE_NODE if doesn't exist or should not be drawn. Returns TILE_NODE if doesn't exist or should not be drawn.
*/ */
TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir) TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
NodeModMap &temp_mods)
{ {
TileSpec spec; TileSpec spec;
spec = mn.getTile(face_dir); spec = mn.getTile(face_dir);
@ -301,13 +302,15 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
/* /*
Check temporary modifications on this node Check temporary modifications on this node
*/ */
core::map<v3s16, NodeMod>::Node *n; /*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p); n = m_temp_mods.find(p);
// If modified // If modified
if(n != NULL) if(n != NULL)
{ {
struct NodeMod mod = n->getValue(); struct NodeMod mod = n->getValue();*/
NodeMod mod;
if(temp_mods.get(p, &mod))
{
if(mod.type == NODEMOD_CHANGECONTENT) if(mod.type == NODEMOD_CHANGECONTENT)
{ {
MapNode mn2(mod.param); MapNode mn2(mod.param);
@ -326,18 +329,20 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
return spec; return spec;
} }
u8 MapBlock::getNodeContent(v3s16 p, MapNode mn) u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
{ {
/* /*
Check temporary modifications on this node Check temporary modifications on this node
*/ */
core::map<v3s16, NodeMod>::Node *n; /*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p); n = m_temp_mods.find(p);
// If modified // If modified
if(n != NULL) if(n != NULL)
{ {
struct NodeMod mod = n->getValue(); struct NodeMod mod = n->getValue();*/
NodeMod mod;
if(temp_mods.get(p, &mod))
{
if(mod.type == NODEMOD_CHANGECONTENT) if(mod.type == NODEMOD_CHANGECONTENT)
{ {
// Overrides content // Overrides content
@ -376,7 +381,8 @@ void MapBlock::updateFastFaceRow(
v3f translate_dir_f, v3f translate_dir_f,
v3s16 face_dir, v3s16 face_dir,
v3f face_dir_f, v3f face_dir_f,
core::array<FastFace> &dest) core::array<FastFace> &dest,
NodeModMap &temp_mods)
{ {
v3s16 p = startpos; v3s16 p = startpos;
@ -387,8 +393,8 @@ void MapBlock::updateFastFaceRow(
u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir); u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
TileSpec tile0 = getNodeTile(n0, p, face_dir); TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir); TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
for(u16 j=0; j<length; j++) for(u16 j=0; j<length; j++)
{ {
@ -406,8 +412,8 @@ void MapBlock::updateFastFaceRow(
p_next = p + translate_dir; p_next = p + translate_dir;
n0_next = getNodeParentNoEx(p_next); n0_next = getNodeParentNoEx(p_next);
n1_next = getNodeParentNoEx(p_next + face_dir); n1_next = getNodeParentNoEx(p_next + face_dir);
tile0_next = getNodeTile(n0_next, p_next, face_dir); tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir); tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir); light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
if(tile0_next == tile0 if(tile0_next == tile0
@ -427,8 +433,8 @@ void MapBlock::updateFastFaceRow(
*/ */
//u8 mf = face_contents(tile0, tile1); //u8 mf = face_contents(tile0, tile1);
// This is hackish // This is hackish
u8 content0 = getNodeContent(p, n0); u8 content0 = getNodeContent(p, n0, temp_mods);
u8 content1 = getNodeContent(p + face_dir, n1); u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
u8 mf = face_contents(content0, content1); u8 mf = face_contents(content0, content1);
if(mf != 0) if(mf != 0)
@ -594,6 +600,16 @@ void MapBlock::updateMesh(u32 daynight_ratio)
v3f posRelative_f(getPosRelative().X, getPosRelative().Y, v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
getPosRelative().Z); // floating point conversion getPosRelative().Z); // floating point conversion
/*
Avoid interlocks by copying m_temp_mods
*/
NodeModMap temp_mods;
{
JMutexAutoLock lock(m_temp_mods_mutex);
m_temp_mods.copy(temp_mods);
}
/* /*
We are including the faces of the trailing edges of the block. We are including the faces of the trailing edges of the block.
This means that when something changes, the caller must This means that when something changes, the caller must
@ -606,9 +622,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
// 4-23ms for MAP_BLOCKSIZE=16 // 4-23ms for MAP_BLOCKSIZE=16
//TimeTaker timer2("updateMesh() collect"); //TimeTaker timer2("updateMesh() collect");
// Lock this, as m_temp_mods will be used directly
JMutexAutoLock lock(m_temp_mods_mutex);
/* /*
Go through every y,z and get top faces in rows of x+ Go through every y,z and get top faces in rows of x+
*/ */
@ -620,7 +633,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
v3f (1,0,0), v3f (1,0,0),
v3s16(0,1,0), //face dir v3s16(0,1,0), //face dir
v3f (0,1,0), v3f (0,1,0),
fastfaces_new); fastfaces_new,
temp_mods);
} }
} }
/* /*
@ -634,7 +648,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
v3f (0,0,1), v3f (0,0,1),
v3s16(1,0,0), v3s16(1,0,0),
v3f (1,0,0), v3f (1,0,0),
fastfaces_new); fastfaces_new,
temp_mods);
} }
} }
/* /*
@ -648,7 +663,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
v3f (1,0,0), v3f (1,0,0),
v3s16(0,0,1), v3s16(0,0,1),
v3f (0,0,1), v3f (0,0,1),
fastfaces_new); fastfaces_new,
temp_mods);
} }
} }
} }
@ -1297,10 +1313,6 @@ void MapBlock::copyTo(VoxelManipulator &dst)
getPosRelative(), data_size); getPosRelative(), data_size);
} }
/*void getPseudoObjects(v3f origin, f32 max_d,
core::array<DistanceSortedObject> &dest)
{
}*/
void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio) void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
{ {
/* /*
@ -1501,6 +1513,8 @@ void MapBlock::serialize(std::ostream &os, u8 version)
flags |= 1; flags |= 1;
if(m_day_night_differs) if(m_day_night_differs)
flags |= 2; flags |= 2;
if(m_lighting_expired)
flags |= 3;
os.write((char*)&flags, 1); os.write((char*)&flags, 1);
u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
@ -1622,6 +1636,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
is.read((char*)&flags, 1); is.read((char*)&flags, 1);
is_underground = (flags & 1) ? true : false; is_underground = (flags & 1) ? true : false;
m_day_night_differs = (flags & 2) ? true : false; m_day_night_differs = (flags & 2) ? true : false;
m_lighting_expired = (flags & 3) ? true : false;
// Uncompress data // Uncompress data
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);

@ -70,6 +70,74 @@ struct NodeMod
u16 param; u16 param;
}; };
class NodeModMap
{
public:
/*
returns true if the mod was different last time
*/
bool set(v3s16 p, const NodeMod &mod)
{
// See if old is different, cancel if it is not different.
core::map<v3s16, NodeMod>::Node *n = m_mods.find(p);
if(n)
{
NodeMod old = n->getValue();
if(old == mod)
return false;
n->setValue(mod);
}
else
{
m_mods.insert(p, mod);
}
return true;
}
// Returns true if there was one
bool get(v3s16 p, NodeMod *mod)
{
core::map<v3s16, NodeMod>::Node *n;
n = m_mods.find(p);
if(n == NULL)
return false;
if(mod)
*mod = n->getValue();
return true;
}
bool clear(v3s16 p)
{
if(m_mods.find(p))
{
m_mods.remove(p);
return true;
}
return false;
}
bool clear()
{
if(m_mods.size() == 0)
return false;
m_mods.clear();
return true;
}
void copy(NodeModMap &dest)
{
dest.m_mods.clear();
for(core::map<v3s16, NodeMod>::Iterator
i = m_mods.getIterator();
i.atEnd() == false; i++)
{
dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue());
}
}
private:
core::map<v3s16, NodeMod> m_mods;
};
enum enum
{ {
NODECONTAINER_ID_MAPBLOCK, NODECONTAINER_ID_MAPBLOCK,
@ -104,11 +172,26 @@ public:
return m_parent; return m_parent;
} }
void reallocate()
{
if(data != NULL)
delete[] data;
u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
data = new MapNode[l];
for(u32 i=0; i<l; i++){
data[i] = MapNode();
}
setChangedFlag();
}
/*
Flags
*/
bool isDummy() bool isDummy()
{ {
return (data == NULL); return (data == NULL);
} }
void unDummify() void unDummify()
{ {
assert(isDummy()); assert(isDummy());
@ -119,36 +202,14 @@ public:
{ {
return changed; return changed;
} }
void resetChangedFlag() void resetChangedFlag()
{ {
changed = false; changed = false;
} }
void setChangedFlag() void setChangedFlag()
{ {
changed = true; changed = true;
} }
#ifndef SERVER
void setMeshExpired(bool expired)
{
m_mesh_expired = expired;
}
bool getMeshExpired()
{
return m_mesh_expired;
}
#endif
v3s16 getPos()
{
return m_pos;
}
v3s16 getPosRelative()
{
return m_pos * MAP_BLOCKSIZE;
}
bool getIsUnderground() bool getIsUnderground()
{ {
@ -161,6 +222,51 @@ public:
setChangedFlag(); setChangedFlag();
} }
#ifndef SERVER
void setMeshExpired(bool expired)
{
m_mesh_expired = expired;
}
bool getMeshExpired()
{
return m_mesh_expired;
}
#endif
void setLightingExpired(bool expired)
{
m_lighting_expired = expired;
setChangedFlag();
}
bool getLightingExpired()
{
return m_lighting_expired;
}
bool isValid()
{
if(m_lighting_expired)
return false;
if(data == NULL)
return false;
return true;
}
/*
Position stuff
*/
v3s16 getPos()
{
return m_pos;
}
v3s16 getPosRelative()
{
return m_pos * MAP_BLOCKSIZE;
}
core::aabbox3d<s16> getBox() core::aabbox3d<s16> getBox()
{ {
return core::aabbox3d<s16>(getPosRelative(), return core::aabbox3d<s16>(getPosRelative(),
@ -169,17 +275,9 @@ public:
- v3s16(1,1,1)); - v3s16(1,1,1));
} }
void reallocate() /*
{ Regular MapNode get-setters
if(data != NULL) */
delete[] data;
u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
data = new MapNode[l];
for(u32 i=0; i<l; i++){
data[i] = MapNode();
}
setChangedFlag();
}
bool isValidPosition(v3s16 p) bool isValidPosition(v3s16 p)
{ {
@ -190,10 +288,6 @@ public:
&& p.Z >= 0 && p.Z < MAP_BLOCKSIZE); && p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
} }
/*
Regular MapNode get-setters
*/
MapNode getNode(s16 x, s16 y, s16 z) MapNode getNode(s16 x, s16 y, s16 z)
{ {
if(data == NULL) if(data == NULL)
@ -271,9 +365,14 @@ public:
setNode(x0+x, y0+y, z0+z, node); setNode(x0+x, y0+y, z0+z, node);
} }
/*
Graphics-related methods
*/
// A quick version with nodes passed as parameters
u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
v3s16 face_dir); v3s16 face_dir);
// A more convenient version
u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir) u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir)
{ {
return getFaceLight(daynight_ratio, return getFaceLight(daynight_ratio,
@ -288,11 +387,16 @@ public:
v3s16 dir, v3f scale, v3f posRelative_f, v3s16 dir, v3f scale, v3f posRelative_f,
core::array<FastFace> &dest); core::array<FastFace> &dest);
TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir); TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
u8 getNodeContent(v3s16 p, MapNode mn); NodeModMap &temp_mods);
u8 getNodeContent(v3s16 p, MapNode mn,
NodeModMap &temp_mods);
/* /*
startpos: Generates the FastFaces of a node row. This has a
ridiculous amount of parameters because that way they
can be precalculated by the caller.
translate_dir: unit vector with only one of x, y or z translate_dir: unit vector with only one of x, y or z
face_dir: unit vector with only one of x, y or z face_dir: unit vector with only one of x, y or z
*/ */
@ -305,12 +409,14 @@ public:
v3f translate_dir_f, v3f translate_dir_f,
v3s16 face_dir, v3s16 face_dir,
v3f face_dir_f, v3f face_dir_f,
core::array<FastFace> &dest); core::array<FastFace> &dest,
NodeModMap &temp_mods);
/*
Thread-safely updates the whole mesh of the mapblock.
*/
void updateMesh(u32 daynight_ratio); void updateMesh(u32 daynight_ratio);
/*void updateMesh(s32 daynight_i);
// Updates all DAYNIGHT_CACHE_COUNT meshes
void updateMeshes(s32 first_i=0);*/
#endif // !SERVER #endif // !SERVER
// See comments in mapblock.cpp // See comments in mapblock.cpp
@ -322,7 +428,7 @@ public:
void copyTo(VoxelManipulator &dst); void copyTo(VoxelManipulator &dst);
/* /*
Object stuff MapBlockObject stuff
*/ */
void serializeObjects(std::ostream &os, u8 version) void serializeObjects(std::ostream &os, u8 version)
@ -384,9 +490,6 @@ public:
m_objects.getObjects(origin, max_d, dest); m_objects.getObjects(origin, max_d, dest);
} }
/*void getPseudoObjects(v3f origin, f32 max_d,
core::array<DistanceSortedObject> &dest);*/
s32 getObjectCount() s32 getObjectCount()
{ {
return m_objects.getCount(); return m_objects.getCount();
@ -399,7 +502,7 @@ public:
returns true if the mod was different last time returns true if the mod was different last time
*/ */
bool setTempMod(v3s16 p, NodeMod mod) bool setTempMod(v3s16 p, const NodeMod &mod)
{ {
/*dstream<<"setTempMod called on block" /*dstream<<"setTempMod called on block"
<<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")" <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
@ -408,52 +511,33 @@ public:
<<std::endl;*/ <<std::endl;*/
JMutexAutoLock lock(m_temp_mods_mutex); JMutexAutoLock lock(m_temp_mods_mutex);
// See if old is different, cancel if it is not different. return m_temp_mods.set(p, mod);
core::map<v3s16, NodeMod>::Node *n = m_temp_mods.find(p);
if(n)
{
NodeMod old = n->getValue();
if(old == mod)
return false;
}
m_temp_mods[p] = mod;
return true;
} }
// Returns true if there was one // Returns true if there was one
bool getTempMod(v3s16 p, struct NodeMod *mod) bool getTempMod(v3s16 p, NodeMod *mod)
{ {
JMutexAutoLock lock(m_temp_mods_mutex); JMutexAutoLock lock(m_temp_mods_mutex);
core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p); return m_temp_mods.get(p, mod);
if(n == NULL)
return false;
if(mod)
*mod = n->getValue();
return true;
} }
bool clearTempMod(v3s16 p) bool clearTempMod(v3s16 p)
{ {
JMutexAutoLock lock(m_temp_mods_mutex); JMutexAutoLock lock(m_temp_mods_mutex);
if(m_temp_mods.find(p))
{ return m_temp_mods.clear(p);
m_temp_mods.remove(p);
return true;
}
return false;
} }
bool clearTempMods() bool clearTempMods()
{ {
JMutexAutoLock lock(m_temp_mods_mutex); JMutexAutoLock lock(m_temp_mods_mutex);
if(m_temp_mods.size() == 0)
return false; return m_temp_mods.clear();
m_temp_mods.clear();
return true;
} }
#endif #endif
/* /*
Day-night lighting difference Update day-night lighting difference flag.
Sets m_day_night_differs to appropriate value.
These methods don't care about neighboring blocks. These methods don't care about neighboring blocks.
It means that to know if a block really doesn't need a mesh It means that to know if a block really doesn't need a mesh
@ -490,17 +574,10 @@ public:
void deSerialize(std::istream &is, u8 version); void deSerialize(std::istream &is, u8 version);
/*
Public member variables
*/
#ifndef SERVER
//scene::SMesh *mesh[DAYNIGHT_CACHE_COUNT];
scene::SMesh *mesh;
JMutex mesh_mutex;
#endif
private: private:
/*
Private methods
*/
/* /*
Used only internally, because changes can't be tracked Used only internally, because changes can't be tracked
@ -520,29 +597,59 @@ private:
return getNodeRef(p.X, p.Y, p.Z); return getNodeRef(p.X, p.Y, p.Z);
} }
public:
/*
Public member variables
*/
#ifndef SERVER
scene::SMesh *mesh;
JMutex mesh_mutex;
#endif
private:
/*
Private member variables
*/
// Parent container (practically the Map)
// Not a MapSector, it is just a structural element.
NodeContainer *m_parent; NodeContainer *m_parent;
// Position in blocks on parent // Position in blocks on parent
v3s16 m_pos; v3s16 m_pos;
/* /*
If NULL, block is a dummy block. If NULL, block is a dummy block.
Dummy blocks are used for caching not-found-on-disk blocks. Dummy blocks are used for caching not-found-on-disk blocks.
*/ */
MapNode * data; MapNode * data;
/* /*
- On the client, this is used for checking whether to
recalculate the face cache. (Is it anymore?)
- On the server, this is used for telling whether the - On the server, this is used for telling whether the
block has been changed from the one on disk. block has been changed from the one on disk.
- On the client, this is used for nothing.
*/ */
bool changed; bool changed;
/* /*
Used for some initial lighting stuff. When propagating sunlight and the above block doesn't exist,
At least /has been/ used. 8) sunlight is assumed if this is false.
It's probably useless now.
In practice this is set to true if the block is completely
undeground with nothing visible above the ground except
caves.
*/ */
bool is_underground; bool is_underground;
/*
Set to true if changes has been made that make the old lighting
values wrong but the lighting hasn't been actually updated.
If this is false, lighting is exactly right.
If this is true, lighting might be wrong or right.
*/
bool m_lighting_expired;
// Whether day and night lighting differs // Whether day and night lighting differs
bool m_day_night_differs; bool m_day_night_differs;
@ -552,11 +659,16 @@ private:
float m_spawn_timer; float m_spawn_timer;
#ifndef SERVER #ifndef SERVER
/*
Set to true if the mesh has been ordered to be updated
sometime in the background.
In practice this is set when the day/night lighting switches.
*/
bool m_mesh_expired; bool m_mesh_expired;
// Temporary modifications to nodes // Temporary modifications to nodes
// These are only used when drawing // These are only used when drawing
core::map<v3s16, NodeMod> m_temp_mods; NodeModMap m_temp_mods;
JMutex m_temp_mods_mutex; JMutex m_temp_mods_mutex;
#endif #endif
}; };

51
src/mapchunk.h Normal file

@ -0,0 +1,51 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MAPCHUNK_HEADER
#define MAPCHUNK_HEADER
/*
MapChunk contains map-generation-time metadata for an area of
some MapSectors. (something like 64x64)
*/
class MapChunk
{
public:
MapChunk():
m_is_volatile(true)
{
}
/*
If is_volatile is true, chunk can be modified when
neighboring chunks are generated.
It is set to false when all the 8 neighboring chunks have
been generated.
*/
bool getIsVolatile(){ return m_is_volatile; }
void setIsVolatile(bool is){ m_is_volatile = is; }
private:
bool m_is_volatile;
};
#endif

@ -82,11 +82,16 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
return block; return block;
} }
MapBlock * MapSector::getBlockNoCreate(s16 y) MapBlock * MapSector::getBlockNoCreateNoEx(s16 y)
{ {
JMutexAutoLock lock(m_mutex); JMutexAutoLock lock(m_mutex);
MapBlock *block = getBlockBuffered(y); return getBlockBuffered(y);
}
MapBlock * MapSector::getBlockNoCreate(s16 y)
{
MapBlock *block = getBlockNoCreateNoEx(y);
if(block == NULL) if(block == NULL)
throw InvalidPositionException(); throw InvalidPositionException();

@ -67,6 +67,7 @@ public:
return m_pos; return m_pos;
} }
MapBlock * getBlockNoCreateNoEx(s16 y);
MapBlock * getBlockNoCreate(s16 y); MapBlock * getBlockNoCreate(s16 y);
MapBlock * createBlankBlockNoInsert(s16 y); MapBlock * createBlankBlockNoInsert(s16 y);
MapBlock * createBlankBlock(s16 y); MapBlock * createBlankBlock(s16 y);

@ -156,20 +156,18 @@ void * EmergeThread::Thread()
only_from_disk = true; only_from_disk = true;
// First check if the block already exists // First check if the block already exists
if(only_from_disk) //block = map.getBlockNoCreate(p);
{
block = map.getBlockNoCreate(p);
}
if(block == NULL) if(block == NULL)
{ {
//dstream<<"Calling emergeBlock"<<std::endl;
block = map.emergeBlock( block = map.emergeBlock(
p, p,
only_from_disk, only_from_disk,
changed_blocks, changed_blocks,
lighting_invalidated_blocks); lighting_invalidated_blocks);
#if 1 #if 0
/* /*
EXPERIMENTAL: Create a few other blocks too EXPERIMENTAL: Create a few other blocks too
*/ */
@ -206,6 +204,12 @@ void * EmergeThread::Thread()
{ {
//dstream<<"EmergeThread: Got a dummy block"<<std::endl; //dstream<<"EmergeThread: Got a dummy block"<<std::endl;
got_block = false; got_block = false;
if(only_from_disk == false)
{
dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
assert(0);
}
} }
} }
catch(InvalidPositionException &e) catch(InvalidPositionException &e)
@ -581,16 +585,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
/* /*
Check if map has this block Check if map has this block
*/ */
MapBlock *block = NULL; MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
try
{
block = server->m_env.getMap().getBlockNoCreate(p);
}
catch(InvalidPositionException &e)
{
}
bool surely_not_found_on_disk = false; bool surely_not_found_on_disk = false;
bool block_is_invalid = false;
if(block != NULL) if(block != NULL)
{ {
/*if(block->isIncomplete()) /*if(block->isIncomplete())
@ -603,6 +601,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
{ {
surely_not_found_on_disk = true; surely_not_found_on_disk = true;
} }
if(block->isValid() == false)
{
block_is_invalid = true;
}
} }
/* /*
@ -627,8 +630,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
/* /*
Add inexistent block to emerge queue. Add inexistent block to emerge queue.
*/ */
if(block == NULL || surely_not_found_on_disk) if(block == NULL || surely_not_found_on_disk || block_is_invalid)
{ {
//dstream<<"asd"<<std::endl;
/*SharedPtr<JMutexAutoLock> lock /*SharedPtr<JMutexAutoLock> lock
(m_num_blocks_in_emerge_queue.getLock());*/ (m_num_blocks_in_emerge_queue.getLock());*/
@ -636,6 +641,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
// Allow only one block in emerge queue // Allow only one block in emerge queue
if(server->m_emerge_queue.peerItemCount(peer_id) < 1) if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
{ {
//dstream<<"Adding block to emerge queue"<<std::endl;
// Add it to the emerge queue and trigger the thread // Add it to the emerge queue and trigger the thread
u8 flags = 0; u8 flags = 0;

@ -538,6 +538,23 @@ inline v3s16 getContainerPos(v3s16 p, s16 d)
); );
} }
inline v2s16 getContainerPos(v2s16 p, v2s16 d)
{
return v2s16(
getContainerPos(p.X, d.X),
getContainerPos(p.Y, d.Y)
);
}
inline v3s16 getContainerPos(v3s16 p, v3s16 d)
{
return v3s16(
getContainerPos(p.X, d.X),
getContainerPos(p.Y, d.Y),
getContainerPos(p.Z, d.Z)
);
}
inline bool isInArea(v3s16 p, s16 d) inline bool isInArea(v3s16 p, s16 d)
{ {
return ( return (

@ -536,10 +536,13 @@ void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p)
} }
} }
#if 1 #if 0
/* /*
Lights neighbors of from_nodes, collects all them and then Lights neighbors of from_nodes, collects all them and then
goes on recursively. goes on recursively.
NOTE: This is faster in small areas but will overflow the
stack on large areas. Thus it is not used.
*/ */
void VoxelManipulator::spreadLight(enum LightBank bank, void VoxelManipulator::spreadLight(enum LightBank bank,
core::map<v3s16, bool> & from_nodes) core::map<v3s16, bool> & from_nodes)
@ -560,7 +563,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank,
} }
#endif #endif
#if 0 #if 1
/* /*
Lights neighbors of from_nodes, collects all them and then Lights neighbors of from_nodes, collects all them and then
goes on recursively. goes on recursively.