Middle mouse click will move 10 items around at a time.
[oweals/minetest.git] / src / map.cpp
index cf8692bff9ae4a5bcb2bdaa1a2338e4cee06aa4f..27a491428dc812f18a24d217d651498a80d724bb 100644 (file)
@@ -630,9 +630,9 @@ s16 Map::propagateSunlight(v3s16 start,
                else
                {
                        /*// Turn mud into grass
-                       if(n.d == CONTENT_MUD)
+                       if(n.getContent() == CONTENT_MUD)
                        {
-                               n.d = CONTENT_GRASS;
+                               n.setContent(CONTENT_GRASS);
                                block->setNode(relpos, n);
                                modified_blocks.insert(blockpos, block);
                        }*/
@@ -920,15 +920,15 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        /*
                If the new node is solid and there is grass below, change it to mud
        */
-       if(content_features(n.d).walkable == true)
+       if(content_features(n).walkable == true)
        {
                try{
                        MapNode bottomnode = getNode(bottompos);
 
-                       if(bottomnode.d == CONTENT_GRASS
-                                       || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
+                       if(bottomnode.getContent() == CONTENT_GRASS
+                                       || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
                        {
-                               bottomnode.d = CONTENT_MUD;
+                               bottomnode.setContent(CONTENT_MUD);
                                setNode(bottompos, bottomnode);
                        }
                }
@@ -943,9 +943,9 @@ 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)
+       if(n.getContent() == CONTENT_MUD && node_under_sunlight)
        {
-               n.d = CONTENT_GRASS;
+               n.setContent(CONTENT_GRASS);
        }
 #endif
 
@@ -986,7 +986,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                If node lets sunlight through and is under sunlight, it has
                sunlight too.
        */
-       if(node_under_sunlight && content_features(n.d).sunlight_propagates)
+       if(node_under_sunlight && content_features(n).sunlight_propagates)
        {
                n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
        }
@@ -1001,7 +1001,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                Add intial metadata
        */
 
-       NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
+       NodeMetadata *meta_proto = content_features(n).initial_metadata;
        if(meta_proto)
        {
                NodeMetadata *meta = meta_proto->clone();
@@ -1015,7 +1015,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                TODO: This could be optimized by mass-unlighting instead
                          of looping
        */
-       if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
+       if(node_under_sunlight && !content_features(n).sunlight_propagates)
        {
                s16 y = p.Y - 1;
                for(;; y--){
@@ -1086,7 +1086,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                v3s16 p2 = p + dirs[i];
 
                MapNode n2 = getNode(p2);
-               if(content_liquid(n2.d) || n2.d == CONTENT_AIR)
+               if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
                {
                        m_transforming_liquid.push_back(p2);
                }
@@ -1111,7 +1111,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
        v3s16 toppos = p + v3s16(0,1,0);
 
        // Node will be replaced with this
-       u8 replace_material = CONTENT_AIR;
+       content_t replace_material = CONTENT_AIR;
 
        /*
                If there is a node at top and it doesn't have sunlight,
@@ -1158,7 +1158,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
        */
 
        MapNode n;
-       n.d = replace_material;
+       n.setContent(replace_material);
        setNode(p, n);
 
        for(s32 i=0; i<2; i++)
@@ -1260,7 +1260,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
                v3s16 p2 = p + dirs[i];
 
                MapNode n2 = getNode(p2);
-               if(content_liquid(n2.d) || n2.d == CONTENT_AIR)
+               if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
                {
                        m_transforming_liquid.push_back(p2);
                }
@@ -1562,50 +1562,54 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
        /*if(initial_size != 0)
                dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
 
+       // list of nodes that due to viscosity have not reached their max level height
+       UniqueQueue<v3s16> must_reflow;
+       
+       // List of MapBlocks that will require a lighting update (due to lava)
+       core::map<v3s16, MapBlock*> lighting_modified_blocks;
+
        while(m_transforming_liquid.size() != 0)
        {
+               // This should be done here so that it is done when continue is used
+               if(loopcount >= initial_size * 3)
+                       break;
+               loopcount++;
+
                /*
                        Get a queued transforming liquid node
                */
                v3s16 p0 = m_transforming_liquid.pop_front();
 
                MapNode n0 = getNodeNoEx(p0);
-                               
+
                /*
                        Collect information about current node
                 */
                s8 liquid_level = -1;
-               u8 liquid_kind;
-               LiquidType liquid_type = content_features(n0.d).liquid_type;
+               u8 liquid_kind = CONTENT_IGNORE;
+               LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
                switch (liquid_type) {
                        case LIQUID_SOURCE:
-                               liquid_level = 8;
-                               liquid_kind = content_features(n0.d).liquid_alternative_flowing;
+                               liquid_level = LIQUID_LEVEL_SOURCE;
+                               liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
                                break;
                        case LIQUID_FLOWING:
                                liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
-                               liquid_kind = n0.d;
+                               liquid_kind = n0.getContent();
                                break;
                        case LIQUID_NONE:
                                // if this is an air node, it *could* be transformed into a liquid. otherwise,
                                // continue with the next node.
-                               if (n0.d != CONTENT_AIR)
+                               if (n0.getContent() != CONTENT_AIR)
                                        continue;
                                liquid_kind = CONTENT_AIR;
                                break;
                }
-               
+
                /*
                        Collect information about the environment
                 */
-               v3s16 dirs[6] = {
-                       v3s16( 0, 1, 0), // top
-                       v3s16( 0,-1, 0), // bottom
-                       v3s16( 1, 0, 0), // right
-                       v3s16(-1, 0, 0), // left
-                       v3s16( 0, 0, 1), // back
-                       v3s16( 0, 0,-1), // front
-               };
+               const v3s16 *dirs = g_6dirs;
                NodeNeighbor sources[6]; // surrounding sources
                int num_sources = 0;
                NodeNeighbor flows[6]; // surrounding flowing liquid nodes
@@ -1618,22 +1622,28 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                for (u16 i = 0; i < 6; i++) {
                        NeighborType nt = NEIGHBOR_SAME_LEVEL;
                        switch (i) {
-                               case 0:
+                               case 1:
                                        nt = NEIGHBOR_UPPER;
                                        break;
-                               case 1:
+                               case 4:
                                        nt = NEIGHBOR_LOWER;
                                        break;
                        }
                        v3s16 npos = p0 + dirs[i];
                        NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
-                       switch (content_features(nb.n.d).liquid_type) {
+                       switch (content_features(nb.n.getContent()).liquid_type) {
                                case LIQUID_NONE:
-                                       if (nb.n.d == CONTENT_AIR) {
+                                       if (nb.n.getContent() == CONTENT_AIR) {
                                                airs[num_airs++] = nb;
+                                               // if the current node is a water source the neighbor
+                                               // should be enqueded for transformation regardless of whether the
+                                               // current node changes or not.
+                                               if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
+                                                       m_transforming_liquid.push_back(npos);
                                                // if the current node happens to be a flowing node, it will start to flow down here.
-                                               if (nb.t == NEIGHBOR_LOWER)
+                                               if (nb.t == NEIGHBOR_LOWER) {
                                                        flowing_down = true;
+                                               }
                                        } else {
                                                neutrals[num_neutrals++] = nb;
                                        }
@@ -1641,8 +1651,8 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                                case LIQUID_SOURCE:
                                        // if this node is not (yet) of a liquid type, choose the first liquid type we encounter 
                                        if (liquid_kind == CONTENT_AIR)
-                                               liquid_kind = content_features(nb.n.d).liquid_alternative_flowing;
-                                       if (content_features(nb.n.d).liquid_alternative_flowing !=liquid_kind) {
+                                               liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
+                                       if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
                                                neutrals[num_neutrals++] = nb;
                                        } else {
                                                sources[num_sources++] = nb;
@@ -1651,8 +1661,8 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                                case LIQUID_FLOWING:
                                        // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
                                        if (liquid_kind == CONTENT_AIR)
-                                               liquid_kind = content_features(nb.n.d).liquid_alternative_flowing;
-                                       if (content_features(nb.n.d).liquid_alternative_flowing != liquid_kind) {
+                                               liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
+                                       if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
                                                neutrals[num_neutrals++] = nb;
                                        } else {
                                                flows[num_flows++] = nb;
@@ -1662,12 +1672,13 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                                        break;
                        }
                }
-               
+
                /*
                        decide on the type (and possibly level) of the current node
                 */
-               u8 new_node_content;
+               content_t new_node_content;
                s8 new_node_level = -1;
+               s8 max_node_level = -1;
                if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
                        // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
                        // or the flowing alternative of the first of the surrounding sources (if it's air), so
@@ -1676,81 +1687,92 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
                        // liquid_kind is set properly, see above
                        new_node_content = liquid_kind;
-                       new_node_level = 7;
+                       max_node_level = new_node_level = LIQUID_LEVEL_MAX;
                } else {
                        // no surrounding sources, so get the maximum level that can flow into this node
                        for (u16 i = 0; i < num_flows; i++) {
                                u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
                                switch (flows[i].t) {
                                        case NEIGHBOR_UPPER:
-                                               if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
-                                                       new_node_level = 7;
-                                                       if (nb_liquid_level + WATER_DROP_BOOST < 7)
-                                                               new_node_level = nb_liquid_level + WATER_DROP_BOOST;
-                                               }
+                                               if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
+                                                       max_node_level = LIQUID_LEVEL_MAX;
+                                                       if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
+                                                               max_node_level = nb_liquid_level + WATER_DROP_BOOST;
+                                               } else if (nb_liquid_level > max_node_level)
+                                                       max_node_level = nb_liquid_level;
                                                break;
                                        case NEIGHBOR_LOWER:
                                                break;
                                        case NEIGHBOR_SAME_LEVEL:
                                                if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
-                                                       nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) {
-                                                       new_node_level = nb_liquid_level - 1;
+                                                       nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
+                                                       max_node_level = nb_liquid_level - 1;
                                                }
                                                break;
                                }
                        }
-                       // don't flow as far in open terrain - if there isn't at least one adjacent solid block,
-                       // substract another unit from the resulting water level.
-                       if (!flowing_down && new_node_level >= 1) {
-                               bool at_wall = false;
-                               for (u16 i = 0; i < num_neutrals; i++) {
-                                       if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) {
-                                               at_wall = true;
-                                               break;
-                                       }
-                               }
-                               if (!at_wall)
-                                       new_node_level -= 1;
-                       }
-                       
+
+                       u8 viscosity = content_features(liquid_kind).liquid_viscosity;
+                       if (viscosity > 1 && max_node_level != liquid_level) {
+                               // amount to gain, limited by viscosity
+                               // must be at least 1 in absolute value
+                               s8 level_inc = max_node_level - liquid_level;
+                               if (level_inc < -viscosity || level_inc > viscosity)
+                                       new_node_level = liquid_level + level_inc/viscosity;
+                               else if (level_inc < 0)
+                                       new_node_level = liquid_level - 1;
+                               else if (level_inc > 0)
+                                       new_node_level = liquid_level + 1;
+                               if (new_node_level != max_node_level)
+                                       must_reflow.push_back(p0);
+                       } else
+                               new_node_level = max_node_level;
+
                        if (new_node_level >= 0)
                                new_node_content = liquid_kind;
                        else
                                new_node_content = CONTENT_AIR;
+
                }
-               
+
                /*
                        check if anything has changed. if not, just continue with the next node.
                 */
-               if (new_node_content == n0.d && (content_features(n0.d).liquid_type != LIQUID_FLOWING ||
+               if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
                                                                                 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
                                                                                 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
                                                                                 == flowing_down)))
                        continue;
-               
-               
+
+
                /*
                        update the current node
                 */
                bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
-               n0.d = new_node_content;
-               if (content_features(n0.d).liquid_type == LIQUID_FLOWING) {
+               if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
                        // set level to last 3 bits, flowing down bit to 4th bit
                        n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
                } else {
-                       n0.param2 = 0;
+                       // set the liquid level and flow bit to 0
+                       n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
                }
+               n0.setContent(new_node_content);
                setNode(p0, n0);
                v3s16 blockpos = getNodeBlockPos(p0);
                MapBlock *block = getBlockNoCreateNoEx(blockpos);
-               if(block != NULL)
+               if(block != NULL) {
                        modified_blocks.insert(blockpos, block);
-               
+                       // If node emits light, MapBlock requires lighting update
+                       if(content_features(n0).light_source != 0)
+                               lighting_modified_blocks[block->getPos()] = block;
+               }
+
                /*
                        enqueue neighbors for update if neccessary
                 */
-               switch (content_features(n0.d).liquid_type) {
+               switch (content_features(n0.getContent()).liquid_type) {
                        case LIQUID_SOURCE:
+                       case LIQUID_FLOWING:
                                // make sure source flows into all neighboring nodes
                                for (u16 i = 0; i < num_flows; i++)
                                        if (flows[i].t != NEIGHBOR_UPPER)
@@ -1764,28 +1786,12 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                                for (u16 i = 0; i < num_flows; i++)
                                        m_transforming_liquid.push_back(flows[i].p);
                                break;
-                       case LIQUID_FLOWING:
-                               for (u16 i = 0; i < num_flows; i++) {
-                                       u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
-                                       // liquid_level is still the ORIGINAL level of this node.
-                                       if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
-                                               flow_down_enabled))
-                                               m_transforming_liquid.push_back(flows[i].p);
-                               }
-                               for (u16 i = 0; i < num_airs; i++) {
-                                       if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
-                                               m_transforming_liquid.push_back(airs[i].p);
-                               }
-                               break;
-               }
-               
-               loopcount++;
-               //if(loopcount >= 100000)
-               if(loopcount >= initial_size * 10) {
-                       break;
                }
        }
        //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
+       while (must_reflow.size() > 0)
+               m_transforming_liquid.push_back(must_reflow.pop_front());
+       updateLighting(lighting_modified_blocks, modified_blocks);
 }
 
 NodeMetadata* Map::getNodeMetadata(v3s16 p)
@@ -2003,9 +2009,19 @@ ServerMap::~ServerMap()
 
 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
 {
-       /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
-                       <<blockpos.Z<<")"<<std::endl;*/
-
+       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+       if(enable_mapgen_debug_info)
+               dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
+                               <<blockpos.Z<<")"<<std::endl;
+       
+       // Do nothing if not inside limits (+-1 because of neighbors)
+       if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
+               blockpos_over_limit(blockpos + v3s16(1,1,1)))
+       {
+               data->no_op = true;
+               return;
+       }
+       
        data->no_op = false;
        data->seed = m_seed;
        data->blockpos = blockpos;
@@ -2026,20 +2042,28 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
 
                        for(s16 y=-1; y<=1; y++)
                        {
-                               MapBlock *block = createBlock(blockpos);
+                               v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
+                               //MapBlock *block = createBlock(p);
+                               // 1) get from memory, 2) load from disk
+                               MapBlock *block = emergeBlock(p, false);
+                               // 3) create a blank one
+                               if(block == NULL)
+                               {
+                                       block = createBlock(p);
+
+                                       /*
+                                               Block gets sunlight if this is true.
+
+                                               Refer to the map generator heuristics.
+                                       */
+                                       bool ug = mapgen::block_is_underground(data->seed, p);
+                                       block->setIsUnderground(ug);
+                               }
 
-                               // Lighting won't be calculated
+                               // Lighting will not be valid after make_chunk is called
                                block->setLightingExpired(true);
                                // Lighting will be calculated
                                //block->setLightingExpired(false);
-
-                               /*
-                                       Block gets sunlight if this is true.
-
-                                       This should be set to true when the top side of a block
-                                       is completely exposed to the sky.
-                               */
-                               block->setIsUnderground(false);
                        }
                }
        }
@@ -2051,6 +2075,7 @@ void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
                neighboring blocks
        */
        
+       // The area that contains this block and it's neighbors
        v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
        v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
        
@@ -2075,7 +2100,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
 
        if(data->no_op)
        {
-               dstream<<"finishBlockMake(): no-op"<<std::endl;
+               //dstream<<"finishBlockMake(): no-op"<<std::endl;
                return NULL;
        }
 
@@ -2114,10 +2139,14 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
        assert(block);
 
        /*
-               Set is_underground flag for lighting with sunlight
+               Set is_underground flag for lighting with sunlight.
+
+               Refer to map generator heuristics.
+
+               NOTE: This is done in initChunkMake
        */
+       //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
 
-       block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
 
        /*
                Add sunlight to central block.
@@ -2145,10 +2174,25 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                TimeTaker t("finishBlockMake lighting update");
 
                core::map<v3s16, MapBlock*> lighting_update_blocks;
+#if 1
                // Center block
                lighting_update_blocks.insert(block->getPos(), block);
-       #if 0
+
+               /*{
+                       s16 x = 0;
+                       s16 z = 0;
+                       v3s16 p = block->getPos()+v3s16(x,1,z);
+                       lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
+               }*/
+#endif
+#if 0
                // All modified blocks
+               // NOTE: Should this be done? If this is not done, then the lighting
+               // of the others will be updated in a different place, one by one, i
+               // think... or they might not? Well, at least they are left marked as
+               // "lighting expired"; it seems that is not handled at all anywhere,
+               // so enabling this will slow it down A LOT because otherwise it
+               // would not do this at all. This causes the black trees.
                for(core::map<v3s16, MapBlock*>::Iterator
                                i = changed_blocks.getIterator();
                                i.atEnd() == false; i++)
@@ -2156,8 +2200,28 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                        lighting_update_blocks.insert(i.getNode()->getKey(),
                                        i.getNode()->getValue());
                }
-       #endif
+               /*// Also force-add all the upmost blocks for proper sunlight
+               for(s16 x=-1; x<=1; x++)
+               for(s16 z=-1; z<=1; z++)
+               {
+                       v3s16 p = block->getPos()+v3s16(x,1,z);
+                       lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
+               }*/
+#endif
                updateLighting(lighting_update_blocks, changed_blocks);
+               
+               /*
+                       Set lighting to non-expired state in all of them.
+                       This is cheating, but it is not fast enough if all of them
+                       would actually be updated.
+               */
+               for(s16 x=-1; x<=1; x++)
+               for(s16 y=-1; y<=1; y++)
+               for(s16 z=-1; z<=1; z++)
+               {
+                       v3s16 p = block->getPos()+v3s16(x,y,z);
+                       getBlockNoCreateNoEx(p)->setLightingExpired(false);
+               }
 
                if(enable_mapgen_debug_info == false)
                        t.stop(true); // Hide output
@@ -2199,7 +2263,26 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
 
        /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
                        <<blockpos.Z<<")"<<std::endl;*/
-       
+#if 0
+       if(enable_mapgen_debug_info)
+       {
+               /*
+                       Analyze resulting blocks
+               */
+               for(s16 x=-1; x<=1; x++)
+               for(s16 y=-1; y<=1; y++)
+               for(s16 z=-1; z<=1; z++)
+               {
+                       v3s16 p = block->getPos()+v3s16(x,y,z);
+                       MapBlock *block = getBlockNoCreateNoEx(p);
+                       char spos[20];
+                       snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
+                       dstream<<"Generated "<<spos<<": "
+                                       <<analyze_block(block)<<std::endl;
+               }
+       }
+#endif
+
        return block;
 }
 
@@ -2319,49 +2402,54 @@ MapBlock * ServerMap::generateBlock(
                Get central block
        */
        MapBlock *block = getBlockNoCreateNoEx(p);
-       assert(block);
 
 #if 0
        /*
                Check result
        */
-       bool erroneus_content = false;
-       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
-       for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
-       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+       if(block)
        {
-               v3s16 p(x0,y0,z0);
-               MapNode n = block->getNode(p);
-               if(n.d == CONTENT_IGNORE)
+               bool erroneus_content = false;
+               for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+               for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+               {
+                       v3s16 p(x0,y0,z0);
+                       MapNode n = block->getNode(p);
+                       if(n.getContent() == CONTENT_IGNORE)
+                       {
+                               dstream<<"CONTENT_IGNORE at "
+                                               <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                                               <<std::endl;
+                               erroneus_content = true;
+                               assert(0);
+                       }
+               }
+               if(erroneus_content)
                {
-                       dstream<<"CONTENT_IGNORE at "
-                                       <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-                                       <<std::endl;
-                       erroneus_content = true;
                        assert(0);
                }
        }
-       if(erroneus_content)
-       {
-               assert(0);
-       }
 #endif
 
 #if 0
        /*
                Generate a completely empty block
        */
-       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
-       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+       if(block)
        {
-               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+               for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+               for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
                {
-                       MapNode n;
-                       if(y0%2==0)
-                               n.d = CONTENT_AIR;
-                       else
-                               n.d = CONTENT_STONE;
-                       block->setNode(v3s16(x0,y0,z0), n);
+                       for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+                       {
+                               MapNode n;
+                               if(y0%2==0)
+                                       n.setContent(CONTENT_AIR);
+                               else
+                                       n.setContent(CONTENT_STONE);
+                               block->setNode(v3s16(x0,y0,z0), n);
+                       }
                }
        }
 #endif
@@ -2443,7 +2531,7 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
        
        {
                MapBlock *block = getBlockNoCreateNoEx(p);
-               if(block)
+               if(block && block->isDummy() == false)
                        return block;
        }
 
@@ -2640,19 +2728,19 @@ s16 ServerMap::findGroundLevel(v2s16 p2d)
        for(; p.Y>min; p.Y--)
        {
                MapNode n = getNodeNoEx(p);
-               if(n.d != CONTENT_IGNORE)
+               if(n.getContent() != 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)
+       if(getNodeNoEx(p).getContent() != 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)
+               if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
                        return p.Y;
        }
 
@@ -4045,10 +4133,16 @@ void ManualMapVoxelManipulator::blitBackAll(
                        i = m_loaded_blocks.getIterator();
                        i.atEnd() == false; i++)
        {
+               v3s16 p = i.getNode()->getKey();
                bool existed = i.getNode()->getValue();
                if(existed == false)
+               {
+                       // The Great Bug was found using this
+                       /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
+                                       <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                                       <<std::endl;*/
                        continue;
-               v3s16 p = i.getNode()->getKey();
+               }
                MapBlock *block = m_map->getBlockNoCreateNoEx(p);
                if(block == NULL)
                {