// is very low
#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 SIGN_TEXT_MAX_LENGTH 50
// Whether to catch all std::exceptions.
// Assert will be called on such an event.
-#ifdef DEBUG
- #define CATCH_UNHANDLED_EXCEPTIONS 0
-#else
+// In debug mode, leave these for the debugger and don't catch them.
+#ifdef NDEBUG
#define CATCH_UNHANDLED_EXCEPTIONS 1
+#else
+ #define CATCH_UNHANDLED_EXCEPTIONS 0
#endif
/*
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_server_total", "4");
- g_settings.setDefault("max_block_send_distance", "6");
- g_settings.setDefault("max_block_generate_distance", "6");
+ g_settings.setDefault("max_block_send_distance", "7");
+ g_settings.setDefault("max_block_generate_distance", "7");
g_settings.setDefault("time_send_interval", "20");
g_settings.setDefault("time_speed", "96");
g_settings.setDefault("server_unload_unused_sectors_timeout", "60");
\r
TODO: Flowing water animation\r
\r
-FIXME: The new texture stuff is slow on wine\r
+FIXME(FIXED): The new texture stuff is slow on wine\r
- A basic grassy ground block takes 20-40ms\r
- A bit more complicated block can take 270ms\r
- On linux, a similar one doesn't take long at all (14ms)\r
is fast to compare, which refers to a cached string, or\r
* Make TextureSpec for using instead of strings\r
\r
+FIXME(FIXED): A lock condition is possible:\r
+ 1) MapBlock::updateMesh() is called from client asynchronously:\r
+ - AsyncProcessData() -> Map::updateMeshes()\r
+ 2) Asynchronous locks m_temp_mods_mutex\r
+ 3) MapBlock::updateMesh() is called from client synchronously:\r
+ - Client::step() -> Environment::step()\r
+ 4) Synchronous starts waiting for m_temp_mods_mutex\r
+ 5) Asynchronous calls getTexture, which starts waiting for main thread\r
+\r
Configuration:\r
--------------\r
\r
NOTE: There are some lighting-related todos and fixmes in\r
ServerMap::emergeBlock. And there always will be. 8)\r
\r
+TODO: Mineral and ground material properties\r
+ - This way mineral ground toughness can be calculated with just\r
+ some formula, as well as tool strengths\r
+\r
+TODO: Change AttributeList to split the area into smaller sections so\r
+ that searching won't be as heavy.\r
+\r
+TODO: Remove HMParams\r
+\r
+TODO: Flowing water to actually contain flow direction information\r
+\r
+TODO: Remove duplicate lighting implementation from Map (leave\r
+ VoxelManipulator, which is faster)\r
+\r
FEATURE: Map generator version 2\r
- Create surface areas based on central points; a given point's\r
area type is given by the nearest central point\r
FEATURE: The map could be generated procedually:\r
- This would need the map to be generated in larger pieces\r
- How large? How do they connect to each other?\r
+ - It has to be split vertically also\r
+ - Lighting would not have to be necessarily calculated until\r
+ the blocks are actually needed - it would be quite fast\r
+ - Something like 64*64*16 MapBlocks?\r
+ - TODO: Separate lighting and block generation\r
* Make the stone level with a heightmap\r
* Carve out stuff in the stone\r
* Dump dirt all around, and simulate it falling off steep\r
parameter field is free for this.\r
- Simulate rock falling from cliffs when water has removed\r
enough solid rock from the bottom\r
-\r
-TODO: Mineral and ground material properties\r
- - This way mineral ground toughness can be calculated with just\r
- some formula, as well as tool strengths\r
-\r
-TODO: Change AttributeList to split the area into smaller sections so\r
- that searching won't be as heavy.\r
-\r
-TODO: Remove HMParams\r
-\r
-TODO: Flowing water to actually contain flow direction information\r
-\r
-TODO: Remove duplicate lighting implementation from Map (leave\r
- VoxelManipulator, which is faster)\r
+TODO: Lazy lighting updates:\r
+ - Set updateLighting to ignore MapBlocks with expired lighting,\r
+ except the blocks specified to it\r
+ - When a MapBlock is generated, lighting expires in all blocks\r
+ touching it (26 blocks + self)\r
+ - When a lighting-wise valid MapBlock is needed and lighting of it\r
+ has expired, what to do?\r
\r
Doing now:\r
----------\r
srand(time(0));\r
mysrand(time(0));\r
\r
+ /*\r
+ Pre-initialize some stuff with a dummy irrlicht wrapper.\r
+\r
+ These are needed for unit tests at least.\r
+ */\r
+ \r
+ IIrrlichtWrapper irrlicht_dummy;\r
+\r
+ init_mapnode(&irrlicht_dummy);\r
+\r
/*\r
Run unit tests\r
*/\r
skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));\r
\r
/*\r
- Preload some textures\r
+ Preload some textures and stuff\r
*/\r
\r
init_content_inventory_texture_paths();\r
dtime_jitter1_max_fraction\r
= dtime_jitter1_max_sample / (dtime_avg1+0.001);\r
jitter1_max = 0.0;\r
- \r
- /*\r
- Control freetime ratio\r
- */\r
- /*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)\r
- {\r
- if(g_freetime_ratio < FREETIME_RATIO_MAX)\r
- g_freetime_ratio += 0.01;\r
- }\r
- else\r
- {\r
- if(g_freetime_ratio > FREETIME_RATIO_MIN)\r
- g_freetime_ratio -= 0.01;\r
- }*/\r
}\r
}\r
\r
}
}
-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){
MapSector * sector = m_sector_cache;
// Reset inactivity timer
}
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
- // If sector doesn't exist, throw an exception
+
if(n == NULL)
- {
- throw InvalidPositionException();
- }
+ return NULL;
MapSector *sector = n->getValue();
m_sector_cache_p = p;
m_sector_cache = sector;
- //MapSector * ref(sector);
-
// Reset inactivity timer
sector->usage_timer = 0.0;
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)
{
v2s16 p2d(p3d.X, p3d.Z);
// For debugging
//bool debug=true;
//u32 count_was = modified_blocks.size();
+
+ core::map<v3s16, MapBlock*> blocks_to_update;
core::map<v3s16, bool> light_sources;
v3s16 pos = block->getPos();
modified_blocks.insert(pos, block);
+ blocks_to_update.insert(pos, block);
+
/*
Clear all light from block
*/
}
else if(bank == LIGHTBANK_NIGHT)
{
+ // For night lighting, sunlight is not propagated
break;
}
else
{
+ // Invalid lighting bank
assert(0);
}
<<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
<<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--;
try{
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");
spreadLight(bank, light_sources, modified_blocks);
{
//MapVoxelManipulator vmanip(this);
-
- ManualMapVoxelManipulator vmanip(this);
+ // Make a manual voxel manipulator and load all the blocks
+ // that touch the requested blocks
+ ManualMapVoxelManipulator vmanip(this);
core::map<v3s16, MapBlock*>::Iterator i;
- i = a_blocks.getIterator();
+ i = blocks_to_update.getIterator();
for(; i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
v3s16 p = block->getPos();
+
+ // Add all surrounding blocks
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");
vmanip.unspreadLight(bank, unlight_from, light_sources);
u32 loopcount = 0;
u32 initial_size = m_transforming_liquid.size();
+ //dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
+
while(m_transforming_liquid.size() != 0)
{
/*
Map(dout_server),
m_heightmap(NULL)
{
+
+ //m_chunksize = 64;
+ //m_chunksize = 16;
+ //m_chunksize = 8;
+ m_chunksize = 2;
+
/*
Experimental and debug stuff
*/
list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
}
-
+
/*
Try to load map; if not found, create a new one.
*/
dstream<<DTIME<<"Initializing new map."<<std::endl;
// 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
(32, &m_padb);
if(m_heightmap != NULL)
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)
{
- DSTACK("%s: p2d=(%d,%d)",
- __FUNCTION_NAME,
- p2d.X, p2d.Y);
- // Check that it doesn't exist already
- try{
- return getSectorNoGenerate(p2d);
+ // 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++;
+ }
+ }
}
- catch(InvalidPositionException &e)
+
+ 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)
+{
/*
- Try to load the sector from disk.
+ Generate chunk and neighbors
*/
- if(loadSectorFull(p2d) == true)
+ for(s16 x=-1; x<=1; x++)
+ for(s16 y=-1; y<=1; y++)
{
- return getSectorNoGenerate(p2d);
+ 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)",
+ __FUNCTION_NAME,
+ p2d.X, p2d.Y);
+
+ // Check that it doesn't exist already
+ ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
+ if(sector != NULL)
+ return sector;
+
/*
If there is no master heightmap, throw.
*/
// Heightmap side width
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
v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
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;
//TimeTaker attrtimer("emergeSector() attribute fetch");
local_plants_amount =
palist->getInterpolatedFloat(nodepos2d);
}
+#endif
/*
Generate sector heightmap
/*
Insert to container
*/
- JMutexAutoLock lock(m_sector_mutex);
m_sectors.insert(p2d, sector);
return sector;
}
-MapBlock * ServerMap::emergeBlock(
- v3s16 p,
- bool only_from_disk,
- core::map<v3s16, MapBlock*> &changed_blocks,
- core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
-)
+MapSector * ServerMap::emergeSector(v2s16 p2d)
{
- DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
+ DSTACK("%s: p2d=(%d,%d)",
__FUNCTION_NAME,
- p.X, p.Y, p.Z, only_from_disk);
-
- /*dstream<<"ServerMap::emergeBlock(): "
- <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
- <<", only_from_disk="<<only_from_disk<<std::endl;*/
- v2s16 p2d(p.X, p.Z);
- s16 block_y = p.Y;
+ p2d.X, p2d.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.
+ Check if it exists already in memory
*/
- 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;
- }
+ MapSector *sector = getSectorNoGenerateNoEx(p2d);
+ if(sector != NULL)
+ return sector;
/*
- If block was not found on disk and not going to generate a
- new one, make sure there is a dummy block in place.
+ Try to load it from disk
*/
- if(not_on_disk && only_from_disk)
+ if(loadSectorFull(p2d) == true)
{
- if(block == NULL)
+ MapSector *sector = getSectorNoGenerateNoEx(p2d);
+ if(sector == NULL)
{
- // Create dummy block
- block = new MapBlock(this, p, true);
-
- // Add block to sector
- sector->insertBlock(block);
+ dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
+ throw InvalidPositionException("");
}
- // Done.
- return block;
+ return sector;
}
- //dstream<<"Not found on disk, generating."<<std::endl;
- // 0ms
- //TimeTaker("emergeBlock() generate");
-
/*
- Do not generate over-limit
+ Check chunk status
*/
- if(blockpos_over_limit(p))
- throw InvalidPositionException("emergeBlock(): pos. over limit");
+ v2s16 chunkpos = sector_to_chunk(p2d);
+ bool chunk_exists = false;
+ MapChunk *chunk = getChunk(chunkpos);
+ if(chunk && chunk->getIsVolatile() == false)
+ chunk_exists = true;
/*
- OK; Not found.
-
- Go on generating the block.
+ 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);
- TODO: If a dungeon gets generated so that it's side gets
- revealed to the outside air, the lighting should be
- recalculated.
+ /*
+ Generate directly
*/
+ //return generateSector();
+}
+
+MapBlock * ServerMap::generateBlock(
+ v3s16 p,
+ MapBlock *original_dummy,
+ ServerMapSector *sector,
+ core::map<v3s16, MapBlock*> &changed_blocks,
+ core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
+)
+{
+ DSTACK("%s: p=(%d,%d,%d)",
+ __FUNCTION_NAME,
+ p.X, p.Y, p.Z);
+
+ /*dstream<<"generateBlock(): "
+ <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+ <<std::endl;*/
+
+ MapBlock *block = original_dummy;
+
+ v2s16 p2d(p.X, p.Z);
+ s16 block_y = p.Y;
+ /*
+ Do not generate over-limit
+ */
+ if(blockpos_over_limit(p))
+ {
+ dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
+ throw InvalidPositionException("generateBlock(): pos. over limit");
+ }
+
/*
If block doesn't exist, create one.
If it exists, it is a dummy. In that case unDummify() it.
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
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));
//assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
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
down by a lot.
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;
PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
}
+#endif
- //dstream<<"emergeBlock(): Done"<<std::endl;
+ //dstream<<"generateBlock(): Done"<<std::endl;
/*
Generate dungeons
do_generate_dungeons = false;
}
// Don't generate if mostly underwater surface
- else if(mostly_underwater_surface)
+ /*else if(mostly_underwater_surface)
{
do_generate_dungeons = false;
- }
+ }*/
// Partly underground = cave
else if(!completely_underground)
{
/*
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);
}
else
{
- dstream<<"ServerMap::emergeBlock(): "
+ dstream<<"ServerMap::generateBlock(): "
"Invalid heightmap object"
<<std::endl;
}
{
objects->remove(*i);
}
+
+ /*
+ Translate sector's changed blocks to global changed blocks
+ */
+
+ for(core::map<s16, MapBlock*>::Iterator
+ i = changed_blocks_sector.getIterator();
+ i.atEnd() == false; i++)
+ {
+ MapBlock *block = i.getNode()->getValue();
+
+ changed_blocks.insert(block->getPos(), block);
+ }
+
+ block->setLightingExpired(true);
+
+#if 0
+ /*
+ Debug information
+ */
+ 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<<"emergeBlock: emergeSector() failed"<<std::endl;
+ throw e;
+ }*/
+ catch(std::exception &e)
+ {
+ dstream<<"emergeBlock: emergeSector() failed: "
+ <<e.what()<<std::endl;
+ throw e;
+ }
+
+ /*
+ 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
// 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 && some_part_underground)
+ if(black_air_left)
{
lighting_invalidated_blocks[block->getPos()] = block;
}
lighting_invalidated_blocks[block->getPos()] = block;
}
}
-
- /*
- Translate sector's changed blocks to global changed blocks
- */
- for(core::map<s16, MapBlock*>::Iterator
- i = changed_blocks_sector.getIterator();
- i.atEnd() == false; i++)
- {
- MapBlock *block = i.getNode()->getValue();
-
- changed_blocks.insert(block->getPos(), block);
- }
-
- /*
- 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;
- }
-
/*
Debug mode operation
*/
#include "mapsector.h"
#include "constants.h"
#include "voxel.h"
+#include "mapchunk.h"
#define MAPTYPE_BASE 0
#define MAPTYPE_SERVER 1
(float)p.Z * BS + 0.5*BS
);
}
-
- //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);
+
/*
This is overloaded by ClientMap and ServerMap to allow
their differing fetch methods.
}
/*
- 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);
+
+ 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.
core::map<v3s16, MapBlock*> &changed_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 createSaveDir();
// returns something like "xxxxxxxx"
std::string m_savedir;
bool m_map_saving_enabled;
+
+ // Chunk size in MapSectors
+ s16 m_chunksize;
+ // Chunks
+ core::map<v2s16, MapChunk*> m_chunks;
};
+/*
+ ClientMap stuff
+*/
+
#ifndef SERVER
struct MapDrawControl
Gets node tile from any place relative to block.
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;
spec = mn.getTile(face_dir);
/*
Check temporary modifications on this node
*/
- core::map<v3s16, NodeMod>::Node *n;
+ /*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
-
// If modified
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)
{
MapNode mn2(mod.param);
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
*/
- core::map<v3s16, NodeMod>::Node *n;
+ /*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
-
// If modified
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)
{
// Overrides content
v3f translate_dir_f,
v3s16 face_dir,
v3f face_dir_f,
- core::array<FastFace> &dest)
+ core::array<FastFace> &dest,
+ NodeModMap &temp_mods)
{
v3s16 p = startpos;
u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
- TileSpec tile0 = getNodeTile(n0, p, face_dir);
- TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
+ TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
+ TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
for(u16 j=0; j<length; j++)
{
p_next = p + translate_dir;
n0_next = getNodeParentNoEx(p_next);
n1_next = getNodeParentNoEx(p_next + face_dir);
- tile0_next = getNodeTile(n0_next, p_next, face_dir);
- tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
+ tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
+ tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
if(tile0_next == tile0
*/
//u8 mf = face_contents(tile0, tile1);
// This is hackish
- u8 content0 = getNodeContent(p, n0);
- u8 content1 = getNodeContent(p + face_dir, n1);
+ u8 content0 = getNodeContent(p, n0, temp_mods);
+ u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
u8 mf = face_contents(content0, content1);
if(mf != 0)
v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
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.
This means that when something changes, the caller must
// 4-23ms for MAP_BLOCKSIZE=16
//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+
*/
v3f (1,0,0),
v3s16(0,1,0), //face dir
v3f (0,1,0),
- fastfaces_new);
+ fastfaces_new,
+ temp_mods);
}
}
/*
v3f (0,0,1),
v3s16(1,0,0),
v3f (1,0,0),
- fastfaces_new);
+ fastfaces_new,
+ temp_mods);
}
}
/*
v3f (1,0,0),
v3s16(0,0,1),
v3f (0,0,1),
- fastfaces_new);
+ fastfaces_new,
+ temp_mods);
}
}
}
getPosRelative(), data_size);
}
-/*void getPseudoObjects(v3f origin, f32 max_d,
- core::array<DistanceSortedObject> &dest)
-{
-}*/
void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
{
/*
flags |= 1;
if(m_day_night_differs)
flags |= 2;
+ if(m_lighting_expired)
+ flags |= 3;
os.write((char*)&flags, 1);
u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
is.read((char*)&flags, 1);
is_underground = (flags & 1) ? true : false;
m_day_night_differs = (flags & 2) ? true : false;
+ m_lighting_expired = (flags & 3) ? true : false;
// Uncompress data
std::ostringstream os(std::ios_base::binary);
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
{
NODECONTAINER_ID_MAPBLOCK,
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()
{
return (data == NULL);
}
-
void unDummify()
{
assert(isDummy());
{
return changed;
}
-
void resetChangedFlag()
{
changed = false;
}
-
void setChangedFlag()
{
changed = true;
}
+
+ bool getIsUnderground()
+ {
+ return is_underground;
+ }
+
+ void setIsUnderground(bool a_is_underground)
+ {
+ is_underground = a_is_underground;
+ setChangedFlag();
+ }
+
#ifndef SERVER
void setMeshExpired(bool expired)
{
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;
return m_pos * MAP_BLOCKSIZE;
}
- bool getIsUnderground()
- {
- return is_underground;
- }
-
- void setIsUnderground(bool a_is_underground)
- {
- is_underground = a_is_underground;
- setChangedFlag();
- }
-
core::aabbox3d<s16> getBox()
{
return core::aabbox3d<s16>(getPosRelative(),
+ v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
- v3s16(1,1,1));
}
-
- 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();
- }
+ /*
+ Regular MapNode get-setters
+ */
+
bool isValidPosition(v3s16 p)
{
if(data == NULL)
&& p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
}
- /*
- Regular MapNode get-setters
- */
-
MapNode getNode(s16 x, s16 y, s16 z)
{
if(data == NULL)
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,
v3s16 face_dir);
-
+ // A more convenient version
u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir)
{
return getFaceLight(daynight_ratio,
v3s16 dir, v3f scale, v3f posRelative_f,
core::array<FastFace> &dest);
- TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir);
- u8 getNodeContent(v3s16 p, MapNode mn);
+ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
+ 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
face_dir: unit vector with only one of x, y or z
*/
v3f translate_dir_f,
v3s16 face_dir,
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(s32 daynight_i);
- // Updates all DAYNIGHT_CACHE_COUNT meshes
- void updateMeshes(s32 first_i=0);*/
+
#endif // !SERVER
// See comments in mapblock.cpp
void copyTo(VoxelManipulator &dst);
/*
- Object stuff
+ MapBlockObject stuff
*/
void serializeObjects(std::ostream &os, u8 version)
m_objects.getObjects(origin, max_d, dest);
}
- /*void getPseudoObjects(v3f origin, f32 max_d,
- core::array<DistanceSortedObject> &dest);*/
-
s32 getObjectCount()
{
return m_objects.getCount();
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"
<<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;*/
JMutexAutoLock lock(m_temp_mods_mutex);
- // See if old is different, cancel if it is not different.
- 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;
+ return m_temp_mods.set(p, mod);
}
// 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);
- core::map<v3s16, NodeMod>::Node *n;
- n = m_temp_mods.find(p);
- if(n == NULL)
- return false;
- if(mod)
- *mod = n->getValue();
- return true;
+
+ return m_temp_mods.get(p, mod);
}
bool clearTempMod(v3s16 p)
{
JMutexAutoLock lock(m_temp_mods_mutex);
- if(m_temp_mods.find(p))
- {
- m_temp_mods.remove(p);
- return true;
- }
- return false;
+
+ return m_temp_mods.clear(p);
}
bool clearTempMods()
{
JMutexAutoLock lock(m_temp_mods_mutex);
- if(m_temp_mods.size() == 0)
- return false;
- m_temp_mods.clear();
- return true;
+
+ return m_temp_mods.clear();
}
#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.
It means that to know if a block really doesn't need a mesh
void deSerialize(std::istream &is, u8 version);
+private:
/*
- Public member variables
+ Private methods
*/
-#ifndef SERVER
- //scene::SMesh *mesh[DAYNIGHT_CACHE_COUNT];
- scene::SMesh *mesh;
- JMutex mesh_mutex;
-#endif
-
-private:
-
/*
Used only internally, because changes can't be tracked
*/
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;
// Position in blocks on parent
v3s16 m_pos;
+
/*
If NULL, block is a dummy block.
Dummy blocks are used for caching not-found-on-disk blocks.
*/
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
block has been changed from the one on disk.
+ - On the client, this is used for nothing.
*/
bool changed;
+
/*
- Used for some initial lighting stuff.
- At least /has been/ used. 8)
- It's probably useless now.
+ When propagating sunlight and the above block doesn't exist,
+ sunlight is assumed if this is false.
+
+ In practice this is set to true if the block is completely
+ undeground with nothing visible above the ground except
+ caves.
*/
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
bool m_day_night_differs;
float m_spawn_timer;
#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;
// Temporary modifications to nodes
// These are only used when drawing
- core::map<v3s16, NodeMod> m_temp_mods;
+ NodeModMap m_temp_mods;
JMutex m_temp_mods_mutex;
#endif
};
--- /dev/null
+/*
+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
+
return block;
}
-MapBlock * MapSector::getBlockNoCreate(s16 y)
+MapBlock * MapSector::getBlockNoCreateNoEx(s16 y)
{
JMutexAutoLock lock(m_mutex);
- MapBlock *block = getBlockBuffered(y);
+ return getBlockBuffered(y);
+}
+
+MapBlock * MapSector::getBlockNoCreate(s16 y)
+{
+ MapBlock *block = getBlockNoCreateNoEx(y);
if(block == NULL)
throw InvalidPositionException();
return m_pos;
}
+ MapBlock * getBlockNoCreateNoEx(s16 y);
MapBlock * getBlockNoCreate(s16 y);
MapBlock * createBlankBlockNoInsert(s16 y);
MapBlock * createBlankBlock(s16 y);
only_from_disk = true;
// First check if the block already exists
- if(only_from_disk)
- {
- block = map.getBlockNoCreate(p);
- }
+ //block = map.getBlockNoCreate(p);
if(block == NULL)
{
+ //dstream<<"Calling emergeBlock"<<std::endl;
block = map.emergeBlock(
p,
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
-#if 1
+#if 0
/*
EXPERIMENTAL: Create a few other blocks too
*/
{
//dstream<<"EmergeThread: Got a dummy block"<<std::endl;
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)
/*
Check if map has this block
*/
- MapBlock *block = NULL;
- try
- {
- block = server->m_env.getMap().getBlockNoCreate(p);
- }
- catch(InvalidPositionException &e)
- {
- }
+ MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
bool surely_not_found_on_disk = false;
+ bool block_is_invalid = false;
if(block != NULL)
{
/*if(block->isIncomplete())
{
surely_not_found_on_disk = true;
}
+
+ if(block->isValid() == false)
+ {
+ block_is_invalid = true;
+ }
}
/*
/*
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
(m_num_blocks_in_emerge_queue.getLock());*/
// Allow only one block in emerge queue
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
u8 flags = 0;
);
}
+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)
{
return (
}
}
-#if 1
+#if 0
/*
Lights neighbors of from_nodes, collects all them and then
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,
core::map<v3s16, bool> & from_nodes)
}
#endif
-#if 0
+#if 1
/*
Lights neighbors of from_nodes, collects all them and then
goes on recursively.