diff options
author | Perttu Ahola <celeron55@gmail.com> | 2011-01-30 01:44:54 +0200 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2011-01-30 01:44:54 +0200 |
commit | be851871cd43316d12fd9a5f2cc6dec98a1c9ce0 (patch) | |
tree | 3f03a82db7c4449bcfebe499ab2843f1fc20b0c4 /src | |
parent | 8788fffec0faf99702e5a84eaadbabc671a023c8 (diff) | |
download | minetest-be851871cd43316d12fd9a5f2cc6dec98a1c9ce0.tar.gz minetest-be851871cd43316d12fd9a5f2cc6dec98a1c9ce0.tar.bz2 minetest-be851871cd43316d12fd9a5f2cc6dec98a1c9ce0.zip |
map generation framework under development... not quite operational at this point.
Diffstat (limited to 'src')
-rw-r--r-- | src/constants.h | 12 | ||||
-rw-r--r-- | src/defaultsettings.cpp | 4 | ||||
-rw-r--r-- | src/main.cpp | 77 | ||||
-rw-r--r-- | src/map.cpp | 564 | ||||
-rw-r--r-- | src/map.h | 107 | ||||
-rw-r--r-- | src/mapblock.cpp | 65 | ||||
-rw-r--r-- | src/mapblock.h | 290 | ||||
-rw-r--r-- | src/mapchunk.h | 51 | ||||
-rw-r--r-- | src/mapsector.cpp | 9 | ||||
-rw-r--r-- | src/mapsector.h | 1 | ||||
-rw-r--r-- | src/server.cpp | 35 | ||||
-rw-r--r-- | src/utility.h | 17 | ||||
-rw-r--r-- | src/voxel.cpp | 7 |
13 files changed, 916 insertions, 323 deletions
diff --git a/src/constants.h b/src/constants.h index f90b278d2..c8c210b13 100644 --- a/src/constants.h +++ b/src/constants.h @@ -76,21 +76,17 @@ with this program; if not, write to the Free Software Foundation, Inc., // 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 /* diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 4046b81b9..0665cd02e 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -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", "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"); diff --git a/src/main.cpp b/src/main.cpp index 0dc822474..dc4716fab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -168,7 +168,7 @@ TODO: Make fetching sector's blocks more efficient when rendering 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 bit more complicated block can take 270ms
- 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
* 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:
--------------
@@ -255,6 +264,20 @@ Map: NOTE: There are some lighting-related todos and fixmes in
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
- Create surface areas based on central points; a given point's
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:
- This would need the map to be generated in larger pieces
- 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
* Carve out stuff in the stone
* 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.
- Simulate rock falling from cliffs when water has removed
enough solid rock from the bottom
-
-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)
+TODO: Lazy lighting updates:
+ - Set updateLighting to ignore MapBlocks with expired lighting,
+ except the blocks specified to it
+ - When a MapBlock is generated, lighting expires in all blocks
+ touching it (26 blocks + self)
+ - When a lighting-wise valid MapBlock is needed and lighting of it
+ has expired, what to do?
Doing now:
----------
@@ -1523,6 +1544,16 @@ int main(int argc, char *argv[]) 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
*/
if((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
@@ -1684,7 +1715,7 @@ int main(int argc, char *argv[]) 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();
@@ -2131,20 +2162,6 @@ int main(int argc, char *argv[]) dtime_jitter1_max_fraction
= dtime_jitter1_max_sample / (dtime_avg1+0.001);
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;
- }*/
}
}
diff --git a/src/map.cpp b/src/map.cpp index 2782cef03..cc1a6d638 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -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){ MapSector * sector = m_sector_cache; // Reset inactivity timer @@ -79,11 +77,9 @@ MapSector * Map::getSectorNoGenerate(v2s16 p) } 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(); @@ -91,13 +87,27 @@ MapSector * Map::getSectorNoGenerate(v2s16 p) 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); @@ -630,6 +640,8 @@ void Map::updateLighting(enum LightBank bank, // For debugging //bool debug=true; //u32 count_was = modified_blocks.size(); + + core::map<v3s16, MapBlock*> blocks_to_update; core::map<v3s16, bool> light_sources; @@ -650,6 +662,8 @@ void Map::updateLighting(enum LightBank bank, v3s16 pos = block->getPos(); modified_blocks.insert(pos, block); + blocks_to_update.insert(pos, block); + /* Clear all light from block */ @@ -699,10 +713,12 @@ void Map::updateLighting(enum LightBank bank, } else if(bank == LIGHTBANK_NIGHT) { + // For night lighting, sunlight is not propagated break; } else { + // Invalid lighting bank assert(0); } @@ -710,7 +726,7 @@ void Map::updateLighting(enum LightBank bank, <<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{ @@ -737,13 +753,6 @@ void Map::updateLighting(enum LightBank bank, 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); @@ -759,17 +768,44 @@ void Map::updateLighting(enum LightBank bank, { //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); @@ -1407,6 +1443,8 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks) u32 loopcount = 0; u32 initial_size = m_transforming_liquid.size(); + //dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl; + while(m_transforming_liquid.size() != 0) { /* @@ -1682,6 +1720,12 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): Map(dout_server), m_heightmap(NULL) { + + //m_chunksize = 64; + //m_chunksize = 16; + //m_chunksize = 8; + m_chunksize = 2; + /* Experimental and debug stuff */ @@ -1862,7 +1906,7 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): 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. */ @@ -1917,18 +1961,6 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): 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); @@ -1966,30 +1998,135 @@ ServerMap::~ServerMap() 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. */ if(m_heightmap == NULL) @@ -2016,7 +2153,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) // 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; @@ -2068,7 +2205,9 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) 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"); @@ -2081,6 +2220,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) local_plants_amount = palist->getInterpolatedFloat(nodepos2d); } +#endif /* Generate sector heightmap @@ -2201,92 +2341,105 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) /* 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. @@ -2318,7 +2471,7 @@ MapBlock * ServerMap::emergeBlock( 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); @@ -2451,23 +2604,25 @@ MapBlock * ServerMap::emergeBlock( 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 @@ -2617,10 +2772,10 @@ continue_generating: 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) { @@ -2723,7 +2878,7 @@ continue_generating: /* 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); @@ -3075,7 +3230,7 @@ continue_generating: } else { - dstream<<"ServerMap::emergeBlock(): " + dstream<<"ServerMap::generateBlock(): " "Invalid heightmap object" <<std::endl; } @@ -3098,6 +3253,148 @@ continue_generating: { 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 @@ -3112,7 +3409,8 @@ continue_generating: // 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; } @@ -3122,37 +3420,7 @@ continue_generating: 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 */ @@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapsector.h" #include "constants.h" #include "voxel.h" +#include "mapchunk.h" #define MAPTYPE_BASE 0 #define MAPTYPE_SERVER 1 @@ -85,9 +86,14 @@ public: (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. @@ -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); + + 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. @@ -346,7 +433,12 @@ public: 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" @@ -396,8 +488,17 @@ private: 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 diff --git a/src/mapblock.cpp b/src/mapblock.cpp index b346b0980..a7bc730ce 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -293,7 +293,8 @@ void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p, 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); @@ -301,13 +302,15 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 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); @@ -326,18 +329,20 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir) 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 @@ -376,7 +381,8 @@ void MapBlock::updateFastFaceRow( 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; @@ -387,8 +393,8 @@ void MapBlock::updateFastFaceRow( 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++) { @@ -406,8 +412,8 @@ void MapBlock::updateFastFaceRow( 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 @@ -427,8 +433,8 @@ void MapBlock::updateFastFaceRow( */ //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) @@ -594,6 +600,16 @@ void MapBlock::updateMesh(u32 daynight_ratio) 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 @@ -606,9 +622,6 @@ void MapBlock::updateMesh(u32 daynight_ratio) // 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+ */ @@ -620,7 +633,8 @@ void MapBlock::updateMesh(u32 daynight_ratio) v3f (1,0,0), v3s16(0,1,0), //face dir v3f (0,1,0), - fastfaces_new); + fastfaces_new, + temp_mods); } } /* @@ -634,7 +648,8 @@ void MapBlock::updateMesh(u32 daynight_ratio) v3f (0,0,1), v3s16(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), v3s16(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); } -/*void getPseudoObjects(v3f origin, f32 max_d, - core::array<DistanceSortedObject> &dest) -{ -}*/ void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio) { /* @@ -1501,6 +1513,8 @@ void MapBlock::serialize(std::ostream &os, u8 version) 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; @@ -1622,6 +1636,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version) 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); diff --git a/src/mapblock.h b/src/mapblock.h index dd5277668..e39db35bd 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -70,6 +70,74 @@ struct NodeMod 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, @@ -104,11 +172,26 @@ public: 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()); @@ -119,16 +202,26 @@ public: { 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) { @@ -140,6 +233,30 @@ public: 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; @@ -150,17 +267,6 @@ public: 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(), @@ -168,19 +274,11 @@ public: + 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) @@ -190,10 +288,6 @@ public: && p.Z >= 0 && p.Z < MAP_BLOCKSIZE); } - /* - Regular MapNode get-setters - */ - MapNode getNode(s16 x, s16 y, s16 z) { if(data == NULL) @@ -271,9 +365,14 @@ public: 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, @@ -288,11 +387,16 @@ public: 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 */ @@ -305,12 +409,14 @@ public: 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 @@ -322,7 +428,7 @@ public: void copyTo(VoxelManipulator &dst); /* - Object stuff + MapBlockObject stuff */ void serializeObjects(std::ostream &os, u8 version) @@ -384,9 +490,6 @@ public: m_objects.getObjects(origin, max_d, dest); } - /*void getPseudoObjects(v3f origin, f32 max_d, - core::array<DistanceSortedObject> &dest);*/ - s32 getObjectCount() { return m_objects.getCount(); @@ -399,7 +502,7 @@ public: 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<<")" @@ -408,52 +511,33 @@ public: <<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 @@ -490,18 +574,11 @@ public: 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 */ @@ -520,28 +597,58 @@ private: 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; @@ -552,11 +659,16 @@ private: 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 }; diff --git a/src/mapchunk.h b/src/mapchunk.h new file mode 100644 index 000000000..284eebe60 --- /dev/null +++ b/src/mapchunk.h @@ -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 + diff --git a/src/mapsector.cpp b/src/mapsector.cpp index 8a3728c8f..fa1bb68d0 100644 --- a/src/mapsector.cpp +++ b/src/mapsector.cpp @@ -82,11 +82,16 @@ MapBlock * MapSector::getBlockBuffered(s16 y) 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(); diff --git a/src/mapsector.h b/src/mapsector.h index 0c32e2606..de93806b5 100644 --- a/src/mapsector.h +++ b/src/mapsector.h @@ -67,6 +67,7 @@ public: return m_pos; } + MapBlock * getBlockNoCreateNoEx(s16 y); MapBlock * getBlockNoCreate(s16 y); MapBlock * createBlankBlockNoInsert(s16 y); MapBlock * createBlankBlock(s16 y); diff --git a/src/server.cpp b/src/server.cpp index 823a48b90..80aa47671 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -156,20 +156,18 @@ void * EmergeThread::Thread() 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 */ @@ -206,6 +204,12 @@ void * EmergeThread::Thread() { //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) @@ -581,16 +585,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, /* 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()) @@ -603,6 +601,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, { 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. */ - 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());*/ @@ -636,6 +641,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, // 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; diff --git a/src/utility.h b/src/utility.h index 055c8db1a..a61de1c37 100644 --- a/src/utility.h +++ b/src/utility.h @@ -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) { return ( diff --git a/src/voxel.cpp b/src/voxel.cpp index d68a8db02..02635d3af 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -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 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) @@ -560,7 +563,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank, } #endif -#if 0 +#if 1 /* Lights neighbors of from_nodes, collects all them and then goes on recursively. |