]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/map.cpp
Modified windows build parameters a bit to make it build
[dragonfireclient.git] / src / map.cpp
index 184d89b9c7f9af4c23a0d6d8b84671b1fb134798..d644215be78911de0d56af9825eb684e98d94d3d 100644 (file)
@@ -34,30 +34,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 Map::Map(std::ostream &dout):
        m_dout(dout),
-       m_camera_position(0,0,0),
-       m_camera_direction(0,0,1),
        m_sector_cache(NULL)
 {
        m_sector_mutex.Init();
-       m_camera_mutex.Init();
        assert(m_sector_mutex.IsInitialized());
-       assert(m_camera_mutex.IsInitialized());
-       
-       // Get this so that the player can stay on it at first
-       //getSector(v2s16(0,0));
 }
 
 Map::~Map()
 {
        /*
-               Stop updater thread
-       */
-       /*updater.setRun(false);
-       while(updater.IsRunning())
-               sleep_s(1);*/
-
-       /*
-               Free all MapSectors.
+               Free all MapSectors
        */
        core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
        for(; i.atEnd() == false; i++)
@@ -67,6 +53,29 @@ Map::~Map()
        }
 }
 
+void Map::addEventReceiver(MapEventReceiver *event_receiver)
+{
+       m_event_receivers.insert(event_receiver, false);
+}
+
+void Map::removeEventReceiver(MapEventReceiver *event_receiver)
+{
+       if(m_event_receivers.find(event_receiver) == NULL)
+               return;
+       m_event_receivers.remove(event_receiver);
+}
+
+void Map::dispatchEvent(MapEditEvent *event)
+{
+       for(core::map<MapEventReceiver*, bool>::Iterator
+                       i = m_event_receivers.getIterator();
+                       i.atEnd()==false; i++)
+       {
+               MapEventReceiver* event_receiver = i.getNode()->getKey();
+               event_receiver->onMapEditEvent(event);
+       }
+}
+
 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
 {
        if(m_sector_cache != NULL && p == m_sector_cache_p){
@@ -145,34 +154,6 @@ MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
        return block;
 }*/
 
-f32 Map::getGroundHeight(v2s16 p, bool generate)
-{
-       try{
-               v2s16 sectorpos = getNodeSectorPos(p);
-               MapSector * sref = getSectorNoGenerate(sectorpos);
-               v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
-               f32 y = sref->getGroundHeight(relpos);
-               return y;
-       }
-       catch(InvalidPositionException &e)
-       {
-               return GROUNDHEIGHT_NOTFOUND_SETVALUE;
-       }
-}
-
-void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
-{
-       /*m_dout<<DTIME<<"Map::setGroundHeight(("
-                       <<p.X<<","<<p.Y
-                       <<"), "<<y<<")"<<std::endl;*/
-       v2s16 sectorpos = getNodeSectorPos(p);
-       MapSector * sref = getSectorNoGenerate(sectorpos);
-       v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
-       //sref->mutex.Lock();
-       sref->setGroundHeight(relpos, y);
-       //sref->mutex.Unlock();
-}
-
 bool Map::isNodeUnderground(v3s16 p)
 {
        v3s16 blockpos = getNodeBlockPos(p);
@@ -860,15 +841,13 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
        The lighting value of the node should be left as-is after changing
        other values. This sets the lighting value to 0.
 */
-/*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
-               core::map<v3s16, MapBlock*> &modified_blocks)*/
 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                core::map<v3s16, MapBlock*> &modified_blocks)
 {
        /*PrintInfo(m_dout);
-       m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
+       m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
                        <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-
+       
        /*
                From this node to nodes underneath:
                If lighting is sunlight (1.0), unlight neighbours and
@@ -898,11 +877,12 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        {
        }
        
-       if(n.d != CONTENT_TORCH)
+       /*
+               If the new node doesn't propagate sunlight and there is
+               grass below, change it to mud
+       */
+       if(content_features(n.d).sunlight_propagates == false)
        {
-               /*
-                       If there is grass below, change it to mud
-               */
                try{
                        MapNode bottomnode = getNode(bottompos);
                        
@@ -918,6 +898,19 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                }
        }
 
+       /*
+               If the new node is mud and it is under sunlight, change it
+               to grass
+       */
+       if(n.d == CONTENT_MUD && node_under_sunlight)
+       {
+               n.d = CONTENT_GRASS;
+       }
+
+       /*
+               Remove all light that has come out of this node
+       */
+
        enum LightBank banks[] =
        {
                LIGHTBANK_DAY,
@@ -935,8 +928,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                assert(block != NULL);
                modified_blocks.insert(blockpos, block);
                
-               if(isValidPosition(p) == false)
-                       throw;
+               assert(isValidPosition(p));
                        
                // Unlight neighbours of node.
                // This means setting light of all consequent dimmer nodes
@@ -947,17 +939,41 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 
                n.setLight(bank, 0);
        }
+
+       /*
+               If node lets sunlight through and is under sunlight, it has
+               sunlight too.
+       */
+       if(node_under_sunlight && content_features(n.d).sunlight_propagates)
+       {
+               n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
+       }
+       
+       /*
+               Set the node on the map
+       */
        
        setNode(p, n);
+
+       /*
+               Add intial metadata
+       */
+
+       NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
+       if(meta_proto)
+       {
+               NodeMetadata *meta = meta_proto->clone();
+               setNodeMetadata(p, meta);
+       }
        
        /*
-               If node is under sunlight, take all sunlighted nodes under
-               it and clear light from them and from where the light has
-               been spread.
+               If node is under sunlight and doesn't let sunlight through,
+               take all sunlighted nodes under it and clear light from them
+               and from where the light has been spread.
                TODO: This could be optimized by mass-unlighting instead
                      of looping
        */
-       if(node_under_sunlight)
+       if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
        {
                s16 y = p.Y - 1;
                for(;; y--){
@@ -975,7 +991,6 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
 
                        if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
                        {
-                               //m_dout<<DTIME<<"doing"<<std::endl;
                                unLightNeighbors(LIGHTBANK_DAY,
                                                n2pos, n2.getLight(LIGHTBANK_DAY),
                                                light_sources, modified_blocks);
@@ -986,14 +1001,13 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                                break;
                }
        }
-       
+
        for(s32 i=0; i<2; i++)
        {
                enum LightBank bank = banks[i];
                
                /*
                        Spread light from all nodes that might be capable of doing so
-                       TODO: Convert to spreadLight
                */
                spreadLight(bank, light_sources, modified_blocks);
        }
@@ -1090,6 +1104,12 @@ void Map::removeNodeAndUpdate(v3s16 p,
                                light_sources, modified_blocks);
        }
 
+       /*
+               Remove node metadata
+       */
+
+       removeNodeMetadata(p);
+
        /*
                Remove the node.
                This also clears the lighting.
@@ -1147,7 +1167,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
                }
                catch(InvalidPositionException &e)
                {
-                       throw;
+                       assert(0);
                }
        }
 
@@ -1207,94 +1227,62 @@ void Map::removeNodeAndUpdate(v3s16 p,
        }
 }
 
-#ifndef SERVER
-void Map::expireMeshes(bool only_daynight_diffed)
+bool Map::addNodeWithEvent(v3s16 p, MapNode n)
 {
-       TimeTaker timer("expireMeshes()");
+       MapEditEvent event;
+       event.type = MEET_ADDNODE;
+       event.p = p;
+       event.n = n;
 
-       core::map<v2s16, MapSector*>::Iterator si;
-       si = m_sectors.getIterator();
-       for(; si.atEnd() == false; si++)
-       {
-               MapSector *sector = si.getNode()->getValue();
+       bool succeeded = true;
+       try{
+               core::map<v3s16, MapBlock*> modified_blocks;
+               addNodeAndUpdate(p, n, modified_blocks);
 
-               core::list< MapBlock * > sectorblocks;
-               sector->getBlocks(sectorblocks);
-               
-               core::list< MapBlock * >::Iterator i;
-               for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
+               // Copy modified_blocks to event
+               for(core::map<v3s16, MapBlock*>::Iterator
+                               i = modified_blocks.getIterator();
+                               i.atEnd()==false; i++)
                {
-                       MapBlock *block = *i;
-
-                       if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
-                       {
-                               continue;
-                       }
-                       
-                       {
-                               JMutexAutoLock lock(block->mesh_mutex);
-                               if(block->mesh != NULL)
-                               {
-                                       /*block->mesh->drop();
-                                       block->mesh = NULL;*/
-                                       block->setMeshExpired(true);
-                               }
-                       }
+                       event.modified_blocks.insert(i.getNode()->getKey(), false);
                }
        }
+       catch(InvalidPositionException &e){
+               succeeded = false;
+       }
+
+       dispatchEvent(&event);
+
+       return succeeded;
 }
 
-void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
+bool Map::removeNodeWithEvent(v3s16 p)
 {
-       assert(mapType() == MAPTYPE_CLIENT);
+       MapEditEvent event;
+       event.type = MEET_REMOVENODE;
+       event.p = p;
 
+       bool succeeded = true;
        try{
-               v3s16 p = blockpos + v3s16(0,0,0);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-       }
-       catch(InvalidPositionException &e){}
-       // Leading edge
-       try{
-               v3s16 p = blockpos + v3s16(-1,0,0);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-       }
-       catch(InvalidPositionException &e){}
-       try{
-               v3s16 p = blockpos + v3s16(0,-1,0);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-       }
-       catch(InvalidPositionException &e){}
-       try{
-               v3s16 p = blockpos + v3s16(0,0,-1);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-       }
-       catch(InvalidPositionException &e){}
-       /*// Trailing edge
-       try{
-               v3s16 p = blockpos + v3s16(1,0,0);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
+               core::map<v3s16, MapBlock*> modified_blocks;
+               removeNodeAndUpdate(p, modified_blocks);
+
+               // Copy modified_blocks to event
+               for(core::map<v3s16, MapBlock*>::Iterator
+                               i = modified_blocks.getIterator();
+                               i.atEnd()==false; i++)
+               {
+                       event.modified_blocks.insert(i.getNode()->getKey(), false);
+               }
        }
-       catch(InvalidPositionException &e){}
-       try{
-               v3s16 p = blockpos + v3s16(0,1,0);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
+       catch(InvalidPositionException &e){
+               succeeded = false;
        }
-       catch(InvalidPositionException &e){}
-       try{
-               v3s16 p = blockpos + v3s16(0,0,1);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-       }
-       catch(InvalidPositionException &e){}*/
-}
 
-#endif
+       dispatchEvent(&event);
+
+       return succeeded;
+}
 
 bool Map::dayNightDiffed(v3s16 blockpos)
 {
@@ -1725,6 +1713,49 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
        //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
 }
 
+NodeMetadata* Map::getNodeMetadata(v3s16 p)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if(block == NULL)
+       {
+               dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
+                               <<std::endl;
+               return NULL;
+       }
+       NodeMetadata *meta = block->m_node_metadata.get(p_rel);
+       return meta;
+}
+
+void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if(block == NULL)
+       {
+               dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
+                               <<std::endl;
+               return;
+       }
+       block->m_node_metadata.set(p_rel, meta);
+}
+
+void Map::removeNodeMetadata(v3s16 p)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if(block == NULL)
+       {
+               dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
+                               <<std::endl;
+               return;
+       }
+       block->m_node_metadata.remove(p_rel);
+}
+
 /*
        ServerMap
 */
@@ -1990,34 +2021,13 @@ double tree_amount_2d(u64 seed, v2s16 p)
                return 0.04 * (noise-zeroval) / (1.0-zeroval);
 }
 
-/*double base_rock_level_2d(u64 seed, v2s16 p)
-{
-       return WATER_LEVEL - 6.0 + 25. * noise2d_perlin(
-                       0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-                       seed, 6, 0.6);
-}*/
-
-/*double highlands_level_2d(u64 seed, v2s16 p)
-{
-       double a = noise2d_perlin(
-                       0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
-                       seed-359, 6, 0.65);
-       if(a > 0.0)
-       //if(1)
-       {
-               return WATER_LEVEL + 25;
-               return WATER_LEVEL + 55. * noise2d_perlin(
-                               0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-                               seed+85039, 6, 0.69);
-       }
-       else
-               return -100000;
-}*/
+#define AVERAGE_MUD_AMOUNT 4
 
 double base_rock_level_2d(u64 seed, v2s16 p)
 {
        // The base ground level
-       double base = (double)WATER_LEVEL - 4.0 + 25. * noise2d_perlin(
+       double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
+                       + 25. * noise2d_perlin(
                        0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
                        (seed>>32)+654879876, 6, 0.6);
        
@@ -3186,6 +3196,9 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
                                // Don't make a tree under water level
                                if(y < WATER_LEVEL)
                                        continue;
+                               // Don't make a tree so high that it doesn't fit
+                               if(y > y_nodes_max - 6)
+                                       continue;
                                v3s16 p(x,y,z);
                                /*
                                        Trees grow only on mud and grass
@@ -3823,6 +3836,7 @@ MapBlock * ServerMap::generateBlock(
                        
        v2s16 p2d(p.X, p.Z);
        s16 block_y = p.Y;
+       v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
        
        /*
                Do not generate over-limit
@@ -3861,14 +3875,17 @@ MapBlock * ServerMap::generateBlock(
        {
                //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
 
-               s16 surface_y = 0;
+               //s16 surface_y = 0;
+
+               s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
+                               + AVERAGE_MUD_AMOUNT;
 
                if(surface_y < lowest_ground_y)
                        lowest_ground_y = surface_y;
                if(surface_y > highest_ground_y)
                        highest_ground_y = surface_y;
 
-               s32 surface_depth = 2;
+               s32 surface_depth = AVERAGE_MUD_AMOUNT;
                
                for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
                {
@@ -4374,7 +4391,7 @@ MapBlock * ServerMap::generateBlock(
                        //if(!is_ground_content(block->getNode(cp).d))
                        if(1)
                        {
-                               RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
+                               RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
                                block->addObject(obj);
                        }
                }
@@ -4412,6 +4429,17 @@ MapBlock * ServerMap::createBlock(v3s16 p)
        DSTACK("%s: p=(%d,%d,%d)",
                        __FUNCTION_NAME, p.X, p.Y, p.Z);
        
+       /*
+               Do not create over-limit
+       */
+       if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
+               throw InvalidPositionException("createBlock(): pos. over limit");
+       
        v2s16 p2d(p.X, p.Z);
        s16 block_y = p.Y;
        /*
@@ -4426,17 +4454,22 @@ MapBlock * ServerMap::createBlock(v3s16 p)
                sector = (ServerMapSector*)createSector(p2d);
                assert(sector->getId() == MAPSECTOR_SERVER);
        }
-       /*catch(InvalidPositionException &e)
+       catch(InvalidPositionException &e)
        {
                dstream<<"createBlock: createSector() failed"<<std::endl;
                throw e;
-       }*/
-       catch(std::exception &e)
+       }
+       /*
+               NOTE: This should not be done, or at least the exception
+               should not be passed on as std::exception, because it
+               won't be catched at all.
+       */
+       /*catch(std::exception &e)
        {
                dstream<<"createBlock: createSector() failed: "
                                <<e.what()<<std::endl;
                throw e;
-       }
+       }*/
 
        /*
                Try to get a block from the sector
@@ -4461,6 +4494,17 @@ MapBlock * ServerMap::emergeBlock(
                        __FUNCTION_NAME,
                        p.X, p.Y, p.Z, only_from_disk);
        
+       /*
+               Do not generate over-limit
+       */
+       if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
+       || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
+               throw InvalidPositionException("emergeBlock(): pos. over limit");
+       
        v2s16 p2d(p.X, p.Z);
        s16 block_y = p.Y;
        /*
@@ -4472,7 +4516,7 @@ MapBlock * ServerMap::emergeBlock(
                sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
                assert(sector->getId() == MAPSECTOR_SERVER);
        }
-       catch(std::exception &e)
+       catch(InvalidPositionException &e)
        {
                dstream<<"emergeBlock: emergeSector() failed: "
                                <<e.what()<<std::endl;
@@ -4481,6 +4525,29 @@ MapBlock * ServerMap::emergeBlock(
                                <<"You could try to delete it."<<std::endl;
                throw e;
        }
+       catch(VersionMismatchException &e)
+       {
+               dstream<<"emergeBlock: emergeSector() failed: "
+                               <<e.what()<<std::endl;
+               dstream<<"Path to failed sector: "<<getSectorDir(p2d)
+                               <<std::endl
+                               <<"You could try to delete it."<<std::endl;
+               throw e;
+       }
+       /*
+               NOTE: This should not be done, or at least the exception
+               should not be passed on as std::exception, because it
+               won't be catched at all.
+       */
+       /*catch(std::exception &e)
+       {
+               dstream<<"emergeBlock: emergeSector() failed: "
+                               <<e.what()<<std::endl;
+               dstream<<"Path to failed sector: "<<getSectorDir(p2d)
+                               <<std::endl
+                               <<"You could try to delete it."<<std::endl;
+               throw e;
+       }*/
 
        /*
                Try to get a block from the sector
@@ -4577,6 +4644,42 @@ MapBlock * ServerMap::emergeBlock(
        return block;
 }
 
+s16 ServerMap::findGroundLevel(v2s16 p2d)
+{
+       /*
+               Uh, just do something random...
+       */
+       // Find existing map from top to down
+       s16 max=63;
+       s16 min=-64;
+       v3s16 p(p2d.X, max, p2d.Y);
+       for(; p.Y>min; p.Y--)
+       {
+               MapNode n = getNodeNoEx(p);
+               if(n.d != CONTENT_IGNORE)
+                       break;
+       }
+       if(p.Y == min)
+               goto plan_b;
+       // If this node is not air, go to plan b
+       if(getNodeNoEx(p).d != CONTENT_AIR)
+               goto plan_b;
+       // Search existing walkable and return it
+       for(; p.Y>min; p.Y--)
+       {
+               MapNode n = getNodeNoEx(p);
+               if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
+                       return p.Y;
+       }
+       // Move to plan b
+plan_b:
+       /*
+               Plan B: Get from map generator perlin noise function
+       */
+       double level = base_rock_level_2d(m_seed, p2d);
+       return (s16)level;
+}
+
 void ServerMap::createDir(std::string path)
 {
        if(fs::CreateDir(path) == false)
@@ -5033,29 +5136,6 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
        return true;
 }
 
-#if 0
-bool ServerMap::deFlushSector(v2s16 p2d)
-{
-       DSTACK(__FUNCTION_NAME);
-       // See if it already exists in memory
-       try{
-               MapSector *sector = getSectorNoGenerate(p2d);
-               return true;
-       }
-       catch(InvalidPositionException &e)
-       {
-               /*
-                       Try to load the sector from disk.
-               */
-               if(loadSectorFull(p2d) == true)
-               {
-                       return true;
-               }
-       }
-       return false;
-}
-#endif
-
 void ServerMap::saveBlock(MapBlock *block)
 {
        DSTACK(__FUNCTION_NAME);
@@ -5114,96 +5194,79 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
 
        try{
 
-       // Block file is map/sectors/xxxxxxxx/xxxx
-       std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
-       std::ifstream is(fullpath.c_str(), std::ios_base::binary);
-       if(is.good() == false)
-               throw FileNotGoodException("Cannot open block file");
+               // Block file is map/sectors/xxxxxxxx/xxxx
+               std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
+               std::ifstream is(fullpath.c_str(), std::ios_base::binary);
+               if(is.good() == false)
+                       throw FileNotGoodException("Cannot open block file");
 
-       v3s16 p3d = getBlockPos(sectordir, blockfile);
-       v2s16 p2d(p3d.X, p3d.Z);
-       
-       assert(sector->getPos() == p2d);
-       
-       u8 version = SER_FMT_VER_INVALID;
-       is.read((char*)&version, 1);
+               v3s16 p3d = getBlockPos(sectordir, blockfile);
+               v2s16 p2d(p3d.X, p3d.Z);
+               
+               assert(sector->getPos() == p2d);
+               
+               u8 version = SER_FMT_VER_INVALID;
+               is.read((char*)&version, 1);
 
-       /*u32 block_size = MapBlock::serializedLength(version);
-       SharedBuffer<u8> data(block_size);
-       is.read((char*)*data, block_size);*/
+               if(is.fail())
+                       throw SerializationError("ServerMap::loadBlock(): Failed"
+                                       " to read MapBlock version");
 
-       // This will always return a sector because we're the server
-       //MapSector *sector = emergeSector(p2d);
+               /*u32 block_size = MapBlock::serializedLength(version);
+               SharedBuffer<u8> data(block_size);
+               is.read((char*)*data, block_size);*/
 
-       MapBlock *block = NULL;
-       bool created_new = false;
-       try{
-               block = sector->getBlockNoCreate(p3d.Y);
-       }
-       catch(InvalidPositionException &e)
-       {
-               block = sector->createBlankBlockNoInsert(p3d.Y);
-               created_new = true;
-       }
-       
-       // deserialize block data
-       block->deSerialize(is, version);
-       
-       /*
-               Versions up from 9 have block objects.
-       */
-       if(version >= 9)
-       {
-               block->updateObjects(is, version, NULL, 0);
-       }
+               // This will always return a sector because we're the server
+               //MapSector *sector = emergeSector(p2d);
 
-       if(created_new)
-               sector->insertBlock(block);
-       
-       /*
-               Convert old formats to new and save
-       */
+               MapBlock *block = NULL;
+               bool created_new = false;
+               try{
+                       block = sector->getBlockNoCreate(p3d.Y);
+               }
+               catch(InvalidPositionException &e)
+               {
+                       block = sector->createBlankBlockNoInsert(p3d.Y);
+                       created_new = true;
+               }
+               
+               // deserialize block data
+               block->deSerialize(is, version);
+               
+               /*
+                       Versions up from 9 have block objects.
+               */
+               if(version >= 9)
+               {
+                       block->updateObjects(is, version, NULL, 0);
+               }
 
-       // Save old format blocks in new format
-       if(version < SER_FMT_VER_HIGHEST)
-       {
-               saveBlock(block);
-       }
-       
-       // We just loaded it from the disk, so it's up-to-date.
-       block->resetChangedFlag();
+               if(created_new)
+                       sector->insertBlock(block);
+               
+               /*
+                       Convert old formats to new and save
+               */
+
+               // Save old format blocks in new format
+               if(version < SER_FMT_VER_HIGHEST)
+               {
+                       saveBlock(block);
+               }
+               
+               // We just loaded it from the disk, so it's up-to-date.
+               block->resetChangedFlag();
 
        }
        catch(SerializationError &e)
        {
                dstream<<"WARNING: Invalid block data on disk "
-                               "(SerializationError). Ignoring."
+                               "(SerializationError). Ignoring. "
+                               "A new one will be generated."
                                <<std::endl;
        }
 }
 
-// Gets from master heightmap
-void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
-{
-       dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
-       //assert(m_heightmap != NULL);
-       /*
-               Corner definition:
-               v2s16(0,0),
-               v2s16(1,0),
-               v2s16(1,1),
-               v2s16(0,1),
-       */
-       /*corners[0] = m_heightmap->getGroundHeight
-                       ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
-       corners[1] = m_heightmap->getGroundHeight
-                       ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
-       corners[2] = m_heightmap->getGroundHeight
-                       ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
-       corners[3] = m_heightmap->getGroundHeight
-                       ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
-}
-
 void ServerMap::PrintInfo(std::ostream &out)
 {
        out<<"ServerMap: ";
@@ -5225,20 +5288,15 @@ ClientMap::ClientMap(
        Map(dout_client),
        scene::ISceneNode(parent, mgr, id),
        m_client(client),
-       m_control(control)
+       m_control(control),
+       m_camera_position(0,0,0),
+       m_camera_direction(0,0,1)
 {
-       //mesh_mutex.Init();
-
-       /*m_box = core::aabbox3d<f32>(0,0,0,
-                       map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
-       /*m_box = core::aabbox3d<f32>(0,0,0,
-                       map->getSizeNodes().X * BS,
-                       map->getSizeNodes().Y * BS,
-                       map->getSizeNodes().Z * BS);*/
+       m_camera_mutex.Init();
+       assert(m_camera_mutex.IsInitialized());
+       
        m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
                        BS*1000000,BS*1000000,BS*1000000);
-       
-       //setPosition(v3f(BS,BS,BS));
 }
 
 ClientMap::~ClientMap()
@@ -5263,7 +5321,7 @@ MapSector * ClientMap::emergeSector(v2s16 p2d)
        {
        }
        
-       // Create a sector with no heightmaps
+       // Create a sector
        ClientMapSector *sector = new ClientMapSector(this, p2d);
        
        {
@@ -5326,7 +5384,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
        */
        int time1 = time(0);
 
-       u32 daynight_ratio = m_client->getDayNightRatio();
+       //u32 daynight_ratio = m_client->getDayNightRatio();
 
        m_camera_mutex.Lock();
        v3f camera_position = m_camera_position;
@@ -5429,7 +5487,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                continue;
                        }
                        
-                       // This is ugly
+                       // This is ugly (spherical distance limit?)
                        /*if(m_control.range_all == false &&
                                        d - 0.5*BS*MAP_BLOCKSIZE > range)
                                continue;*/
@@ -5437,6 +5495,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 #if 1
                        /*
                                Update expired mesh (used for day/night change)
+
+                               It doesn't work exactly like it should now with the
+                               tasked mesh update but whatever.
                        */
 
                        bool mesh_expired = false;
@@ -5473,28 +5534,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                mesh_update_count++;
 
                                // Mesh has been expired: generate new mesh
-                               //block->updateMeshes(daynight_i);
-                               block->updateMesh(daynight_ratio);
+                               //block->updateMesh(daynight_ratio);
+                               m_client->addUpdateMeshTask(block->getPos());
 
                                mesh_expired = false;
                        }
                        
-                       /*
-                               Don't draw an expired mesh that is far away
-                       */
-                       /*if(mesh_expired && d >= faraway)
-                       //if(mesh_expired)
-                       {
-                               // Instead, delete it
-                               JMutexAutoLock lock(block->mesh_mutex);
-                               if(block->mesh)
-                               {
-                                       block->mesh->drop();
-                                       block->mesh = NULL;
-                               }
-                               // And continue to next block
-                               continue;
-                       }*/
 #endif
                        /*
                                Draw the faces of the block
@@ -5638,6 +5683,114 @@ bool ClientMap::clearTempMod(v3s16 p,
        return changed;
 }
 
+void ClientMap::expireMeshes(bool only_daynight_diffed)
+{
+       TimeTaker timer("expireMeshes()");
+
+       core::map<v2s16, MapSector*>::Iterator si;
+       si = m_sectors.getIterator();
+       for(; si.atEnd() == false; si++)
+       {
+               MapSector *sector = si.getNode()->getValue();
+
+               core::list< MapBlock * > sectorblocks;
+               sector->getBlocks(sectorblocks);
+               
+               core::list< MapBlock * >::Iterator i;
+               for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
+               {
+                       MapBlock *block = *i;
+
+                       if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
+                       {
+                               continue;
+                       }
+                       
+                       {
+                               JMutexAutoLock lock(block->mesh_mutex);
+                               if(block->mesh != NULL)
+                               {
+                                       /*block->mesh->drop();
+                                       block->mesh = NULL;*/
+                                       block->setMeshExpired(true);
+                               }
+                       }
+               }
+       }
+}
+
+void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
+{
+       assert(mapType() == MAPTYPE_CLIENT);
+
+       try{
+               v3s16 p = blockpos + v3s16(0,0,0);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
+               //b->setMeshExpired(true);
+       }
+       catch(InvalidPositionException &e){}
+       // Leading edge
+       try{
+               v3s16 p = blockpos + v3s16(-1,0,0);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
+               //b->setMeshExpired(true);
+       }
+       catch(InvalidPositionException &e){}
+       try{
+               v3s16 p = blockpos + v3s16(0,-1,0);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
+               //b->setMeshExpired(true);
+       }
+       catch(InvalidPositionException &e){}
+       try{
+               v3s16 p = blockpos + v3s16(0,0,-1);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
+               //b->setMeshExpired(true);
+       }
+       catch(InvalidPositionException &e){}
+}
+
+#if 0
+/*
+       Update mesh of block in which the node is, and if the node is at the
+       leading edge, update the appropriate leading blocks too.
+*/
+void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
+{
+       v3s16 dirs[4] = {
+               v3s16(0,0,0),
+               v3s16(-1,0,0),
+               v3s16(0,-1,0),
+               v3s16(0,0,-1),
+       };
+       v3s16 blockposes[4];
+       for(u32 i=0; i<4; i++)
+       {
+               v3s16 np = nodepos + dirs[i];
+               blockposes[i] = getNodeBlockPos(np);
+               // Don't update mesh of block if it has been done already
+               bool already_updated = false;
+               for(u32 j=0; j<i; j++)
+               {
+                       if(blockposes[j] == blockposes[i])
+                       {
+                               already_updated = true;
+                               break;
+                       }
+               }
+               if(already_updated)
+                       continue;
+               // Update mesh
+               MapBlock *b = getBlockNoCreate(blockposes[i]);
+               b->updateMesh(daynight_ratio);
+       }
+}
+#endif
+
 void ClientMap::PrintInfo(std::ostream &out)
 {
        out<<"ClientMap: ";