map generation framework under development... not quite operational at this point.
authorPerttu Ahola <celeron55@gmail.com>
Sat, 29 Jan 2011 23:44:54 +0000 (01:44 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Sat, 29 Jan 2011 23:44:54 +0000 (01:44 +0200)
13 files changed:
src/constants.h
src/defaultsettings.cpp
src/main.cpp
src/map.cpp
src/map.h
src/mapblock.cpp
src/mapblock.h
src/mapchunk.h [new file with mode: 0644]
src/mapsector.cpp
src/mapsector.h
src/server.cpp
src/utility.h
src/voxel.cpp

index f90b278d2b25ade145f26c0ce1744e1431a84ec2..c8c210b1307cd03c7076f569386aafc98ba17b1d 100644 (file)
@@ -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
 
 /*
index 4046b81b96b6b61acb55b918970e1fe906da6b9a..0665cd02eee12022058af3e6f7bc927a3b648ea8 100644 (file)
@@ -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");
index 0dc822474ec9e61b0903564d9cbccfe8d09132d7..dc4716fabb35f9b58db33968f78286434145025d 100644 (file)
@@ -168,7 +168,7 @@ TODO: Make fetching sector's blocks more efficient when rendering
 \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
@@ -182,6 +182,15 @@ FIXME: The new texture stuff is slow on wine
          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
@@ -255,6 +264,20 @@ Map:
 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
@@ -269,6 +292,11 @@ FEATURE: Map generator version 2
 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
@@ -283,20 +311,13 @@ FEATURE: The map could be generated procedually:
                        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
@@ -1522,6 +1543,16 @@ int main(int argc, char *argv[])
        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
@@ -1684,7 +1715,7 @@ int main(int argc, char *argv[])
        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
@@ -2131,20 +2162,6 @@ int main(int argc, char *argv[])
                                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
index 2782cef03dd1a210118ee43850f07fd337ed163b..cc1a6d638d4c07c88c4472cabcb86a2d5fc32c08 100644 (file)
@@ -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,29 +1998,134 @@ 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.
        */
@@ -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,91 +2341,104 @@ 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
        */
index 9140d4bf254372b50af39e9224ff101fe1714a27..bc6984c8851e7400c9b356dd1a8e200e8366241d 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -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
index b346b098090ffd5edd055ae8e8a662c4ad017752..a7bc730ce076723ce69062fa39c7a6f0ef0210b5 100644 (file)
@@ -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);
index dd527766861c6ae7e128e847c366bca5b10054cd..e39db35bdb8dde49caa68134c3a47f199f3347c3 100644 (file)
@@ -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 (file)
index 0000000..284eebe
--- /dev/null
@@ -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
+
index 8a3728c8f2a0ddedd29933efd35b375e0b41befd..fa1bb68d0dc861e4e83f9844409d336e5d82d915 100644 (file)
@@ -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();
index 0c32e2606f07b3554b5ef91172f9cd71226c6428..de93806b5a88703b91c7885eb2b6e8c67f211db0 100644 (file)
@@ -67,6 +67,7 @@ public:
                return m_pos;
        }
 
+       MapBlock * getBlockNoCreateNoEx(s16 y);
        MapBlock * getBlockNoCreate(s16 y);
        MapBlock * createBlankBlockNoInsert(s16 y);
        MapBlock * createBlankBlock(s16 y);
index 823a48b900b7d86b2424b26ea8627545ddb6878c..80aa47671849809a9af105f11a190981c03f94a3 100644 (file)
@@ -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;
index 055c8db1a15d41b19f96830f99bbc6b78100343c..a61de1c37e7815d92e94e9b7b47b801eac39523d 100644 (file)
@@ -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 (
index d68a8db0211eb7bd14afed2b0988e9c9962275dc..02635d3af54a6ae4548ad6f2b4ebf45903a2f848 100644 (file)
@@ -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.