tweaking mapgenv2 settings for maximum awesomeness.
[oweals/minetest.git] / src / map.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "map.h"
21 #include "main.h"
22 #include "jmutexautolock.h"
23 #include "client.h"
24 #include "filesys.h"
25 #include "utility.h"
26 #include "voxel.h"
27 #include "porting.h"
28 #include "mineral.h"
29
30 /*
31         Map
32 */
33
34 Map::Map(std::ostream &dout):
35         m_dout(dout),
36         m_camera_position(0,0,0),
37         m_camera_direction(0,0,1),
38         m_sector_cache(NULL),
39         m_hwrapper(this)
40 {
41         m_sector_mutex.Init();
42         m_camera_mutex.Init();
43         assert(m_sector_mutex.IsInitialized());
44         assert(m_camera_mutex.IsInitialized());
45         
46         // Get this so that the player can stay on it at first
47         //getSector(v2s16(0,0));
48 }
49
50 Map::~Map()
51 {
52         /*
53                 Stop updater thread
54         */
55         /*updater.setRun(false);
56         while(updater.IsRunning())
57                 sleep_s(1);*/
58
59         /*
60                 Free all MapSectors.
61         */
62         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
63         for(; i.atEnd() == false; i++)
64         {
65                 MapSector *sector = i.getNode()->getValue();
66                 delete sector;
67         }
68 }
69
70 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
71 {
72         if(m_sector_cache != NULL && p == m_sector_cache_p){
73                 MapSector * sector = m_sector_cache;
74                 // Reset inactivity timer
75                 sector->usage_timer = 0.0;
76                 return sector;
77         }
78         
79         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
80         
81         if(n == NULL)
82                 return NULL;
83         
84         MapSector *sector = n->getValue();
85         
86         // Cache the last result
87         m_sector_cache_p = p;
88         m_sector_cache = sector;
89
90         // Reset inactivity timer
91         sector->usage_timer = 0.0;
92         return sector;
93 }
94
95 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
96 {
97         JMutexAutoLock lock(m_sector_mutex);
98
99         return getSectorNoGenerateNoExNoLock(p);
100 }
101
102 MapSector * Map::getSectorNoGenerate(v2s16 p)
103 {
104         MapSector *sector = getSectorNoGenerateNoEx(p);
105         if(sector == NULL)
106                 throw InvalidPositionException();
107         
108         return sector;
109 }
110
111 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
112 {       
113         v2s16 p2d(p3d.X, p3d.Z);
114         MapSector * sector = getSectorNoGenerate(p2d);
115
116         MapBlock *block = sector->getBlockNoCreate(p3d.Y);
117
118         return block;
119 }
120
121 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
122 {
123         try
124         {
125                 v2s16 p2d(p3d.X, p3d.Z);
126                 MapSector * sector = getSectorNoGenerate(p2d);
127                 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
128                 return block;
129         }
130         catch(InvalidPositionException &e)
131         {
132                 return NULL;
133         }
134 }
135
136 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
137 {
138         v2s16 p2d(p3d.X, p3d.Z);
139         MapSector * sector = getSectorCreate(p2d);
140         assert(sector);
141         MapBlock *block = sector->getBlockNoCreate(p3d.Y);
142         if(block)
143                 return block;
144         block = sector->createBlankBlock(p3d.Y);
145         return block;
146 }*/
147
148 f32 Map::getGroundHeight(v2s16 p, bool generate)
149 {
150         try{
151                 v2s16 sectorpos = getNodeSectorPos(p);
152                 MapSector * sref = getSectorNoGenerate(sectorpos);
153                 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
154                 f32 y = sref->getGroundHeight(relpos);
155                 return y;
156         }
157         catch(InvalidPositionException &e)
158         {
159                 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
160         }
161 }
162
163 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
164 {
165         /*m_dout<<DTIME<<"Map::setGroundHeight(("
166                         <<p.X<<","<<p.Y
167                         <<"), "<<y<<")"<<std::endl;*/
168         v2s16 sectorpos = getNodeSectorPos(p);
169         MapSector * sref = getSectorNoGenerate(sectorpos);
170         v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
171         //sref->mutex.Lock();
172         sref->setGroundHeight(relpos, y);
173         //sref->mutex.Unlock();
174 }
175
176 bool Map::isNodeUnderground(v3s16 p)
177 {
178         v3s16 blockpos = getNodeBlockPos(p);
179         try{
180                 MapBlock * block = getBlockNoCreate(blockpos);
181                 return block->getIsUnderground();
182         }
183         catch(InvalidPositionException &e)
184         {
185                 return false;
186         }
187 }
188
189 /*
190         Goes recursively through the neighbours of the node.
191
192         Alters only transparent nodes.
193
194         If the lighting of the neighbour is lower than the lighting of
195         the node was (before changing it to 0 at the step before), the
196         lighting of the neighbour is set to 0 and then the same stuff
197         repeats for the neighbour.
198
199         The ending nodes of the routine are stored in light_sources.
200         This is useful when a light is removed. In such case, this
201         routine can be called for the light node and then again for
202         light_sources to re-light the area without the removed light.
203
204         values of from_nodes are lighting values.
205 */
206 void Map::unspreadLight(enum LightBank bank,
207                 core::map<v3s16, u8> & from_nodes,
208                 core::map<v3s16, bool> & light_sources,
209                 core::map<v3s16, MapBlock*>  & modified_blocks)
210 {
211         v3s16 dirs[6] = {
212                 v3s16(0,0,1), // back
213                 v3s16(0,1,0), // top
214                 v3s16(1,0,0), // right
215                 v3s16(0,0,-1), // front
216                 v3s16(0,-1,0), // bottom
217                 v3s16(-1,0,0), // left
218         };
219         
220         if(from_nodes.size() == 0)
221                 return;
222         
223         u32 blockchangecount = 0;
224
225         core::map<v3s16, u8> unlighted_nodes;
226         core::map<v3s16, u8>::Iterator j;
227         j = from_nodes.getIterator();
228
229         /*
230                 Initialize block cache
231         */
232         v3s16 blockpos_last;
233         MapBlock *block = NULL;
234         // Cache this a bit, too
235         bool block_checked_in_modified = false;
236         
237         for(; j.atEnd() == false; j++)
238         {
239                 v3s16 pos = j.getNode()->getKey();
240                 v3s16 blockpos = getNodeBlockPos(pos);
241                 
242                 // Only fetch a new block if the block position has changed
243                 try{
244                         if(block == NULL || blockpos != blockpos_last){
245                                 block = getBlockNoCreate(blockpos);
246                                 blockpos_last = blockpos;
247
248                                 block_checked_in_modified = false;
249                                 blockchangecount++;
250                         }
251                 }
252                 catch(InvalidPositionException &e)
253                 {
254                         continue;
255                 }
256
257                 if(block->isDummy())
258                         continue;
259
260                 // Calculate relative position in block
261                 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
262
263                 // Get node straight from the block
264                 MapNode n = block->getNode(relpos);
265                 
266                 u8 oldlight = j.getNode()->getValue();
267                 
268                 // Loop through 6 neighbors
269                 for(u16 i=0; i<6; i++)
270                 {
271                         // Get the position of the neighbor node
272                         v3s16 n2pos = pos + dirs[i];
273                         
274                         // Get the block where the node is located
275                         v3s16 blockpos = getNodeBlockPos(n2pos);
276
277                         try
278                         {
279                                 // Only fetch a new block if the block position has changed
280                                 try{
281                                         if(block == NULL || blockpos != blockpos_last){
282                                                 block = getBlockNoCreate(blockpos);
283                                                 blockpos_last = blockpos;
284
285                                                 block_checked_in_modified = false;
286                                                 blockchangecount++;
287                                         }
288                                 }
289                                 catch(InvalidPositionException &e)
290                                 {
291                                         continue;
292                                 }
293                                 
294                                 // Calculate relative position in block
295                                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
296                                 // Get node straight from the block
297                                 MapNode n2 = block->getNode(relpos);
298                                 
299                                 bool changed = false;
300
301                                 //TODO: Optimize output by optimizing light_sources?
302
303                                 /*
304                                         If the neighbor is dimmer than what was specified
305                                         as oldlight (the light of the previous node)
306                                 */
307                                 if(n2.getLight(bank) < oldlight)
308                                 {
309                                         /*
310                                                 And the neighbor is transparent and it has some light
311                                         */
312                                         if(n2.light_propagates() && n2.getLight(bank) != 0)
313                                         {
314                                                 /*
315                                                         Set light to 0 and add to queue
316                                                 */
317
318                                                 u8 current_light = n2.getLight(bank);
319                                                 n2.setLight(bank, 0);
320                                                 block->setNode(relpos, n2);
321
322                                                 unlighted_nodes.insert(n2pos, current_light);
323                                                 changed = true;
324
325                                                 /*
326                                                         Remove from light_sources if it is there
327                                                         NOTE: This doesn't happen nearly at all
328                                                 */
329                                                 /*if(light_sources.find(n2pos))
330                                                 {
331                                                         std::cout<<"Removed from light_sources"<<std::endl;
332                                                         light_sources.remove(n2pos);
333                                                 }*/
334                                         }
335                                         
336                                         /*// DEBUG
337                                         if(light_sources.find(n2pos) != NULL)
338                                                 light_sources.remove(n2pos);*/
339                                 }
340                                 else{
341                                         light_sources.insert(n2pos, true);
342                                 }
343
344                                 // Add to modified_blocks
345                                 if(changed == true && block_checked_in_modified == false)
346                                 {
347                                         // If the block is not found in modified_blocks, add.
348                                         if(modified_blocks.find(blockpos) == NULL)
349                                         {
350                                                 modified_blocks.insert(blockpos, block);
351                                         }
352                                         block_checked_in_modified = true;
353                                 }
354                         }
355                         catch(InvalidPositionException &e)
356                         {
357                                 continue;
358                         }
359                 }
360         }
361
362         /*dstream<<"unspreadLight(): Changed block "
363                         <<blockchangecount<<" times"
364                         <<" for "<<from_nodes.size()<<" nodes"
365                         <<std::endl;*/
366         
367         if(unlighted_nodes.size() > 0)
368                 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
369 }
370
371 /*
372         A single-node wrapper of the above
373 */
374 void Map::unLightNeighbors(enum LightBank bank,
375                 v3s16 pos, u8 lightwas,
376                 core::map<v3s16, bool> & light_sources,
377                 core::map<v3s16, MapBlock*>  & modified_blocks)
378 {
379         core::map<v3s16, u8> from_nodes;
380         from_nodes.insert(pos, lightwas);
381
382         unspreadLight(bank, from_nodes, light_sources, modified_blocks);
383 }
384
385 /*
386         Lights neighbors of from_nodes, collects all them and then
387         goes on recursively.
388 */
389 void Map::spreadLight(enum LightBank bank,
390                 core::map<v3s16, bool> & from_nodes,
391                 core::map<v3s16, MapBlock*> & modified_blocks)
392 {
393         const v3s16 dirs[6] = {
394                 v3s16(0,0,1), // back
395                 v3s16(0,1,0), // top
396                 v3s16(1,0,0), // right
397                 v3s16(0,0,-1), // front
398                 v3s16(0,-1,0), // bottom
399                 v3s16(-1,0,0), // left
400         };
401
402         if(from_nodes.size() == 0)
403                 return;
404         
405         u32 blockchangecount = 0;
406
407         core::map<v3s16, bool> lighted_nodes;
408         core::map<v3s16, bool>::Iterator j;
409         j = from_nodes.getIterator();
410
411         /*
412                 Initialize block cache
413         */
414         v3s16 blockpos_last;
415         MapBlock *block = NULL;
416         // Cache this a bit, too
417         bool block_checked_in_modified = false;
418         
419         for(; j.atEnd() == false; j++)
420         //for(; j != from_nodes.end(); j++)
421         {
422                 v3s16 pos = j.getNode()->getKey();
423                 //v3s16 pos = *j;
424                 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
425                 v3s16 blockpos = getNodeBlockPos(pos);
426                 
427                 // Only fetch a new block if the block position has changed
428                 try{
429                         if(block == NULL || blockpos != blockpos_last){
430                                 block = getBlockNoCreate(blockpos);
431                                 blockpos_last = blockpos;
432
433                                 block_checked_in_modified = false;
434                                 blockchangecount++;
435                         }
436                 }
437                 catch(InvalidPositionException &e)
438                 {
439                         continue;
440                 }
441
442                 if(block->isDummy())
443                         continue;
444
445                 // Calculate relative position in block
446                 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
447
448                 // Get node straight from the block
449                 MapNode n = block->getNode(relpos);
450
451                 u8 oldlight = n.getLight(bank);
452                 u8 newlight = diminish_light(oldlight);
453
454                 // Loop through 6 neighbors
455                 for(u16 i=0; i<6; i++){
456                         // Get the position of the neighbor node
457                         v3s16 n2pos = pos + dirs[i];
458                         
459                         // Get the block where the node is located
460                         v3s16 blockpos = getNodeBlockPos(n2pos);
461
462                         try
463                         {
464                                 // Only fetch a new block if the block position has changed
465                                 try{
466                                         if(block == NULL || blockpos != blockpos_last){
467                                                 block = getBlockNoCreate(blockpos);
468                                                 blockpos_last = blockpos;
469
470                                                 block_checked_in_modified = false;
471                                                 blockchangecount++;
472                                         }
473                                 }
474                                 catch(InvalidPositionException &e)
475                                 {
476                                         continue;
477                                 }
478                                 
479                                 // Calculate relative position in block
480                                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
481                                 // Get node straight from the block
482                                 MapNode n2 = block->getNode(relpos);
483                                 
484                                 bool changed = false;
485                                 /*
486                                         If the neighbor is brighter than the current node,
487                                         add to list (it will light up this node on its turn)
488                                 */
489                                 if(n2.getLight(bank) > undiminish_light(oldlight))
490                                 {
491                                         lighted_nodes.insert(n2pos, true);
492                                         //lighted_nodes.push_back(n2pos);
493                                         changed = true;
494                                 }
495                                 /*
496                                         If the neighbor is dimmer than how much light this node
497                                         would spread on it, add to list
498                                 */
499                                 if(n2.getLight(bank) < newlight)
500                                 {
501                                         if(n2.light_propagates())
502                                         {
503                                                 n2.setLight(bank, newlight);
504                                                 block->setNode(relpos, n2);
505                                                 lighted_nodes.insert(n2pos, true);
506                                                 //lighted_nodes.push_back(n2pos);
507                                                 changed = true;
508                                         }
509                                 }
510
511                                 // Add to modified_blocks
512                                 if(changed == true && block_checked_in_modified == false)
513                                 {
514                                         // If the block is not found in modified_blocks, add.
515                                         if(modified_blocks.find(blockpos) == NULL)
516                                         {
517                                                 modified_blocks.insert(blockpos, block);
518                                         }
519                                         block_checked_in_modified = true;
520                                 }
521                         }
522                         catch(InvalidPositionException &e)
523                         {
524                                 continue;
525                         }
526                 }
527         }
528
529         /*dstream<<"spreadLight(): Changed block "
530                         <<blockchangecount<<" times"
531                         <<" for "<<from_nodes.size()<<" nodes"
532                         <<std::endl;*/
533         
534         if(lighted_nodes.size() > 0)
535                 spreadLight(bank, lighted_nodes, modified_blocks);
536 }
537
538 /*
539         A single-node source variation of the above.
540 */
541 void Map::lightNeighbors(enum LightBank bank,
542                 v3s16 pos,
543                 core::map<v3s16, MapBlock*> & modified_blocks)
544 {
545         core::map<v3s16, bool> from_nodes;
546         from_nodes.insert(pos, true);
547         spreadLight(bank, from_nodes, modified_blocks);
548 }
549
550 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
551 {
552         v3s16 dirs[6] = {
553                 v3s16(0,0,1), // back
554                 v3s16(0,1,0), // top
555                 v3s16(1,0,0), // right
556                 v3s16(0,0,-1), // front
557                 v3s16(0,-1,0), // bottom
558                 v3s16(-1,0,0), // left
559         };
560         
561         u8 brightest_light = 0;
562         v3s16 brightest_pos(0,0,0);
563         bool found_something = false;
564
565         // Loop through 6 neighbors
566         for(u16 i=0; i<6; i++){
567                 // Get the position of the neighbor node
568                 v3s16 n2pos = p + dirs[i];
569                 MapNode n2;
570                 try{
571                         n2 = getNode(n2pos);
572                 }
573                 catch(InvalidPositionException &e)
574                 {
575                         continue;
576                 }
577                 if(n2.getLight(bank) > brightest_light || found_something == false){
578                         brightest_light = n2.getLight(bank);
579                         brightest_pos = n2pos;
580                         found_something = true;
581                 }
582         }
583
584         if(found_something == false)
585                 throw InvalidPositionException();
586                 
587         return brightest_pos;
588 }
589
590 /*
591         Propagates sunlight down from a node.
592         Starting point gets sunlight.
593
594         Returns the lowest y value of where the sunlight went.
595
596         Mud is turned into grass in where the sunlight stops.
597 */
598 s16 Map::propagateSunlight(v3s16 start,
599                 core::map<v3s16, MapBlock*> & modified_blocks)
600 {
601         s16 y = start.Y;
602         for(; ; y--)
603         {
604                 v3s16 pos(start.X, y, start.Z);
605                 
606                 v3s16 blockpos = getNodeBlockPos(pos);
607                 MapBlock *block;
608                 try{
609                         block = getBlockNoCreate(blockpos);
610                 }
611                 catch(InvalidPositionException &e)
612                 {
613                         break;
614                 }
615
616                 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
617                 MapNode n = block->getNode(relpos);
618
619                 if(n.sunlight_propagates())
620                 {
621                         n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
622                         block->setNode(relpos, n);
623
624                         modified_blocks.insert(blockpos, block);
625                 }
626                 else
627                 {
628                         // Turn mud into grass
629                         if(n.d == CONTENT_MUD)
630                         {
631                                 n.d = CONTENT_GRASS;
632                                 block->setNode(relpos, n);
633                                 modified_blocks.insert(blockpos, block);
634                         }
635
636                         // Sunlight goes no further
637                         break;
638                 }
639         }
640         return y + 1;
641 }
642
643 void Map::updateLighting(enum LightBank bank,
644                 core::map<v3s16, MapBlock*> & a_blocks,
645                 core::map<v3s16, MapBlock*> & modified_blocks)
646 {
647         /*m_dout<<DTIME<<"Map::updateLighting(): "
648                         <<a_blocks.size()<<" blocks."<<std::endl;*/
649         
650         //TimeTaker timer("updateLighting");
651         
652         // For debugging
653         //bool debug=true;
654         //u32 count_was = modified_blocks.size();
655         
656         core::map<v3s16, MapBlock*> blocks_to_update;
657
658         core::map<v3s16, bool> light_sources;
659         
660         core::map<v3s16, u8> unlight_from;
661                 
662         core::map<v3s16, MapBlock*>::Iterator i;
663         i = a_blocks.getIterator();
664         for(; i.atEnd() == false; i++)
665         {
666                 MapBlock *block = i.getNode()->getValue();
667                 
668                 for(;;)
669                 {
670                         // Don't bother with dummy blocks.
671                         if(block->isDummy())
672                                 break;
673                 
674                         v3s16 pos = block->getPos();
675                         modified_blocks.insert(pos, block);
676
677                         blocks_to_update.insert(pos, block);
678
679                         /*
680                                 Clear all light from block
681                         */
682                         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
683                         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
684                         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
685                         {
686                                 
687                                 try{
688                                         v3s16 p(x,y,z);
689                                         MapNode n = block->getNode(v3s16(x,y,z));
690                                         u8 oldlight = n.getLight(bank);
691                                         n.setLight(bank, 0);
692                                         block->setNode(v3s16(x,y,z), n);
693                                         
694                                         // Collect borders for unlighting
695                                         if(x==0 || x == MAP_BLOCKSIZE-1
696                                         || y==0 || y == MAP_BLOCKSIZE-1
697                                         || z==0 || z == MAP_BLOCKSIZE-1)
698                                         {
699                                                 v3s16 p_map = p + v3s16(
700                                                                 MAP_BLOCKSIZE*pos.X,
701                                                                 MAP_BLOCKSIZE*pos.Y,
702                                                                 MAP_BLOCKSIZE*pos.Z);
703                                                 unlight_from.insert(p_map, oldlight);
704                                         }
705                                 }
706                                 catch(InvalidPositionException &e)
707                                 {
708                                         /*
709                                                 This would happen when dealing with a
710                                                 dummy block.
711                                         */
712                                         //assert(0);
713                                         dstream<<"updateLighting(): InvalidPositionException"
714                                                         <<std::endl;
715                                 }
716                         }
717                         
718                         if(bank == LIGHTBANK_DAY)
719                         {
720                                 bool bottom_valid = block->propagateSunlight(light_sources);
721
722                                 // If bottom is valid, we're done.
723                                 if(bottom_valid)
724                                         break;
725                         }
726                         else if(bank == LIGHTBANK_NIGHT)
727                         {
728                                 // For night lighting, sunlight is not propagated
729                                 break;
730                         }
731                         else
732                         {
733                                 // Invalid lighting bank
734                                 assert(0);
735                         }
736                                 
737                         /*dstream<<"Bottom for sunlight-propagated block ("
738                                         <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
739                                         <<std::endl;*/
740
741                         // Bottom sunlight is not valid; get the block and loop to it
742
743                         pos.Y--;
744                         try{
745                                 block = getBlockNoCreate(pos);
746                         }
747                         catch(InvalidPositionException &e)
748                         {
749                                 assert(0);
750                         }
751                         
752                 }
753         }
754
755 #if 0
756         {
757                 TimeTaker timer("unspreadLight");
758                 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
759         }
760         
761         if(debug)
762         {
763                 u32 diff = modified_blocks.size() - count_was;
764                 count_was = modified_blocks.size();
765                 dstream<<"unspreadLight modified "<<diff<<std::endl;
766         }
767
768         {
769                 TimeTaker timer("spreadLight");
770                 spreadLight(bank, light_sources, modified_blocks);
771         }
772         
773         if(debug)
774         {
775                 u32 diff = modified_blocks.size() - count_was;
776                 count_was = modified_blocks.size();
777                 dstream<<"spreadLight modified "<<diff<<std::endl;
778         }
779 #endif
780         
781         {
782                 //MapVoxelManipulator vmanip(this);
783                 
784                 // Make a manual voxel manipulator and load all the blocks
785                 // that touch the requested blocks
786                 ManualMapVoxelManipulator vmanip(this);
787                 core::map<v3s16, MapBlock*>::Iterator i;
788                 i = blocks_to_update.getIterator();
789                 for(; i.atEnd() == false; i++)
790                 {
791                         MapBlock *block = i.getNode()->getValue();
792                         v3s16 p = block->getPos();
793                         
794                         // Add all surrounding blocks
795                         vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
796
797                         /*
798                                 Add all surrounding blocks that have up-to-date lighting
799                                 NOTE: This doesn't quite do the job (not everything
800                                       appropriate is lighted)
801                         */
802                         /*for(s16 z=-1; z<=1; z++)
803                         for(s16 y=-1; y<=1; y++)
804                         for(s16 x=-1; x<=1; x++)
805                         {
806                                 v3s16 p(x,y,z);
807                                 MapBlock *block = getBlockNoCreateNoEx(p);
808                                 if(block == NULL)
809                                         continue;
810                                 if(block->isDummy())
811                                         continue;
812                                 if(block->getLightingExpired())
813                                         continue;
814                                 vmanip.initialEmerge(p, p);
815                         }*/
816                         
817                         // Lighting of block will be updated completely
818                         block->setLightingExpired(false);
819                 }
820
821                 {
822                         //TimeTaker timer("unSpreadLight");
823                         vmanip.unspreadLight(bank, unlight_from, light_sources);
824                 }
825                 {
826                         //TimeTaker timer("spreadLight");
827                         vmanip.spreadLight(bank, light_sources);
828                 }
829                 {
830                         //TimeTaker timer("blitBack");
831                         vmanip.blitBack(modified_blocks);
832                 }
833                 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
834                 emerge_time = 0;*/
835         }
836
837         //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
838 }
839
840 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
841                 core::map<v3s16, MapBlock*> & modified_blocks)
842 {
843         updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
844         updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
845         
846         /*
847                 Update information about whether day and night light differ
848         */
849         for(core::map<v3s16, MapBlock*>::Iterator
850                         i = modified_blocks.getIterator();
851                         i.atEnd() == false; i++)
852         {
853                 MapBlock *block = i.getNode()->getValue();
854                 block->updateDayNightDiff();
855         }
856 }
857
858 /*
859         This is called after changing a node from transparent to opaque.
860         The lighting value of the node should be left as-is after changing
861         other values. This sets the lighting value to 0.
862 */
863 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
864                 core::map<v3s16, MapBlock*> &modified_blocks)*/
865 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
866                 core::map<v3s16, MapBlock*> &modified_blocks)
867 {
868         /*PrintInfo(m_dout);
869         m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
870                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
871
872         /*
873                 From this node to nodes underneath:
874                 If lighting is sunlight (1.0), unlight neighbours and
875                 set lighting to 0.
876                 Else discontinue.
877         */
878
879         v3s16 toppos = p + v3s16(0,1,0);
880         v3s16 bottompos = p + v3s16(0,-1,0);
881
882         bool node_under_sunlight = true;
883         core::map<v3s16, bool> light_sources;
884
885         /*
886                 If there is a node at top and it doesn't have sunlight,
887                 there has not been any sunlight going down.
888
889                 Otherwise there probably is.
890         */
891         try{
892                 MapNode topnode = getNode(toppos);
893
894                 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
895                         node_under_sunlight = false;
896         }
897         catch(InvalidPositionException &e)
898         {
899         }
900         
901         if(n.d != CONTENT_TORCH)
902         {
903                 /*
904                         If there is grass below, change it to mud
905                 */
906                 try{
907                         MapNode bottomnode = getNode(bottompos);
908                         
909                         if(bottomnode.d == CONTENT_GRASS
910                                         || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
911                         {
912                                 bottomnode.d = CONTENT_MUD;
913                                 setNode(bottompos, bottomnode);
914                         }
915                 }
916                 catch(InvalidPositionException &e)
917                 {
918                 }
919         }
920
921         enum LightBank banks[] =
922         {
923                 LIGHTBANK_DAY,
924                 LIGHTBANK_NIGHT
925         };
926         for(s32 i=0; i<2; i++)
927         {
928                 enum LightBank bank = banks[i];
929
930                 u8 lightwas = getNode(p).getLight(bank);
931
932                 // Add the block of the added node to modified_blocks
933                 v3s16 blockpos = getNodeBlockPos(p);
934                 MapBlock * block = getBlockNoCreate(blockpos);
935                 assert(block != NULL);
936                 modified_blocks.insert(blockpos, block);
937                 
938                 if(isValidPosition(p) == false)
939                         throw;
940                         
941                 // Unlight neighbours of node.
942                 // This means setting light of all consequent dimmer nodes
943                 // to 0.
944                 // This also collects the nodes at the border which will spread
945                 // light again into this.
946                 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
947
948                 n.setLight(bank, 0);
949         }
950         
951         setNode(p, n);
952         
953         /*
954                 If node is under sunlight, take all sunlighted nodes under
955                 it and clear light from them and from where the light has
956                 been spread.
957                 TODO: This could be optimized by mass-unlighting instead
958                       of looping
959         */
960         if(node_under_sunlight)
961         {
962                 s16 y = p.Y - 1;
963                 for(;; y--){
964                         //m_dout<<DTIME<<"y="<<y<<std::endl;
965                         v3s16 n2pos(p.X, y, p.Z);
966                         
967                         MapNode n2;
968                         try{
969                                 n2 = getNode(n2pos);
970                         }
971                         catch(InvalidPositionException &e)
972                         {
973                                 break;
974                         }
975
976                         if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
977                         {
978                                 //m_dout<<DTIME<<"doing"<<std::endl;
979                                 unLightNeighbors(LIGHTBANK_DAY,
980                                                 n2pos, n2.getLight(LIGHTBANK_DAY),
981                                                 light_sources, modified_blocks);
982                                 n2.setLight(LIGHTBANK_DAY, 0);
983                                 setNode(n2pos, n2);
984                         }
985                         else
986                                 break;
987                 }
988         }
989         
990         for(s32 i=0; i<2; i++)
991         {
992                 enum LightBank bank = banks[i];
993                 
994                 /*
995                         Spread light from all nodes that might be capable of doing so
996                         TODO: Convert to spreadLight
997                 */
998                 spreadLight(bank, light_sources, modified_blocks);
999         }
1000
1001         /*
1002                 Update information about whether day and night light differ
1003         */
1004         for(core::map<v3s16, MapBlock*>::Iterator
1005                         i = modified_blocks.getIterator();
1006                         i.atEnd() == false; i++)
1007         {
1008                 MapBlock *block = i.getNode()->getValue();
1009                 block->updateDayNightDiff();
1010         }
1011
1012         /*
1013                 Add neighboring liquid nodes and the node itself if it is
1014                 liquid (=water node was added) to transform queue.
1015         */
1016         v3s16 dirs[7] = {
1017                 v3s16(0,0,0), // self
1018                 v3s16(0,0,1), // back
1019                 v3s16(0,1,0), // top
1020                 v3s16(1,0,0), // right
1021                 v3s16(0,0,-1), // front
1022                 v3s16(0,-1,0), // bottom
1023                 v3s16(-1,0,0), // left
1024         };
1025         for(u16 i=0; i<7; i++)
1026         {
1027                 try
1028                 {
1029
1030                 v3s16 p2 = p + dirs[i];
1031                 
1032                 MapNode n2 = getNode(p2);
1033                 if(content_liquid(n2.d))
1034                 {
1035                         m_transforming_liquid.push_back(p2);
1036                 }
1037                 
1038                 }catch(InvalidPositionException &e)
1039                 {
1040                 }
1041         }
1042 }
1043
1044 /*
1045 */
1046 void Map::removeNodeAndUpdate(v3s16 p,
1047                 core::map<v3s16, MapBlock*> &modified_blocks)
1048 {
1049         /*PrintInfo(m_dout);
1050         m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1051                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1052         
1053         bool node_under_sunlight = true;
1054         
1055         v3s16 toppos = p + v3s16(0,1,0);
1056
1057         // Node will be replaced with this
1058         u8 replace_material = CONTENT_AIR;
1059         
1060         /*
1061                 If there is a node at top and it doesn't have sunlight,
1062                 there will be no sunlight going down.
1063         */
1064         try{
1065                 MapNode topnode = getNode(toppos);
1066
1067                 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1068                         node_under_sunlight = false;
1069         }
1070         catch(InvalidPositionException &e)
1071         {
1072         }
1073
1074         core::map<v3s16, bool> light_sources;
1075
1076         enum LightBank banks[] =
1077         {
1078                 LIGHTBANK_DAY,
1079                 LIGHTBANK_NIGHT
1080         };
1081         for(s32 i=0; i<2; i++)
1082         {
1083                 enum LightBank bank = banks[i];
1084         
1085                 /*
1086                         Unlight neighbors (in case the node is a light source)
1087                 */
1088                 unLightNeighbors(bank, p,
1089                                 getNode(p).getLight(bank),
1090                                 light_sources, modified_blocks);
1091         }
1092
1093         /*
1094                 Remove the node.
1095                 This also clears the lighting.
1096         */
1097
1098         MapNode n;
1099         n.d = replace_material;
1100         setNode(p, n);
1101         
1102         for(s32 i=0; i<2; i++)
1103         {
1104                 enum LightBank bank = banks[i];
1105         
1106                 /*
1107                         Recalculate lighting
1108                 */
1109                 spreadLight(bank, light_sources, modified_blocks);
1110         }
1111
1112         // Add the block of the removed node to modified_blocks
1113         v3s16 blockpos = getNodeBlockPos(p);
1114         MapBlock * block = getBlockNoCreate(blockpos);
1115         assert(block != NULL);
1116         modified_blocks.insert(blockpos, block);
1117
1118         /*
1119                 If the removed node was under sunlight, propagate the
1120                 sunlight down from it and then light all neighbors
1121                 of the propagated blocks.
1122         */
1123         if(node_under_sunlight)
1124         {
1125                 s16 ybottom = propagateSunlight(p, modified_blocks);
1126                 /*m_dout<<DTIME<<"Node was under sunlight. "
1127                                 "Propagating sunlight";
1128                 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1129                 s16 y = p.Y;
1130                 for(; y >= ybottom; y--)
1131                 {
1132                         v3s16 p2(p.X, y, p.Z);
1133                         /*m_dout<<DTIME<<"lighting neighbors of node ("
1134                                         <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1135                                         <<std::endl;*/
1136                         lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1137                 }
1138         }
1139         else
1140         {
1141                 // Set the lighting of this node to 0
1142                 // TODO: Is this needed? Lighting is cleared up there already.
1143                 try{
1144                         MapNode n = getNode(p);
1145                         n.setLight(LIGHTBANK_DAY, 0);
1146                         setNode(p, n);
1147                 }
1148                 catch(InvalidPositionException &e)
1149                 {
1150                         throw;
1151                 }
1152         }
1153
1154         for(s32 i=0; i<2; i++)
1155         {
1156                 enum LightBank bank = banks[i];
1157         
1158                 // Get the brightest neighbour node and propagate light from it
1159                 v3s16 n2p = getBrightestNeighbour(bank, p);
1160                 try{
1161                         MapNode n2 = getNode(n2p);
1162                         lightNeighbors(bank, n2p, modified_blocks);
1163                 }
1164                 catch(InvalidPositionException &e)
1165                 {
1166                 }
1167         }
1168
1169         /*
1170                 Update information about whether day and night light differ
1171         */
1172         for(core::map<v3s16, MapBlock*>::Iterator
1173                         i = modified_blocks.getIterator();
1174                         i.atEnd() == false; i++)
1175         {
1176                 MapBlock *block = i.getNode()->getValue();
1177                 block->updateDayNightDiff();
1178         }
1179
1180         /*
1181                 Add neighboring liquid nodes to transform queue.
1182         */
1183         v3s16 dirs[6] = {
1184                 v3s16(0,0,1), // back
1185                 v3s16(0,1,0), // top
1186                 v3s16(1,0,0), // right
1187                 v3s16(0,0,-1), // front
1188                 v3s16(0,-1,0), // bottom
1189                 v3s16(-1,0,0), // left
1190         };
1191         for(u16 i=0; i<6; i++)
1192         {
1193                 try
1194                 {
1195
1196                 v3s16 p2 = p + dirs[i];
1197                 
1198                 MapNode n2 = getNode(p2);
1199                 if(content_liquid(n2.d))
1200                 {
1201                         m_transforming_liquid.push_back(p2);
1202                 }
1203                 
1204                 }catch(InvalidPositionException &e)
1205                 {
1206                 }
1207         }
1208 }
1209
1210 #ifndef SERVER
1211 void Map::expireMeshes(bool only_daynight_diffed)
1212 {
1213         TimeTaker timer("expireMeshes()");
1214
1215         core::map<v2s16, MapSector*>::Iterator si;
1216         si = m_sectors.getIterator();
1217         for(; si.atEnd() == false; si++)
1218         {
1219                 MapSector *sector = si.getNode()->getValue();
1220
1221                 core::list< MapBlock * > sectorblocks;
1222                 sector->getBlocks(sectorblocks);
1223                 
1224                 core::list< MapBlock * >::Iterator i;
1225                 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1226                 {
1227                         MapBlock *block = *i;
1228
1229                         if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1230                         {
1231                                 continue;
1232                         }
1233                         
1234                         {
1235                                 JMutexAutoLock lock(block->mesh_mutex);
1236                                 if(block->mesh != NULL)
1237                                 {
1238                                         /*block->mesh->drop();
1239                                         block->mesh = NULL;*/
1240                                         block->setMeshExpired(true);
1241                                 }
1242                         }
1243                 }
1244         }
1245 }
1246
1247 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1248 {
1249         assert(mapType() == MAPTYPE_CLIENT);
1250
1251         try{
1252                 v3s16 p = blockpos + v3s16(0,0,0);
1253                 MapBlock *b = getBlockNoCreate(p);
1254                 b->updateMesh(daynight_ratio);
1255         }
1256         catch(InvalidPositionException &e){}
1257         // Leading edge
1258         try{
1259                 v3s16 p = blockpos + v3s16(-1,0,0);
1260                 MapBlock *b = getBlockNoCreate(p);
1261                 b->updateMesh(daynight_ratio);
1262         }
1263         catch(InvalidPositionException &e){}
1264         try{
1265                 v3s16 p = blockpos + v3s16(0,-1,0);
1266                 MapBlock *b = getBlockNoCreate(p);
1267                 b->updateMesh(daynight_ratio);
1268         }
1269         catch(InvalidPositionException &e){}
1270         try{
1271                 v3s16 p = blockpos + v3s16(0,0,-1);
1272                 MapBlock *b = getBlockNoCreate(p);
1273                 b->updateMesh(daynight_ratio);
1274         }
1275         catch(InvalidPositionException &e){}
1276         /*// Trailing edge
1277         try{
1278                 v3s16 p = blockpos + v3s16(1,0,0);
1279                 MapBlock *b = getBlockNoCreate(p);
1280                 b->updateMesh(daynight_ratio);
1281         }
1282         catch(InvalidPositionException &e){}
1283         try{
1284                 v3s16 p = blockpos + v3s16(0,1,0);
1285                 MapBlock *b = getBlockNoCreate(p);
1286                 b->updateMesh(daynight_ratio);
1287         }
1288         catch(InvalidPositionException &e){}
1289         try{
1290                 v3s16 p = blockpos + v3s16(0,0,1);
1291                 MapBlock *b = getBlockNoCreate(p);
1292                 b->updateMesh(daynight_ratio);
1293         }
1294         catch(InvalidPositionException &e){}*/
1295 }
1296
1297 #endif
1298
1299 bool Map::dayNightDiffed(v3s16 blockpos)
1300 {
1301         try{
1302                 v3s16 p = blockpos + v3s16(0,0,0);
1303                 MapBlock *b = getBlockNoCreate(p);
1304                 if(b->dayNightDiffed())
1305                         return true;
1306         }
1307         catch(InvalidPositionException &e){}
1308         // Leading edges
1309         try{
1310                 v3s16 p = blockpos + v3s16(-1,0,0);
1311                 MapBlock *b = getBlockNoCreate(p);
1312                 if(b->dayNightDiffed())
1313                         return true;
1314         }
1315         catch(InvalidPositionException &e){}
1316         try{
1317                 v3s16 p = blockpos + v3s16(0,-1,0);
1318                 MapBlock *b = getBlockNoCreate(p);
1319                 if(b->dayNightDiffed())
1320                         return true;
1321         }
1322         catch(InvalidPositionException &e){}
1323         try{
1324                 v3s16 p = blockpos + v3s16(0,0,-1);
1325                 MapBlock *b = getBlockNoCreate(p);
1326                 if(b->dayNightDiffed())
1327                         return true;
1328         }
1329         catch(InvalidPositionException &e){}
1330         // Trailing edges
1331         try{
1332                 v3s16 p = blockpos + v3s16(1,0,0);
1333                 MapBlock *b = getBlockNoCreate(p);
1334                 if(b->dayNightDiffed())
1335                         return true;
1336         }
1337         catch(InvalidPositionException &e){}
1338         try{
1339                 v3s16 p = blockpos + v3s16(0,1,0);
1340                 MapBlock *b = getBlockNoCreate(p);
1341                 if(b->dayNightDiffed())
1342                         return true;
1343         }
1344         catch(InvalidPositionException &e){}
1345         try{
1346                 v3s16 p = blockpos + v3s16(0,0,1);
1347                 MapBlock *b = getBlockNoCreate(p);
1348                 if(b->dayNightDiffed())
1349                         return true;
1350         }
1351         catch(InvalidPositionException &e){}
1352
1353         return false;
1354 }
1355
1356 /*
1357         Updates usage timers
1358 */
1359 void Map::timerUpdate(float dtime)
1360 {
1361         JMutexAutoLock lock(m_sector_mutex);
1362
1363         core::map<v2s16, MapSector*>::Iterator si;
1364
1365         si = m_sectors.getIterator();
1366         for(; si.atEnd() == false; si++)
1367         {
1368                 MapSector *sector = si.getNode()->getValue();
1369                 sector->usage_timer += dtime;
1370         }
1371 }
1372
1373 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1374 {
1375         /*
1376                 Wait for caches to be removed before continuing.
1377                 
1378                 This disables the existence of caches while locked
1379         */
1380         //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1381
1382         core::list<v2s16>::Iterator j;
1383         for(j=list.begin(); j!=list.end(); j++)
1384         {
1385                 MapSector *sector = m_sectors[*j];
1386                 if(only_blocks)
1387                 {
1388                         sector->deleteBlocks();
1389                 }
1390                 else
1391                 {
1392                         /*
1393                                 If sector is in sector cache, remove it from there
1394                         */
1395                         if(m_sector_cache == sector)
1396                         {
1397                                 m_sector_cache = NULL;
1398                         }
1399                         /*
1400                                 Remove from map and delete
1401                         */
1402                         m_sectors.remove(*j);
1403                         delete sector;
1404                 }
1405         }
1406 }
1407
1408 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1409                 core::list<v3s16> *deleted_blocks)
1410 {
1411         JMutexAutoLock lock(m_sector_mutex);
1412
1413         core::list<v2s16> sector_deletion_queue;
1414         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1415         for(; i.atEnd() == false; i++)
1416         {
1417                 MapSector *sector = i.getNode()->getValue();
1418                 /*
1419                         Delete sector from memory if it hasn't been used in a long time
1420                 */
1421                 if(sector->usage_timer > timeout)
1422                 {
1423                         sector_deletion_queue.push_back(i.getNode()->getKey());
1424                         
1425                         if(deleted_blocks != NULL)
1426                         {
1427                                 // Collect positions of blocks of sector
1428                                 MapSector *sector = i.getNode()->getValue();
1429                                 core::list<MapBlock*> blocks;
1430                                 sector->getBlocks(blocks);
1431                                 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1432                                                 i != blocks.end(); i++)
1433                                 {
1434                                         deleted_blocks->push_back((*i)->getPos());
1435                                 }
1436                         }
1437                 }
1438         }
1439         deleteSectors(sector_deletion_queue, only_blocks);
1440         return sector_deletion_queue.getSize();
1441 }
1442
1443 void Map::PrintInfo(std::ostream &out)
1444 {
1445         out<<"Map: ";
1446 }
1447
1448 #define WATER_DROP_BOOST 4
1449
1450 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1451 {
1452         DSTACK(__FUNCTION_NAME);
1453         //TimeTaker timer("transformLiquids()");
1454
1455         u32 loopcount = 0;
1456         u32 initial_size = m_transforming_liquid.size();
1457
1458         //dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1459
1460         while(m_transforming_liquid.size() != 0)
1461         {
1462                 /*
1463                         Get a queued transforming liquid node
1464                 */
1465                 v3s16 p0 = m_transforming_liquid.pop_front();
1466
1467                 MapNode n0 = getNode(p0);
1468                 
1469                 // Don't deal with non-liquids
1470                 if(content_liquid(n0.d) == false)
1471                         continue;
1472
1473                 bool is_source = !content_flowing_liquid(n0.d);
1474                 
1475                 u8 liquid_level = 8;
1476                 if(is_source == false)
1477                         liquid_level = n0.param2 & 0x0f;
1478                 
1479                 // Turn possible source into non-source
1480                 u8 nonsource_c = make_liquid_flowing(n0.d);
1481
1482                 /*
1483                         If not source, check that some node flows into this one
1484                         and what is the level of liquid in this one
1485                 */
1486                 if(is_source == false)
1487                 {
1488                         s8 new_liquid_level_max = -1;
1489
1490                         v3s16 dirs_from[5] = {
1491                                 v3s16(0,1,0), // top
1492                                 v3s16(0,0,1), // back
1493                                 v3s16(1,0,0), // right
1494                                 v3s16(0,0,-1), // front
1495                                 v3s16(-1,0,0), // left
1496                         };
1497                         for(u16 i=0; i<5; i++)
1498                         {
1499                                 try
1500                                 {
1501
1502                                 bool from_top = (i==0);
1503
1504                                 v3s16 p2 = p0 + dirs_from[i];
1505                                 MapNode n2 = getNode(p2);
1506
1507                                 if(content_liquid(n2.d))
1508                                 {
1509                                         u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1510                                         // Check that the liquids are the same type
1511                                         if(n2_nonsource_c != nonsource_c)
1512                                         {
1513                                                 dstream<<"WARNING: Not handling: different liquids"
1514                                                                 " collide"<<std::endl;
1515                                                 continue;
1516                                         }
1517                                         bool n2_is_source = !content_flowing_liquid(n2.d);
1518                                         s8 n2_liquid_level = 8;
1519                                         if(n2_is_source == false)
1520                                                 n2_liquid_level = n2.param2 & 0x07;
1521                                         
1522                                         s8 new_liquid_level = -1;
1523                                         if(from_top)
1524                                         {
1525                                                 //new_liquid_level = 7;
1526                                                 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1527                                                         new_liquid_level = 7;
1528                                                 else
1529                                                         new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1530                                         }
1531                                         else if(n2_liquid_level > 0)
1532                                         {
1533                                                 new_liquid_level = n2_liquid_level - 1;
1534                                         }
1535
1536                                         if(new_liquid_level > new_liquid_level_max)
1537                                                 new_liquid_level_max = new_liquid_level;
1538                                 }
1539
1540                                 }catch(InvalidPositionException &e)
1541                                 {
1542                                 }
1543                         } //for
1544                         
1545                         /*
1546                                 If liquid level should be something else, update it and
1547                                 add all the neighboring water nodes to the transform queue.
1548                         */
1549                         if(new_liquid_level_max != liquid_level)
1550                         {
1551                                 if(new_liquid_level_max == -1)
1552                                 {
1553                                         // Remove water alltoghether
1554                                         n0.d = CONTENT_AIR;
1555                                         n0.param2 = 0;
1556                                         setNode(p0, n0);
1557                                 }
1558                                 else
1559                                 {
1560                                         n0.param2 = new_liquid_level_max;
1561                                         setNode(p0, n0);
1562                                 }
1563                                 
1564                                 // Block has been modified
1565                                 {
1566                                         v3s16 blockpos = getNodeBlockPos(p0);
1567                                         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1568                                         if(block != NULL)
1569                                                 modified_blocks.insert(blockpos, block);
1570                                 }
1571                                 
1572                                 /*
1573                                         Add neighboring non-source liquid nodes to transform queue.
1574                                 */
1575                                 v3s16 dirs[6] = {
1576                                         v3s16(0,0,1), // back
1577                                         v3s16(0,1,0), // top
1578                                         v3s16(1,0,0), // right
1579                                         v3s16(0,0,-1), // front
1580                                         v3s16(0,-1,0), // bottom
1581                                         v3s16(-1,0,0), // left
1582                                 };
1583                                 for(u16 i=0; i<6; i++)
1584                                 {
1585                                         try
1586                                         {
1587
1588                                         v3s16 p2 = p0 + dirs[i];
1589                                         
1590                                         MapNode n2 = getNode(p2);
1591                                         if(content_flowing_liquid(n2.d))
1592                                         {
1593                                                 m_transforming_liquid.push_back(p2);
1594                                         }
1595                                         
1596                                         }catch(InvalidPositionException &e)
1597                                         {
1598                                         }
1599                                 }
1600                         }
1601                 }
1602                 
1603                 // Get a new one from queue if the node has turned into non-water
1604                 if(content_liquid(n0.d) == false)
1605                         continue;
1606
1607                 /*
1608                         Flow water from this node
1609                 */
1610                 v3s16 dirs_to[5] = {
1611                         v3s16(0,-1,0), // bottom
1612                         v3s16(0,0,1), // back
1613                         v3s16(1,0,0), // right
1614                         v3s16(0,0,-1), // front
1615                         v3s16(-1,0,0), // left
1616                 };
1617                 for(u16 i=0; i<5; i++)
1618                 {
1619                         try
1620                         {
1621
1622                         bool to_bottom = (i == 0);
1623
1624                         // If liquid is at lowest possible height, it's not going
1625                         // anywhere except down
1626                         if(liquid_level == 0 && to_bottom == false)
1627                                 continue;
1628                         
1629                         u8 liquid_next_level = 0;
1630                         // If going to bottom
1631                         if(to_bottom)
1632                         {
1633                                 //liquid_next_level = 7;
1634                                 if(liquid_level >= 7 - WATER_DROP_BOOST)
1635                                         liquid_next_level = 7;
1636                                 else
1637                                         liquid_next_level = liquid_level + WATER_DROP_BOOST;
1638                         }
1639                         else
1640                                 liquid_next_level = liquid_level - 1;
1641
1642                         bool n2_changed = false;
1643                         bool flowed = false;
1644                         
1645                         v3s16 p2 = p0 + dirs_to[i];
1646
1647                         MapNode n2 = getNode(p2);
1648                         //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1649
1650                         if(content_liquid(n2.d))
1651                         {
1652                                 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1653                                 // Check that the liquids are the same type
1654                                 if(n2_nonsource_c != nonsource_c)
1655                                 {
1656                                         dstream<<"WARNING: Not handling: different liquids"
1657                                                         " collide"<<std::endl;
1658                                         continue;
1659                                 }
1660                                 bool n2_is_source = !content_flowing_liquid(n2.d);
1661                                 u8 n2_liquid_level = 8;
1662                                 if(n2_is_source == false)
1663                                         n2_liquid_level = n2.param2 & 0x07;
1664                                 
1665                                 if(to_bottom)
1666                                 {
1667                                         flowed = true;
1668                                 }
1669
1670                                 if(n2_is_source)
1671                                 {
1672                                         // Just flow into the source, nothing changes.
1673                                         // n2_changed is not set because destination didn't change
1674                                         flowed = true;
1675                                 }
1676                                 else
1677                                 {
1678                                         if(liquid_next_level > liquid_level)
1679                                         {
1680                                                 n2.param2 = liquid_next_level;
1681                                                 setNode(p2, n2);
1682
1683                                                 n2_changed = true;
1684                                                 flowed = true;
1685                                         }
1686                                 }
1687                         }
1688                         else if(n2.d == CONTENT_AIR)
1689                         {
1690                                 n2.d = nonsource_c;
1691                                 n2.param2 = liquid_next_level;
1692                                 setNode(p2, n2);
1693                                 
1694                                 n2_changed = true;
1695                                 flowed = true;
1696                         }
1697                         
1698                         //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1699
1700                         if(n2_changed)
1701                         {
1702                                 m_transforming_liquid.push_back(p2);
1703                                 
1704                                 v3s16 blockpos = getNodeBlockPos(p2);
1705                                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1706                                 if(block != NULL)
1707                                         modified_blocks.insert(blockpos, block);
1708                         }
1709                         
1710                         // If n2_changed to bottom, don't flow anywhere else
1711                         if(to_bottom && flowed && !is_source)
1712                                 break;
1713                                 
1714                         }catch(InvalidPositionException &e)
1715                         {
1716                         }
1717                 }
1718
1719                 loopcount++;
1720                 //if(loopcount >= 100000)
1721                 if(loopcount >= initial_size * 1)
1722                         break;
1723         }
1724         //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1725 }
1726
1727 /*
1728         ServerMap
1729 */
1730
1731 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1732         Map(dout_server),
1733         m_heightmap(NULL)
1734 {
1735         
1736         //m_chunksize = 64;
1737         //m_chunksize = 16;
1738         m_chunksize = 8;
1739         //m_chunksize = 4;
1740         //m_chunksize = 2;
1741
1742         /*
1743                 Experimental and debug stuff
1744         */
1745         
1746         {
1747                 dstream<<"Generating map point attribute lists"<<std::endl;
1748                 
1749                 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1750                 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1751                 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1752                 //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1753                 //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1754
1755 #if 0
1756                 /*
1757                         NOTE: BEWARE: Too big amount of these will make map generation
1758                         slow. Especially those that are read by every block emerge.
1759                         
1760                         Fetch times:
1761                         1000 points: 2-3ms
1762                         5000 points: 15ms
1763                         15000 points: 40ms
1764                 */
1765                 
1766                 for(u32 i=0; i<500; i++)
1767                 {
1768                         /*u32 lim = MAP_GENERATION_LIMIT;
1769                         if(i < 400)
1770                                 lim = 2000;*/
1771
1772                         u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1773
1774                         v3s16 p(
1775                                 -lim + myrand()%(lim*2),
1776                                 0,
1777                                 -lim + myrand()%(lim*2)
1778                         );
1779                         /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1780                         plants_amount = pow(plants_amount, 5);
1781                         list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1782                         
1783                         float plants_amount = 0;
1784                         if(myrand()%4 == 0)
1785                         {
1786                                 plants_amount = 1.5;
1787                         }
1788                         else if(myrand()%4 == 0)
1789                         {
1790                                 plants_amount = 0.5;
1791                         }
1792                         else if(myrand()%2 == 0)
1793                         {
1794                                 plants_amount = 0.03;
1795                         }
1796                         else
1797                         {
1798                                 plants_amount = 0.0;
1799                         }
1800
1801
1802                         list_plants_amount->addPoint(p, Attribute(plants_amount));
1803                 }
1804
1805                 for(u32 i=0; i<500; i++)
1806                 {
1807                         /*u32 lim = MAP_GENERATION_LIMIT;
1808                         if(i < 400)
1809                                 lim = 2000;*/
1810
1811                         u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1812
1813                         v3s16 p(
1814                                 -lim + myrand()%(lim*2),
1815                                 0,
1816                                 -lim + myrand()%(lim*2)
1817                         );
1818
1819                         float caves_amount = 0;
1820                         if(myrand()%5 == 0)
1821                         {
1822                                 caves_amount = 1.0;
1823                         }
1824                         else if(myrand()%3 == 0)
1825                         {
1826                                 caves_amount = 0.3;
1827                         }
1828                         else
1829                         {
1830                                 caves_amount = 0.05;
1831                         }
1832
1833                         list_caves_amount->addPoint(p, Attribute(caves_amount));
1834                 }
1835                 
1836                 for(u32 i=0; i<500; i++)
1837                 {
1838                         /*u32 lim = MAP_GENERATION_LIMIT;
1839                         if(i < 400)
1840                                 lim = 2000;*/
1841
1842                         u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1843
1844                         v3s16 p(
1845                                 -lim + (myrand()%(lim*2)),
1846                                 0,
1847                                 -lim + (myrand()%(lim*2))
1848                         );
1849                         
1850                         /*s32 bh_i = (myrand()%200) - 50;
1851                         float baseheight = (float)bh_i;
1852                         
1853                         float m = 100.;
1854                         float e = 3.;
1855                         float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1856                         randmax = pow(randmax, e);
1857
1858                         //float randmax = (float)(myrand()%60);
1859                         float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1860
1861                         float baseheight = 0;
1862                         float randmax = 0;
1863                         float randfactor = 0;
1864
1865                         /*if(myrand()%5 == 0)
1866                         {
1867                                 baseheight = 100;
1868                                 randmax = 50;
1869                                 randfactor = 0.63;
1870                         }
1871                         else if(myrand()%6 == 0)
1872                         {
1873                                 baseheight = 200;
1874                                 randmax = 100;
1875                                 randfactor = 0.66;
1876                         }
1877                         else if(myrand()%4 == 0)
1878                         {
1879                                 baseheight = -3;
1880                                 randmax = 30;
1881                                 randfactor = 0.7;
1882                         }
1883                         else if(myrand()%3 == 0)
1884                         {
1885                                 baseheight = 0;
1886                                 randmax = 30;
1887                                 randfactor = 0.63;
1888                         }
1889                         else
1890                         {
1891                                 baseheight = -3;
1892                                 randmax = 20;
1893                                 randfactor = 0.5;
1894                         }*/
1895                         
1896                         if(myrand()%3 < 2)
1897                         {
1898                                 baseheight = 10;
1899                                 randmax = 30;
1900                                 randfactor = 0.7;
1901                         }
1902                         else
1903                         {
1904                                 baseheight = 0;
1905                                 randmax = 15;
1906                                 randfactor = 0.63;
1907                         }
1908
1909                         list_baseheight->addPoint(p, Attribute(baseheight));
1910                         list_randmax->addPoint(p, Attribute(randmax));
1911                         list_randfactor->addPoint(p, Attribute(randfactor));
1912                 }
1913 #endif
1914                 
1915                 // Add only one entry
1916                 list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1917                 list_randmax->addPoint(v3s16(0,0,0), Attribute(30));
1918                 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1919
1920                 // Easy spawn point
1921                 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1922                 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1923                 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1924         }
1925         
1926         /*
1927                 Try to load map; if not found, create a new one.
1928         */
1929
1930         m_savedir = savedir;
1931         m_map_saving_enabled = false;
1932         
1933         try
1934         {
1935                 // If directory exists, check contents and load if possible
1936                 if(fs::PathExists(m_savedir))
1937                 {
1938                         // If directory is empty, it is safe to save into it.
1939                         if(fs::GetDirListing(m_savedir).size() == 0)
1940                         {
1941                                 dstream<<DTIME<<"Server: Empty save directory is valid."
1942                                                 <<std::endl;
1943                                 m_map_saving_enabled = true;
1944                         }
1945                         else
1946                         {
1947                                 // Load master heightmap
1948                                 loadMasterHeightmap();
1949                                 
1950                                 // Load sector (0,0) and throw and exception on fail
1951                                 if(loadSectorFull(v2s16(0,0)) == false)
1952                                         throw LoadError("Failed to load sector (0,0)");
1953
1954                                 dstream<<DTIME<<"Server: Successfully loaded master "
1955                                                 "heightmap and sector (0,0) from "<<savedir<<
1956                                                 ", assuming valid save directory."
1957                                                 <<std::endl;
1958
1959                                 m_map_saving_enabled = true;
1960                                 // Map loaded, not creating new one
1961                                 return;
1962                         }
1963                 }
1964                 // If directory doesn't exist, it is safe to save to it
1965                 else{
1966                         m_map_saving_enabled = true;
1967                 }
1968         }
1969         catch(std::exception &e)
1970         {
1971                 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1972                                 <<", exception: "<<e.what()<<std::endl;
1973                 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1974                 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1975         }
1976
1977         dstream<<DTIME<<"Initializing new map."<<std::endl;
1978         
1979         // Create master heightmap
1980         m_heightmap = new UnlimitedHeightmap
1981                         (32, &m_padb);
1982         
1983         // Set map parameters
1984         m_params = mp;
1985         
1986         // Create zero sector
1987         emergeSector(v2s16(0,0));
1988
1989         // Initially write whole map
1990         save(false);
1991 }
1992
1993 ServerMap::~ServerMap()
1994 {
1995         try
1996         {
1997                 if(m_map_saving_enabled)
1998                 {
1999                         //save(false);
2000                         // Save only changed parts
2001                         save(true);
2002                         dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2003                 }
2004                 else
2005                 {
2006                         dstream<<DTIME<<"Server: map not saved"<<std::endl;
2007                 }
2008         }
2009         catch(std::exception &e)
2010         {
2011                 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2012                                 <<", exception: "<<e.what()<<std::endl;
2013         }
2014         
2015         if(m_heightmap != NULL)
2016                 delete m_heightmap;
2017         
2018         /*
2019                 Free all MapChunks
2020         */
2021         core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2022         for(; i.atEnd() == false; i++)
2023         {
2024                 MapChunk *chunk = i.getNode()->getValue();
2025                 delete chunk;
2026         }
2027 }
2028
2029 /*
2030         Some helper functions
2031 */
2032
2033 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2034 {
2035         v3s16 em = vmanip.m_area.getExtent();
2036         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2037         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2038         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2039         s16 y;
2040         for(y=y_nodes_max; y>=y_nodes_min; y--)
2041         {
2042                 MapNode &n = vmanip.m_data[i];
2043                 if(content_walkable(n.d))
2044                         break;
2045                         
2046                 vmanip.m_area.add_y(em, i, -1);
2047         }
2048         if(y >= y_nodes_min)
2049                 return y;
2050         else
2051                 return y_nodes_min;
2052 }
2053
2054 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2055 {
2056         MapNode treenode(CONTENT_TREE);
2057         MapNode leavesnode(CONTENT_LEAVES);
2058
2059         s16 trunk_h = myrand_range(2, 6);
2060         v3s16 p1 = p0;
2061         for(s16 ii=0; ii<trunk_h; ii++)
2062         {
2063                 if(vmanip.m_area.contains(p1))
2064                         vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2065                 p1.Y++;
2066         }
2067         
2068         // p1 is now the last piece of the trunk
2069         p1.Y -= 1;
2070
2071         VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2072         SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2073         for(s32 i=0; i<leaves_a.getVolume(); i++)
2074                 leaves_d[i] = 0;
2075         
2076         // Force leaves at near the end of the trunk
2077         {
2078                 s16 d = 1;
2079                 for(s16 z=-d; z<=d; z++)
2080                 for(s16 y=-d; y<=d; y++)
2081                 for(s16 x=-d; x<=d; x++)
2082                 {
2083                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2084                 }
2085         }
2086         
2087         // Add leaves randomly
2088         for(u32 iii=0; iii<7; iii++)
2089         {
2090                 s16 d = 1;
2091
2092                 v3s16 p(
2093                         myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2094                         myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2095                         myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2096                 );
2097                 
2098                 for(s16 z=0; z<=d; z++)
2099                 for(s16 y=0; y<=d; y++)
2100                 for(s16 x=0; x<=d; x++)
2101                 {
2102                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2103                 }
2104         }
2105         
2106         // Blit leaves to vmanip
2107         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2108         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2109         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2110         {
2111                 v3s16 p(x,y,z);
2112                 p += p1;
2113                 if(vmanip.m_area.contains(p) == false)
2114                         continue;
2115                 u32 vi = vmanip.m_area.index(p);
2116                 if(vmanip.m_data[vi].d != CONTENT_AIR)
2117                         continue;
2118                 u32 i = leaves_a.index(x,y,z);
2119                 if(leaves_d[i] == 1)
2120                         vmanip.m_data[vi] = leavesnode;
2121         }
2122 }
2123
2124 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2125                 core::map<v3s16, MapBlock*> &changed_blocks)
2126 {
2127         /*
2128                 Don't generate if already fully generated
2129         */
2130         {
2131                 MapChunk *chunk = getChunk(chunkpos);
2132                 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2133                 {
2134                         dstream<<"generateChunkRaw(): Chunk "
2135                                         <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2136                                         <<" already generated"<<std::endl;
2137                         return chunk;
2138                 }
2139         }
2140
2141         dstream<<"generateChunkRaw(): Generating chunk "
2142                         <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2143                         <<std::endl;
2144         
2145         TimeTaker timer("generateChunkRaw()");
2146         
2147         // The distance how far into the neighbors the generator is allowed to go
2148         s16 max_spread_amount_sectors = 2;
2149         assert(max_spread_amount_sectors <= m_chunksize);
2150         s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2151         // Minimum amount of space left on sides for mud to fall in
2152         s16 min_mud_fall_space = 2;
2153         // Maximum diameter of stone obstacles in X and Z
2154         s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2155         assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2156         
2157         s16 y_blocks_min = -4;
2158         s16 y_blocks_max = 3;
2159         s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2160         s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2161         s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2162
2163         v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2164         s16 sectorpos_base_size = m_chunksize;
2165
2166         /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2167         s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2168         v2s16 sectorpos_bigbase =
2169                         sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2170         s16 sectorpos_bigbase_size =
2171                         sectorpos_base_size + 2 * max_spread_amount_sectors;
2172
2173         v3s16 bigarea_blocks_min(
2174                 sectorpos_bigbase.X,
2175                 y_blocks_min,
2176                 sectorpos_bigbase.Y
2177         );
2178
2179         v3s16 bigarea_blocks_max(
2180                 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2181                 y_blocks_max,
2182                 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2183         );
2184         
2185         // Relative values to control amount of stuff in one chunk
2186         u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2187                         *(u32)sectorpos_base_size*MAP_BLOCKSIZE;
2188         u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2189                         *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2190                         *(u32)h_blocks*MAP_BLOCKSIZE;
2191                 
2192         /*
2193                 Create the whole area of this and the neighboring chunks
2194         */
2195         {
2196                 TimeTaker timer("generateChunkRaw() create area");
2197                 
2198                 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2199                 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2200                 {
2201                         v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2202                         ServerMapSector *sector = createSector(sectorpos);
2203                         assert(sector);
2204
2205                         for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2206                         {
2207                                 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2208                                 MapBlock *block = createBlock(blockpos);
2209
2210                                 // Lighting won't be calculated
2211                                 //block->setLightingExpired(true);
2212                                 // Lighting will be calculated
2213                                 block->setLightingExpired(false);
2214
2215                                 /*
2216                                         Block gets sunlight if this is true.
2217
2218                                         This should be set to true when the top side of a block
2219                                         is completely exposed to the sky.
2220
2221                                         Actually this doesn't matter now because the
2222                                         initial lighting is done here.
2223                                 */
2224                                 block->setIsUnderground(y != y_blocks_max);
2225                         }
2226                 }
2227         }
2228
2229         /*
2230                 Now we have a big empty area.
2231
2232                 Make a ManualMapVoxelManipulator that contains this and the
2233                 neighboring chunks
2234         */
2235
2236         ManualMapVoxelManipulator vmanip(this);
2237         // Add the area we just generated
2238         {
2239                 TimeTaker timer("generateChunkRaw() initialEmerge");
2240                 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2241         }
2242
2243         TimeTaker timer_generate("generateChunkRaw() generate");
2244
2245         /*
2246                 Generate general ground level to full area
2247         */
2248         
2249         {
2250         // 22ms @cs=8
2251         //TimeTaker timer1("ground level");
2252
2253         for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2254         for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2255         {
2256                 // Node position
2257                 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2258                 
2259                 /*
2260                         Skip of already generated
2261                 */
2262                 {
2263                         v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2264                         if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2265                                 continue;
2266                 }
2267
2268                 // Ground height at this point
2269                 float surface_y_f = 0.0;
2270                 /*
2271                         A hack to get the ground height from the sector.
2272                         Do this better.
2273                 */
2274                 {
2275                         v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
2276                         v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
2277                         MapSector *sector = getSectorNoGenerate(sectorpos);
2278                         assert(sector);
2279                         float h = sector->getGroundHeight(sector_relpos);
2280                         if(h > GROUNDHEIGHT_VALID_MINVALUE)
2281                                 surface_y_f = h;
2282                         else
2283                                 dstream<<"WARNING: "<<__FUNCTION_NAME
2284                                 <<": sector->getGroundHeight returned bad height"<<std::endl;
2285                 }
2286                 // Convert to integer
2287                 s16 surface_y = (s16)surface_y_f;
2288
2289                 /*
2290                         Fill ground with stone
2291                 */
2292                 {
2293                         // Use fast index incrementing
2294                         v3s16 em = vmanip.m_area.getExtent();
2295                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2296                         for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2297                         {
2298                                 vmanip.m_data[i].d = CONTENT_STONE;
2299
2300                                 vmanip.m_area.add_y(em, i, 1);
2301                         }
2302                 }
2303         }
2304         
2305         }//timer1
2306
2307         // This is set during the next operation.
2308         // Maximum height of the stone surface and obstacles.
2309         // This is used to disable dungeon generation from going too high.
2310         s16 stone_surface_max_y = 0;
2311
2312         {
2313         // 8ms @cs=8
2314         //TimeTaker timer1("stone obstacles");
2315
2316         /*
2317                 Add some random stone obstacles
2318         */
2319
2320         for(u32 ri=0; ri<15; ri++)
2321         {
2322                 // Randomize max height so usually stuff will be quite low
2323                 s16 maxheight_randomized = myrand_range(0, 30);
2324
2325                 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2326                 v3s16 ob_size(
2327                         myrand_range(5, stone_obstacle_max_size),
2328                         myrand_range(0, maxheight_randomized),
2329                         myrand_range(5, stone_obstacle_max_size)
2330                 );
2331                 v2s16 ob_place(
2332                         myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2333                         myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2334                 );
2335                 
2336                 // Minimum space left on top of the obstacle
2337                 s16 min_head_space = 10;
2338                 
2339                 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2340                 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2341                 {
2342                         // Node position in 2d
2343                         v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2344                         
2345                         // Find stone ground level
2346                         // (ignore everything else than mud in already generated chunks)
2347                         // and mud amount over the stone level
2348                         s16 surface_y = 0;
2349                         s16 mud_amount = 0;
2350                         {
2351                                 v3s16 em = vmanip.m_area.getExtent();
2352                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2353                                 s16 y;
2354                                 // Go to ground level
2355                                 for(y=y_nodes_max; y>=y_nodes_min; y--)
2356                                 {
2357                                         MapNode &n = vmanip.m_data[i];
2358                                         /*if(content_walkable(n.d)
2359                                                         && n.d != CONTENT_MUD
2360                                                         && n.d != CONTENT_GRASS)
2361                                                 break;*/
2362                                         if(n.d == CONTENT_STONE)
2363                                                 break;
2364
2365                                         if(n.d == CONTENT_MUD || n.d == CONTENT_GRASS)
2366                                                 mud_amount++;
2367                                                 
2368                                         vmanip.m_area.add_y(em, i, -1);
2369                                 }
2370                                 if(y >= y_nodes_min)
2371                                         surface_y = y;
2372                                 else
2373                                         surface_y = y_nodes_min;
2374                         }
2375
2376
2377                         /*
2378                                 Add stone on ground
2379                         */
2380                         {
2381                                 v3s16 em = vmanip.m_area.getExtent();
2382                                 s16 y_start = surface_y+1;
2383                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2384                                 s16 y;
2385                                 // Add stone
2386                                 s16 count = 0;
2387                                 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2388                                 {
2389                                         MapNode &n = vmanip.m_data[i];
2390                                         n.d = CONTENT_STONE;
2391
2392                                         if(y > stone_surface_max_y)
2393                                                 stone_surface_max_y = y;
2394
2395                                         count++;
2396                                         if(count >= ob_size.Y)
2397                                                 break;
2398                                                 
2399                                         vmanip.m_area.add_y(em, i, 1);
2400                                 }
2401                                 // Add mud
2402                                 count = 0;
2403                                 for(; y<=y_nodes_max; y++)
2404                                 {
2405                                         MapNode &n = vmanip.m_data[i];
2406                                         n.d = CONTENT_MUD;
2407                                         count++;
2408                                         if(count >= mud_amount)
2409                                                 break;
2410                                                 
2411                                         vmanip.m_area.add_y(em, i, 1);
2412                                 }
2413                         }
2414
2415                 }
2416         }
2417
2418         }//timer1
2419         {
2420         // 24ms @cs=8
2421         //TimeTaker timer1("dungeons");
2422
2423         /*
2424                 Make dungeons
2425         */
2426         u32 dungeons_count = relative_volume/200000;
2427         for(u32 jj=0; jj<dungeons_count; jj++)
2428         {
2429                 s16 min_tunnel_diameter = 1;
2430                 s16 max_tunnel_diameter = 5;
2431                 u16 tunnel_routepoints = 15;
2432                 
2433                 bool bruise_surface = (jj < dungeons_count / 3);
2434
2435                 if(bruise_surface)
2436                 {
2437                         min_tunnel_diameter = 5;
2438                         max_tunnel_diameter = 10;
2439                         tunnel_routepoints = 10;
2440                 }
2441
2442                 // Allowed route area size in nodes
2443                 v3s16 ar(
2444                         sectorpos_base_size*MAP_BLOCKSIZE,
2445                         h_blocks*MAP_BLOCKSIZE,
2446                         sectorpos_base_size*MAP_BLOCKSIZE
2447                 );
2448
2449                 // Area starting point in nodes
2450                 v3s16 of(
2451                         sectorpos_base.X*MAP_BLOCKSIZE,
2452                         y_blocks_min*MAP_BLOCKSIZE,
2453                         sectorpos_base.Y*MAP_BLOCKSIZE
2454                 );
2455
2456                 // Allow a bit more
2457                 //(this should be more than the maximum radius of the tunnel)
2458                 s16 insure = 5;
2459                 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2460                 ar += v3s16(1,0,1) * more * 2;
2461                 of -= v3s16(1,0,1) * more;
2462                 
2463                 s16 route_y_min = 0;
2464                 //s16 route_y_max = ar.Y-1;
2465                 s16 route_y_max = stone_surface_max_y - of.Y;
2466
2467                 if(bruise_surface)
2468                 {
2469                         // Minimum is at y=0
2470                         route_y_min = -of.Y - 0;
2471                         route_y_min = rangelim(route_y_min, 0, route_y_max);
2472                 }
2473
2474                 /*dstream<<"route_y_min = "<<route_y_min
2475                                 <<", route_y_max = "<<route_y_max<<std::endl;*/
2476
2477                 // Randomize starting position
2478                 v3f orp(
2479                         (float)(myrand()%ar.X)+0.5,
2480                         (float)(myrand_range(route_y_min, route_y_max))+0.5,
2481                         (float)(myrand()%ar.Z)+0.5
2482                 );
2483
2484                 MapNode airnode(CONTENT_AIR);
2485                 
2486                 /*
2487                         Generate some tunnel starting from orp
2488                 */
2489                 
2490                 for(u16 j=0; j<tunnel_routepoints; j++)
2491                 {
2492                         v3s16 maxlen(20, 10, 20);
2493
2494                         if(bruise_surface)
2495                         {
2496                                 maxlen = v3s16(60,60,60);
2497                         }
2498
2499                         v3f vec(
2500                                 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2501                                 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2502                                 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2503                         );
2504                         v3f rp = orp + vec;
2505                         if(rp.X < 0)
2506                                 rp.X = 0;
2507                         else if(rp.X >= ar.X)
2508                                 rp.X = ar.X-1;
2509                         if(rp.Y < route_y_min)
2510                                 rp.Y = route_y_min;
2511                         else if(rp.Y >= route_y_max)
2512                                 rp.Y = route_y_max-1;
2513                         if(rp.Z < 0)
2514                                 rp.Z = 0;
2515                         else if(rp.Z >= ar.Z)
2516                                 rp.Z = ar.Z-1;
2517                         vec = rp - orp;
2518
2519                         // Randomize size
2520                         s16 min_d = min_tunnel_diameter;
2521                         s16 max_d = max_tunnel_diameter;
2522                         s16 rs = myrand_range(min_d, max_d);
2523                         
2524                         for(float f=0; f<1.0; f+=1.0/vec.getLength())
2525                         {
2526                                 v3f fp = orp + vec * f;
2527                                 v3s16 cp(fp.X, fp.Y, fp.Z);
2528
2529                                 s16 d0 = -rs/2;
2530                                 s16 d1 = d0 + rs - 1;
2531                                 for(s16 z0=d0; z0<=d1; z0++)
2532                                 {
2533                                         s16 si = rs - abs(z0);
2534                                         for(s16 x0=-si; x0<=si-1; x0++)
2535                                         {
2536                                                 s16 si2 = rs - abs(x0);
2537                                                 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2538                                                 {
2539                                                         s16 z = cp.Z + z0;
2540                                                         s16 y = cp.Y + y0;
2541                                                         s16 x = cp.X + x0;
2542                                                         v3s16 p(x,y,z);
2543                                                         /*if(isInArea(p, ar) == false)
2544                                                                 continue;*/
2545                                                         // Check only height
2546                                                         if(y < 0 || y >= ar.Y)
2547                                                                 continue;
2548                                                         p += of;
2549                                                         
2550                                                         //assert(vmanip.m_area.contains(p));
2551                                                         if(vmanip.m_area.contains(p) == false)
2552                                                         {
2553                                                                 dstream<<"WARNING: "<<__FUNCTION_NAME
2554                                                                                 <<":"<<__LINE__<<": "
2555                                                                                 <<"point not in area"
2556                                                                                 <<std::endl;
2557                                                                 continue;
2558                                                         }
2559                                                         
2560                                                         // Just set it to air, it will be changed to
2561                                                         // water afterwards
2562                                                         u32 i = vmanip.m_area.index(p);
2563                                                         vmanip.m_data[i] = airnode;
2564                                                 }
2565                                         }
2566                                 }
2567                         }
2568
2569                         orp = rp;
2570                 }
2571         
2572         }
2573
2574         }//timer1
2575         {
2576         // 46ms @cs=8
2577         //TimeTaker timer1("ore veins");
2578
2579         /*
2580                 Make ore veins
2581         */
2582         for(u32 jj=0; jj<relative_volume/524; jj++)
2583         {
2584                 s16 max_vein_diameter = 3;
2585
2586                 // Allowed route area size in nodes
2587                 v3s16 ar(
2588                         sectorpos_base_size*MAP_BLOCKSIZE,
2589                         h_blocks*MAP_BLOCKSIZE,
2590                         sectorpos_base_size*MAP_BLOCKSIZE
2591                 );
2592
2593                 // Area starting point in nodes
2594                 v3s16 of(
2595                         sectorpos_base.X*MAP_BLOCKSIZE,
2596                         y_blocks_min*MAP_BLOCKSIZE,
2597                         sectorpos_base.Y*MAP_BLOCKSIZE
2598                 );
2599
2600                 // Allow a bit more
2601                 //(this should be more than the maximum radius of the tunnel)
2602                 s16 insure = 3;
2603                 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2604                 ar += v3s16(1,0,1) * more * 2;
2605                 of -= v3s16(1,0,1) * more;
2606                 
2607                 // Randomize starting position
2608                 v3f orp(
2609                         (float)(myrand()%ar.X)+0.5,
2610                         (float)(myrand()%ar.Y)+0.5,
2611                         (float)(myrand()%ar.Z)+0.5
2612                 );
2613
2614                 // Randomize mineral
2615                 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2616
2617                 /*
2618                         Generate some vein starting from orp
2619                 */
2620
2621                 for(u16 j=0; j<2; j++)
2622                 {
2623                         /*v3f rp(
2624                                 (float)(myrand()%ar.X)+0.5,
2625                                 (float)(myrand()%ar.Y)+0.5,
2626                                 (float)(myrand()%ar.Z)+0.5
2627                         );
2628                         v3f vec = rp - orp;*/
2629                         
2630                         v3s16 maxlen(10, 10, 10);
2631                         v3f vec(
2632                                 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2633                                 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2634                                 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2635                         );
2636                         v3f rp = orp + vec;
2637                         if(rp.X < 0)
2638                                 rp.X = 0;
2639                         else if(rp.X >= ar.X)
2640                                 rp.X = ar.X;
2641                         if(rp.Y < 0)
2642                                 rp.Y = 0;
2643                         else if(rp.Y >= ar.Y)
2644                                 rp.Y = ar.Y;
2645                         if(rp.Z < 0)
2646                                 rp.Z = 0;
2647                         else if(rp.Z >= ar.Z)
2648                                 rp.Z = ar.Z;
2649                         vec = rp - orp;
2650
2651                         // Randomize size
2652                         s16 min_d = 0;
2653                         s16 max_d = max_vein_diameter;
2654                         s16 rs = myrand_range(min_d, max_d);
2655                         
2656                         for(float f=0; f<1.0; f+=1.0/vec.getLength())
2657                         {
2658                                 v3f fp = orp + vec * f;
2659                                 v3s16 cp(fp.X, fp.Y, fp.Z);
2660                                 s16 d0 = -rs/2;
2661                                 s16 d1 = d0 + rs - 1;
2662                                 for(s16 z0=d0; z0<=d1; z0++)
2663                                 {
2664                                         s16 si = rs - abs(z0);
2665                                         for(s16 x0=-si; x0<=si-1; x0++)
2666                                         {
2667                                                 s16 si2 = rs - abs(x0);
2668                                                 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2669                                                 {
2670                                                         // Don't put mineral to every place
2671                                                         if(myrand()%5 != 0)
2672                                                                 continue;
2673
2674                                                         s16 z = cp.Z + z0;
2675                                                         s16 y = cp.Y + y0;
2676                                                         s16 x = cp.X + x0;
2677                                                         v3s16 p(x,y,z);
2678                                                         /*if(isInArea(p, ar) == false)
2679                                                                 continue;*/
2680                                                         // Check only height
2681                                                         if(y < 0 || y >= ar.Y)
2682                                                                 continue;
2683                                                         p += of;
2684                                                         
2685                                                         assert(vmanip.m_area.contains(p));
2686                                                         
2687                                                         // Just set it to air, it will be changed to
2688                                                         // water afterwards
2689                                                         u32 i = vmanip.m_area.index(p);
2690                                                         MapNode *n = &vmanip.m_data[i];
2691                                                         if(n->d == CONTENT_STONE)
2692                                                                 n->param = mineral;
2693                                                 }
2694                                         }
2695                                 }
2696                         }
2697
2698                         orp = rp;
2699                 }
2700         
2701         }
2702
2703         }//timer1
2704         {
2705         // 15ms @cs=8
2706         //TimeTaker timer1("add mud");
2707
2708         /*
2709                 Add mud to the central chunk
2710         */
2711         
2712         for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2713         for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2714         {
2715                 // Node position in 2d
2716                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2717                 
2718                 // Find ground level
2719                 s16 surface_y = find_ground_level(vmanip, p2d);
2720
2721                 /*
2722                         Add mud on ground
2723                 */
2724                 {
2725                         s16 mudcount = 0;
2726                         v3s16 em = vmanip.m_area.getExtent();
2727                         s16 y_start = surface_y+1;
2728                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2729                         for(s16 y=y_start; y<=y_nodes_max; y++)
2730                         {
2731                                 MapNode &n = vmanip.m_data[i];
2732                                 n.d = CONTENT_MUD;
2733                                 mudcount++;
2734                                 if(mudcount >= 3)
2735                                         break;
2736                                         
2737                                 vmanip.m_area.add_y(em, i, 1);
2738                         }
2739                 }
2740
2741         }
2742
2743         }//timer1
2744         {
2745         // 179ms @cs=8
2746         //TimeTaker timer1("flow mud");
2747
2748         /*
2749                 Flow mud away from steep edges
2750         */
2751
2752         // Iterate a few times
2753         for(s16 k=0; k<4; k++)
2754         {
2755
2756         for(s16 x=0-max_spread_amount+1;
2757                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2758                         x++)
2759         for(s16 z=0-max_spread_amount+1;
2760                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2761                         z++)
2762         {
2763                 // Node position in 2d
2764                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2765                 
2766                 v3s16 em = vmanip.m_area.getExtent();
2767                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2768                 s16 y;
2769                 // Go to ground level
2770                 for(y=y_nodes_max; y>=y_nodes_min; y--)
2771                 {
2772                         MapNode &n = vmanip.m_data[i];
2773                         //if(n.d != CONTENT_AIR)
2774                         if(content_walkable(n.d))
2775                                 break;
2776                                 
2777                         vmanip.m_area.add_y(em, i, -1);
2778                 }
2779
2780                 // If not mud, do nothing to it
2781                 MapNode *n = &vmanip.m_data[i];
2782                 if(n->d != CONTENT_MUD)
2783                         continue;
2784
2785                 v3s16 dirs4[4] = {
2786                         v3s16(0,0,1), // back
2787                         v3s16(1,0,0), // right
2788                         v3s16(0,0,-1), // front
2789                         v3s16(-1,0,0), // left
2790                 };
2791
2792                 // Drop mud on side
2793                 
2794                 for(u32 di=0; di<4; di++)
2795                 {
2796                         v3s16 dirp = dirs4[di];
2797                         u32 i2 = i;
2798                         // Check that side is air
2799                         vmanip.m_area.add_p(em, i2, dirp);
2800                         MapNode *n2 = &vmanip.m_data[i2];
2801                         if(content_walkable(n2->d))
2802                                 continue;
2803                         // Check that under side is air
2804                         vmanip.m_area.add_y(em, i2, -1);
2805                         n2 = &vmanip.m_data[i2];
2806                         if(content_walkable(n2->d))
2807                                 continue;
2808                         // Loop further down until not air
2809                         do{
2810                                 vmanip.m_area.add_y(em, i2, -1);
2811                                 n2 = &vmanip.m_data[i2];
2812                         }while(content_walkable(n2->d) == false);
2813                         // Loop one up so that we're in air
2814                         vmanip.m_area.add_y(em, i2, 1);
2815                         n2 = &vmanip.m_data[i2];
2816
2817                         // Move mud to new place
2818                         *n2 = *n;
2819                         // Set old place to be air
2820                         *n = MapNode(CONTENT_AIR);
2821
2822                         #if 0
2823                         // Switch mud and other and change mud source to air
2824                         //MapNode tempnode = *n2;
2825                         *n2 = *n;
2826                         //*n = tempnode;
2827                         // Force old mud position to be air
2828                         n->d = CONTENT_AIR;
2829                         #endif
2830
2831                         // Done
2832                         break;
2833                 }
2834         }
2835         
2836         }
2837
2838         }//timer1
2839         {
2840         // 50ms @cs=8
2841         //TimeTaker timer1("add water");
2842
2843         /*
2844                 Add water to the central chunk (and a bit more)
2845         */
2846         
2847         for(s16 x=0-max_spread_amount;
2848                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2849                         x++)
2850         for(s16 z=0-max_spread_amount;
2851                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2852                         z++)
2853         {
2854                 // Node position in 2d
2855                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2856                 
2857                 // Find ground level
2858                 //s16 surface_y = find_ground_level(vmanip, p2d);
2859
2860                 /*
2861                         If ground level is over water level, skip.
2862                         NOTE: This leaves caves near water without water,
2863                         which looks especially crappy when the nearby water
2864                         won't start flowing either for some reason
2865                 */
2866                 /*if(surface_y > WATER_LEVEL)
2867                         continue;*/
2868
2869                 /*
2870                         Add water on ground
2871                 */
2872                 {
2873                         v3s16 em = vmanip.m_area.getExtent();
2874                         s16 y_start = WATER_LEVEL;
2875                         u8 light = LIGHT_MAX;
2876                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2877                         MapNode *n = &vmanip.m_data[i];
2878                         /*
2879                                 Add first one to transforming liquid queue
2880                         */
2881                         if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2882                         {
2883                                 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2884                                 m_transforming_liquid.push_back(p);
2885                         }
2886                         for(s16 y=y_start; y>=y_nodes_min; y--)
2887                         {
2888                                 n = &vmanip.m_data[i];
2889                                 
2890                                 // Stop when there is no water and no air
2891                                 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2892                                                 && n->d != CONTENT_WATER)
2893                                 {
2894                                         /*
2895                                                 Add bottom one to transforming liquid queue
2896                                         */
2897                                         vmanip.m_area.add_y(em, i, 1);
2898                                         n = &vmanip.m_data[i];
2899                                         if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2900                                         {
2901                                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2902                                                 m_transforming_liquid.push_back(p);
2903                                         }
2904
2905                                         break;
2906                                 }
2907                                 
2908                                 n->d = CONTENT_WATERSOURCE;
2909                                 n->setLight(LIGHTBANK_DAY, light);
2910
2911                                 /*// Add to transforming liquid queue (in case it'd
2912                                 // start flowing)
2913                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2914                                 m_transforming_liquid.push_back(p);*/
2915                                 
2916                                 // Next one
2917                                 vmanip.m_area.add_y(em, i, -1);
2918                                 if(light > 0)
2919                                         light--;
2920                         }
2921                 }
2922
2923         }
2924
2925         }//timer1
2926         {
2927         // 1ms @cs=8
2928         //TimeTaker timer1("plant trees");
2929
2930         /*
2931                 Plant some trees
2932         */
2933         {
2934                 u32 tree_max = relative_area / 100;
2935                 
2936                 u32 count = myrand_range(0, tree_max);
2937                 for(u32 i=0; i<count; i++)
2938                 {
2939                         s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2940                         s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2941                         x += sectorpos_base.X*MAP_BLOCKSIZE;
2942                         z += sectorpos_base.Y*MAP_BLOCKSIZE;
2943                         s16 y = find_ground_level(vmanip, v2s16(x,z));
2944                         // Don't make a tree under water level
2945                         if(y < WATER_LEVEL)
2946                                 continue;
2947                         v3s16 p(x,y+1,z);
2948                         // Make a tree
2949                         make_tree(vmanip, p);
2950                 }
2951         }
2952
2953         }//timer1
2954         {
2955         // 19ms @cs=8
2956         //TimeTaker timer1("grow grass");
2957
2958         /*
2959                 Grow grass
2960         */
2961
2962         /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
2963         for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
2964         for(s16 x=0-max_spread_amount;
2965                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2966                         x++)
2967         for(s16 z=0-max_spread_amount;
2968                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2969                         z++)
2970         {
2971                 // Node position in 2d
2972                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2973                 
2974                 /*
2975                         Find the lowest surface to which enough light ends up
2976                         to make grass grow.
2977
2978                         Basically just wait until not air and not leaves.
2979                 */
2980                 s16 surface_y = 0;
2981                 {
2982                         v3s16 em = vmanip.m_area.getExtent();
2983                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2984                         s16 y;
2985                         // Go to ground level
2986                         for(y=y_nodes_max; y>=y_nodes_min; y--)
2987                         {
2988                                 MapNode &n = vmanip.m_data[i];
2989                                 if(n.d != CONTENT_AIR
2990                                                 && n.d != CONTENT_LEAVES)
2991                                         break;
2992                                 vmanip.m_area.add_y(em, i, -1);
2993                         }
2994                         if(y >= y_nodes_min)
2995                                 surface_y = y;
2996                         else
2997                                 surface_y = y_nodes_min;
2998                 }
2999                 
3000                 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3001                 MapNode *n = &vmanip.m_data[i];
3002                 if(n->d == CONTENT_MUD)
3003                         n->d = CONTENT_GRASS;
3004         }
3005
3006         }//timer1
3007
3008         /*
3009                 Handle lighting
3010         */
3011
3012         core::map<v3s16, bool> light_sources;
3013
3014         {
3015         // 750ms @cs=8, can't optimize more
3016         //TimeTaker timer1("initial lighting");
3017
3018         /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3019         for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3020         for(s16 x=0-max_spread_amount+1;
3021                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3022                         x++)
3023         for(s16 z=0-max_spread_amount+1;
3024                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3025                         z++)
3026         {
3027                 // Node position in 2d
3028                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3029                 
3030                 /*
3031                         Apply initial sunlight
3032                 */
3033                 {
3034                         u8 light = LIGHT_SUN;
3035                         bool add_to_sources = false;
3036                         v3s16 em = vmanip.m_area.getExtent();
3037                         s16 y_start = y_nodes_max;
3038                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3039                         for(s16 y=y_start; y>=y_nodes_min; y--)
3040                         {
3041                                 MapNode *n = &vmanip.m_data[i];
3042
3043                                 if(light_propagates_content(n->d) == false)
3044                                 {
3045                                         light = 0;
3046                                 }
3047                                 else if(light != LIGHT_SUN
3048                                         || sunlight_propagates_content(n->d) == false)
3049                                 {
3050                                         if(light > 0)
3051                                                 light--;
3052                                 }
3053                                 
3054                                 // This doesn't take much time
3055                                 if(add_to_sources == false)
3056                                 {
3057                                         /*
3058                                                 Check sides. If side is not air or water, start
3059                                                 adding to light_sources.
3060                                         */
3061                                         v3s16 dirs4[4] = {
3062                                                 v3s16(0,0,1), // back
3063                                                 v3s16(1,0,0), // right
3064                                                 v3s16(0,0,-1), // front
3065                                                 v3s16(-1,0,0), // left
3066                                         };
3067                                         for(u32 di=0; di<4; di++)
3068                                         {
3069                                                 v3s16 dirp = dirs4[di];
3070                                                 u32 i2 = i;
3071                                                 vmanip.m_area.add_p(em, i2, dirp);
3072                                                 MapNode *n2 = &vmanip.m_data[i2];
3073                                                 if(
3074                                                         n2->d != CONTENT_AIR
3075                                                         && n2->d != CONTENT_WATERSOURCE
3076                                                         && n2->d != CONTENT_WATER
3077                                                 ){
3078                                                         add_to_sources = true;
3079                                                         break;
3080                                                 }
3081                                         }
3082                                 }
3083
3084                                 n->setLight(LIGHTBANK_DAY, light);
3085                                 n->setLight(LIGHTBANK_NIGHT, 0);
3086                                 
3087                                 // This doesn't take much time
3088                                 if(light != 0 && add_to_sources)
3089                                 {
3090                                         // Insert light source
3091                                         light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3092                                 }
3093                                 
3094                                 // Increment index by y
3095                                 vmanip.m_area.add_y(em, i, -1);
3096                         }
3097                 }
3098         }
3099
3100         }//timer1
3101
3102         // Spread light around
3103         {
3104                 TimeTaker timer("generateChunkRaw() spreadLight");
3105                 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3106         }
3107         
3108         /*
3109                 Generation ended
3110         */
3111
3112         timer_generate.stop();
3113
3114         /*
3115                 Blit generated stuff to map
3116         */
3117         {
3118                 // 70ms @cs=8
3119                 //TimeTaker timer("generateChunkRaw() blitBackAll");
3120                 vmanip.blitBackAll(&changed_blocks);
3121         }
3122         /*
3123                 Update day/night difference cache of the MapBlocks
3124         */
3125         {
3126                 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3127                                 i.atEnd() == false; i++)
3128                 {
3129                         MapBlock *block = i.getNode()->getValue();
3130                         block->updateDayNightDiff();
3131                 }
3132         }
3133
3134         
3135         /*
3136                 Create chunk metadata
3137         */
3138
3139         for(s16 x=-1; x<=1; x++)
3140         for(s16 y=-1; y<=1; y++)
3141         {
3142                 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3143                 // Add chunk meta information
3144                 MapChunk *chunk = getChunk(chunkpos0);
3145                 if(chunk == NULL)
3146                 {
3147                         chunk = new MapChunk();
3148                         m_chunks.insert(chunkpos0, chunk);
3149                 }
3150                 //chunk->setIsVolatile(true);
3151                 if(chunk->getGenLevel() > GENERATED_PARTLY)
3152                         chunk->setGenLevel(GENERATED_PARTLY);
3153         }
3154
3155         /*
3156                 Set central chunk non-volatile and return it
3157         */
3158         MapChunk *chunk = getChunk(chunkpos);
3159         assert(chunk);
3160         // Set non-volatile
3161         //chunk->setIsVolatile(false);
3162         chunk->setGenLevel(GENERATED_FULLY);
3163         // Return it
3164         return chunk;
3165 }
3166
3167 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3168                 core::map<v3s16, MapBlock*> &changed_blocks)
3169 {
3170         dstream<<"generateChunk(): Generating chunk "
3171                         <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3172                         <<std::endl;
3173         
3174         /*for(s16 x=-1; x<=1; x++)
3175         for(s16 y=-1; y<=1; y++)*/
3176         for(s16 x=-0; x<=0; x++)
3177         for(s16 y=-0; y<=0; y++)
3178         {
3179                 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3180                 MapChunk *chunk = getChunk(chunkpos0);
3181                 // Skip if already generated
3182                 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3183                         continue;
3184                 generateChunkRaw(chunkpos0, changed_blocks);
3185         }
3186         
3187         assert(chunkNonVolatile(chunkpos1));
3188
3189         MapChunk *chunk = getChunk(chunkpos1);
3190         return chunk;
3191 }
3192
3193 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3194 {
3195         DSTACK("%s: p2d=(%d,%d)",
3196                         __FUNCTION_NAME,
3197                         p2d.X, p2d.Y);
3198         
3199         /*
3200                 Check if it exists already in memory
3201         */
3202         ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3203         if(sector != NULL)
3204                 return sector;
3205         
3206         /*
3207                 Try to load it from disk (with blocks)
3208         */
3209         if(loadSectorFull(p2d) == true)
3210         {
3211                 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3212                 if(sector == NULL)
3213                 {
3214                         dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3215                         throw InvalidPositionException("");
3216                 }
3217                 return sector;
3218         }
3219
3220         /*
3221                 If there is no master heightmap, throw.
3222         */
3223         if(m_heightmap == NULL)
3224         {
3225                 throw InvalidPositionException("createSector(): no heightmap");
3226         }
3227
3228         /*
3229                 Do not create over-limit
3230         */
3231         if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3232         || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3233         || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3234         || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3235                 throw InvalidPositionException("createSector(): pos. over limit");
3236
3237         /*
3238                 Generate blank sector
3239         */
3240         
3241         // Number of heightmaps in sector in each direction
3242         u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3243
3244         // Heightmap side width
3245         s16 hm_d = MAP_BLOCKSIZE / hm_split;
3246
3247         sector = new ServerMapSector(this, p2d, hm_split);
3248         
3249         // Sector position on map in nodes
3250         v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3251
3252         /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3253                         " heightmaps and objects"<<std::endl;*/
3254         
3255         /*
3256                 Generate sector heightmap
3257         */
3258
3259         v2s16 mhm_p = p2d * hm_split;
3260         /*f32 corners[4] = {
3261                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3262                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3263                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3264                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3265         };*/
3266         
3267         // Loop through sub-heightmaps
3268         for(s16 y=0; y<hm_split; y++)
3269         for(s16 x=0; x<hm_split; x++)
3270         {
3271                 v2s16 p_in_sector = v2s16(x,y);
3272                 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3273                 f32 corners[4] = {
3274                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3275                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3276                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3277                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3278                 };
3279
3280                 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3281                                 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3282                                 <<std::endl;*/
3283
3284                 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3285                                 mhm_p, hm_d);
3286                 sector->setHeightmap(p_in_sector, hm);
3287
3288                 //hm->generateContinued(1.0, 0.5, corners);
3289                 hm->generateContinued(0.5, 0.5, corners);
3290
3291                 //hm->print();
3292         }
3293         
3294         // Add dummy objects
3295         core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3296         sector->setObjects(objects);
3297         
3298         /*
3299                 Insert to container
3300         */
3301         m_sectors.insert(p2d, sector);
3302         
3303         return sector;
3304 }
3305
3306 MapSector * ServerMap::emergeSector(v2s16 p2d,
3307                 core::map<v3s16, MapBlock*> &changed_blocks)
3308 {
3309         DSTACK("%s: p2d=(%d,%d)",
3310                         __FUNCTION_NAME,
3311                         p2d.X, p2d.Y);
3312         
3313         /*
3314                 Check chunk status
3315         */
3316         v2s16 chunkpos = sector_to_chunk(p2d);
3317         /*bool chunk_nonvolatile = false;
3318         MapChunk *chunk = getChunk(chunkpos);
3319         if(chunk && chunk->getIsVolatile() == false)
3320                 chunk_nonvolatile = true;*/
3321         bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3322
3323         /*
3324                 If chunk is not fully generated, generate chunk
3325         */
3326         if(chunk_nonvolatile == false)
3327         {
3328                 // Generate chunk and neighbors
3329                 generateChunk(chunkpos, changed_blocks);
3330         }
3331         
3332         /*
3333                 Return sector if it exists now
3334         */
3335         MapSector *sector = getSectorNoGenerateNoEx(p2d);
3336         if(sector != NULL)
3337                 return sector;
3338         
3339         /*
3340                 Try to load it from disk
3341         */
3342         if(loadSectorFull(p2d) == true)
3343         {
3344                 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3345                 if(sector == NULL)
3346                 {
3347                         dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3348                         throw InvalidPositionException("");
3349                 }
3350                 return sector;
3351         }
3352
3353         /*
3354                 generateChunk should have generated the sector
3355         */
3356         assert(0);
3357
3358         /*
3359                 Generate directly
3360         */
3361         //return generateSector();
3362 }
3363
3364 /*
3365         NOTE: This is not used for main map generation, only for blocks
3366         that are very high or low
3367 */
3368 MapBlock * ServerMap::generateBlock(
3369                 v3s16 p,
3370                 MapBlock *original_dummy,
3371                 ServerMapSector *sector,
3372                 core::map<v3s16, MapBlock*> &changed_blocks,
3373                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3374 )
3375 {
3376         DSTACK("%s: p=(%d,%d,%d)",
3377                         __FUNCTION_NAME,
3378                         p.X, p.Y, p.Z);
3379         
3380         /*dstream<<"generateBlock(): "
3381                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3382                         <<std::endl;*/
3383         
3384         MapBlock *block = original_dummy;
3385                         
3386         v2s16 p2d(p.X, p.Z);
3387         s16 block_y = p.Y;
3388         
3389         /*
3390                 Do not generate over-limit
3391         */
3392         if(blockpos_over_limit(p))
3393         {
3394                 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3395                 throw InvalidPositionException("generateBlock(): pos. over limit");
3396         }
3397
3398         /*
3399                 If block doesn't exist, create one.
3400                 If it exists, it is a dummy. In that case unDummify() it.
3401
3402                 NOTE: This already sets the map as the parent of the block
3403         */
3404         if(block == NULL)
3405         {
3406                 block = sector->createBlankBlockNoInsert(block_y);
3407         }
3408         else
3409         {
3410                 // Remove the block so that nobody can get a half-generated one.
3411                 sector->removeBlock(block);
3412                 // Allocate the block to contain the generated data
3413                 block->unDummify();
3414         }
3415         
3416         /*u8 water_material = CONTENT_WATER;
3417         if(g_settings.getBool("endless_water"))
3418                 water_material = CONTENT_WATERSOURCE;*/
3419         u8 water_material = CONTENT_WATERSOURCE;
3420         
3421         s32 lowest_ground_y = 32767;
3422         s32 highest_ground_y = -32768;
3423         
3424         // DEBUG
3425         //sector->printHeightmaps();
3426
3427         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3428         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3429         {
3430                 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3431
3432                 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3433                 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3434                 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3435                 {
3436                         dstream<<"WARNING: Surface height not found in sector "
3437                                         "for block that is being emerged"<<std::endl;
3438                         surface_y_f = 0.0;
3439                 }
3440
3441                 s16 surface_y = surface_y_f;
3442                 //avg_ground_y += surface_y;
3443                 if(surface_y < lowest_ground_y)
3444                         lowest_ground_y = surface_y;
3445                 if(surface_y > highest_ground_y)
3446                         highest_ground_y = surface_y;
3447
3448                 s32 surface_depth = 0;
3449                 
3450                 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3451                 
3452                 //float min_slope = 0.45;
3453                 //float max_slope = 0.85;
3454                 float min_slope = 0.60;
3455                 float max_slope = 1.20;
3456                 float min_slope_depth = 5.0;
3457                 float max_slope_depth = 0;
3458
3459                 if(slope < min_slope)
3460                         surface_depth = min_slope_depth;
3461                 else if(slope > max_slope)
3462                         surface_depth = max_slope_depth;
3463                 else
3464                         surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3465
3466                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3467                 {
3468                         s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3469                         MapNode n;
3470                         /*
3471                                 Calculate lighting
3472                                 
3473                                 NOTE: If there are some man-made structures above the
3474                                 newly created block, they won't be taken into account.
3475                         */
3476                         if(real_y > surface_y)
3477                                 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3478
3479                         /*
3480                                 Calculate material
3481                         */
3482
3483                         // If node is over heightmap y, it's air or water
3484                         if(real_y > surface_y)
3485                         {
3486                                 // If under water level, it's water
3487                                 if(real_y < WATER_LEVEL)
3488                                 {
3489                                         n.d = water_material;
3490                                         n.setLight(LIGHTBANK_DAY,
3491                                                         diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3492                                         /*
3493                                                 Add to transforming liquid queue (in case it'd
3494                                                 start flowing)
3495                                         */
3496                                         v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3497                                         m_transforming_liquid.push_back(real_pos);
3498                                 }
3499                                 // else air
3500                                 else
3501                                         n.d = CONTENT_AIR;
3502                         }
3503                         // Else it's ground or dungeons (air)
3504                         else
3505                         {
3506                                 // If it's surface_depth under ground, it's stone
3507                                 if(real_y <= surface_y - surface_depth)
3508                                 {
3509                                         n.d = CONTENT_STONE;
3510                                 }
3511                                 else
3512                                 {
3513                                         // It is mud if it is under the first ground
3514                                         // level or under water
3515                                         if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3516                                         {
3517                                                 n.d = CONTENT_MUD;
3518                                         }
3519                                         else
3520                                         {
3521                                                 n.d = CONTENT_GRASS;
3522                                         }
3523
3524                                         //n.d = CONTENT_MUD;
3525                                         
3526                                         /*// If under water level, it's mud
3527                                         if(real_y < WATER_LEVEL)
3528                                                 n.d = CONTENT_MUD;
3529                                         // Only the topmost node is grass
3530                                         else if(real_y <= surface_y - 1)
3531                                                 n.d = CONTENT_MUD;
3532                                         else
3533                                                 n.d = CONTENT_GRASS;*/
3534                                 }
3535                         }
3536
3537                         block->setNode(v3s16(x0,y0,z0), n);
3538                 }
3539         }
3540         
3541         /*
3542                 Calculate some helper variables
3543         */
3544         
3545         // Completely underground if the highest part of block is under lowest
3546         // ground height.
3547         // This has to be very sure; it's probably one too strict now but
3548         // that's just better.
3549         bool completely_underground =
3550                         block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3551
3552         bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3553
3554         bool mostly_underwater_surface = false;
3555         if(highest_ground_y < WATER_LEVEL
3556                         && some_part_underground && !completely_underground)
3557                 mostly_underwater_surface = true;
3558
3559         /*
3560                 Get local attributes
3561         */
3562
3563         //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3564
3565         float caves_amount = 0.5;
3566
3567 #if 0
3568         {
3569                 /*
3570                         NOTE: BEWARE: Too big amount of attribute points slows verything
3571                         down by a lot.
3572                         1 interpolation from 5000 points takes 2-3ms.
3573                 */
3574                 //TimeTaker timer("generateBlock() local attribute retrieval");
3575                 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3576                 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3577                 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3578         }
3579 #endif
3580
3581         //dstream<<"generateBlock(): Done"<<std::endl;
3582
3583         /*
3584                 Generate dungeons
3585         */
3586
3587         // Initialize temporary table
3588         const s32 ued = MAP_BLOCKSIZE;
3589         bool underground_emptiness[ued*ued*ued];
3590         for(s32 i=0; i<ued*ued*ued; i++)
3591         {
3592                 underground_emptiness[i] = 0;
3593         }
3594         
3595         // Fill table
3596 #if 1
3597         {
3598                 /*
3599                         Initialize orp and ors. Try to find if some neighboring
3600                         MapBlock has a tunnel ended in its side
3601                 */
3602
3603                 v3f orp(
3604                         (float)(myrand()%ued)+0.5,
3605                         (float)(myrand()%ued)+0.5,
3606                         (float)(myrand()%ued)+0.5
3607                 );
3608                 
3609                 bool found_existing = false;
3610
3611                 // Check z-
3612                 try
3613                 {
3614                         s16 z = -1;
3615                         for(s16 y=0; y<ued; y++)
3616                         for(s16 x=0; x<ued; x++)
3617                         {
3618                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3619                                 if(getNode(ap).d == CONTENT_AIR)
3620                                 {
3621                                         orp = v3f(x+1,y+1,0);
3622                                         found_existing = true;
3623                                         goto continue_generating;
3624                                 }
3625                         }
3626                 }
3627                 catch(InvalidPositionException &e){}
3628                 
3629                 // Check z+
3630                 try
3631                 {
3632                         s16 z = ued;
3633                         for(s16 y=0; y<ued; y++)
3634                         for(s16 x=0; x<ued; x++)
3635                         {
3636                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3637                                 if(getNode(ap).d == CONTENT_AIR)
3638                                 {
3639                                         orp = v3f(x+1,y+1,ued-1);
3640                                         found_existing = true;
3641                                         goto continue_generating;
3642                                 }
3643                         }
3644                 }
3645                 catch(InvalidPositionException &e){}
3646                 
3647                 // Check x-
3648                 try
3649                 {
3650                         s16 x = -1;
3651                         for(s16 y=0; y<ued; y++)
3652                         for(s16 z=0; z<ued; z++)
3653                         {
3654                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3655                                 if(getNode(ap).d == CONTENT_AIR)
3656                                 {
3657                                         orp = v3f(0,y+1,z+1);
3658                                         found_existing = true;
3659                                         goto continue_generating;
3660                                 }
3661                         }
3662                 }
3663                 catch(InvalidPositionException &e){}
3664                 
3665                 // Check x+
3666                 try
3667                 {
3668                         s16 x = ued;
3669                         for(s16 y=0; y<ued; y++)
3670                         for(s16 z=0; z<ued; z++)
3671                         {
3672                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3673                                 if(getNode(ap).d == CONTENT_AIR)
3674                                 {
3675                                         orp = v3f(ued-1,y+1,z+1);
3676                                         found_existing = true;
3677                                         goto continue_generating;
3678                                 }
3679                         }
3680                 }
3681                 catch(InvalidPositionException &e){}
3682
3683                 // Check y-
3684                 try
3685                 {
3686                         s16 y = -1;
3687                         for(s16 x=0; x<ued; x++)
3688                         for(s16 z=0; z<ued; z++)
3689                         {
3690                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3691                                 if(getNode(ap).d == CONTENT_AIR)
3692                                 {
3693                                         orp = v3f(x+1,0,z+1);
3694                                         found_existing = true;
3695                                         goto continue_generating;
3696                                 }
3697                         }
3698                 }
3699                 catch(InvalidPositionException &e){}
3700                 
3701                 // Check y+
3702                 try
3703                 {
3704                         s16 y = ued;
3705                         for(s16 x=0; x<ued; x++)
3706                         for(s16 z=0; z<ued; z++)
3707                         {
3708                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3709                                 if(getNode(ap).d == CONTENT_AIR)
3710                                 {
3711                                         orp = v3f(x+1,ued-1,z+1);
3712                                         found_existing = true;
3713                                         goto continue_generating;
3714                                 }
3715                         }
3716                 }
3717                 catch(InvalidPositionException &e){}
3718
3719 continue_generating:
3720                 
3721                 /*
3722                         Choose whether to actually generate dungeon
3723                 */
3724                 bool do_generate_dungeons = true;
3725                 // Don't generate if no part is underground
3726                 if(!some_part_underground)
3727                 {
3728                         do_generate_dungeons = false;
3729                 }
3730                 // Don't generate if mostly underwater surface
3731                 /*else if(mostly_underwater_surface)
3732                 {
3733                         do_generate_dungeons = false;
3734                 }*/
3735                 // Partly underground = cave
3736                 else if(!completely_underground)
3737                 {
3738                         do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3739                 }
3740                 // Found existing dungeon underground
3741                 else if(found_existing && completely_underground)
3742                 {
3743                         do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3744                 }
3745                 // Underground and no dungeons found
3746                 else
3747                 {
3748                         do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3749                 }
3750
3751                 if(do_generate_dungeons)
3752                 {
3753                         /*
3754                                 Generate some tunnel starting from orp and ors
3755                         */
3756                         for(u16 i=0; i<3; i++)
3757                         {
3758                                 v3f rp(
3759                                         (float)(myrand()%ued)+0.5,
3760                                         (float)(myrand()%ued)+0.5,
3761                                         (float)(myrand()%ued)+0.5
3762                                 );
3763                                 s16 min_d = 0;
3764                                 s16 max_d = 4;
3765                                 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3766                                 
3767                                 v3f vec = rp - orp;
3768
3769                                 for(float f=0; f<1.0; f+=0.04)
3770                                 {
3771                                         v3f fp = orp + vec * f;
3772                                         v3s16 cp(fp.X, fp.Y, fp.Z);
3773                                         s16 d0 = -rs/2;
3774                                         s16 d1 = d0 + rs - 1;
3775                                         for(s16 z0=d0; z0<=d1; z0++)
3776                                         {
3777                                                 s16 si = rs - abs(z0);
3778                                                 for(s16 x0=-si; x0<=si-1; x0++)
3779                                                 {
3780                                                         s16 si2 = rs - abs(x0);
3781                                                         for(s16 y0=-si2+1; y0<=si2-1; y0++)
3782                                                         {
3783                                                                 s16 z = cp.Z + z0;
3784                                                                 s16 y = cp.Y + y0;
3785                                                                 s16 x = cp.X + x0;
3786                                                                 v3s16 p(x,y,z);
3787                                                                 if(isInArea(p, ued) == false)
3788                                                                         continue;
3789                                                                 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3790                                                         }
3791                                                 }
3792                                         }
3793                                 }
3794
3795                                 orp = rp;
3796                         }
3797                 }
3798         }
3799 #endif
3800
3801         // Set to true if has caves.
3802         // Set when some non-air is changed to air when making caves.
3803         bool has_dungeons = false;
3804
3805         /*
3806                 Apply temporary cave data to block
3807         */
3808
3809         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3810         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3811         {
3812                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3813                 {
3814                         MapNode n = block->getNode(v3s16(x0,y0,z0));
3815
3816                         // Create dungeons
3817                         if(underground_emptiness[
3818                                         ued*ued*(z0*ued/MAP_BLOCKSIZE)
3819                                         +ued*(y0*ued/MAP_BLOCKSIZE)
3820                                         +(x0*ued/MAP_BLOCKSIZE)])
3821                         {
3822                                 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
3823                                 {
3824                                         // Has now caves
3825                                         has_dungeons = true;
3826                                         // Set air to node
3827                                         n.d = CONTENT_AIR;
3828                                 }
3829                         }
3830
3831                         block->setNode(v3s16(x0,y0,z0), n);
3832                 }
3833         }
3834         
3835         /*
3836                 This is used for guessing whether or not the block should
3837                 receive sunlight from the top if the block above doesn't exist
3838         */
3839         block->setIsUnderground(completely_underground);
3840
3841         /*
3842                 Force lighting update if some part of block is partly
3843                 underground and has caves.
3844         */
3845         /*if(some_part_underground && !completely_underground && has_dungeons)
3846         {
3847                 //dstream<<"Half-ground caves"<<std::endl;
3848                 lighting_invalidated_blocks[block->getPos()] = block;
3849         }*/
3850         
3851         // DEBUG: Always update lighting
3852         //lighting_invalidated_blocks[block->getPos()] = block;
3853
3854         /*
3855                 Add some minerals
3856         */
3857
3858         if(some_part_underground)
3859         {
3860                 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
3861
3862                 /*
3863                         Add meseblocks
3864                 */
3865                 for(s16 i=0; i<underground_level/4 + 1; i++)
3866                 {
3867                         if(myrand()%50 == 0)
3868                         {
3869                                 v3s16 cp(
3870                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3871                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3872                                         (myrand()%(MAP_BLOCKSIZE-2))+1
3873                                 );
3874
3875                                 MapNode n;
3876                                 n.d = CONTENT_MESE;
3877                                 
3878                                 for(u16 i=0; i<27; i++)
3879                                 {
3880                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3881                                                 if(myrand()%8 == 0)
3882                                                         block->setNode(cp+g_27dirs[i], n);
3883                                 }
3884                         }
3885                 }
3886
3887                 /*
3888                         Add coal
3889                 */
3890                 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
3891                 u16 coal_rareness = 60 / coal_amount;
3892                 if(coal_rareness == 0)
3893                         coal_rareness = 1;
3894                 if(myrand()%coal_rareness == 0)
3895                 {
3896                         u16 a = myrand() % 16;
3897                         u16 amount = coal_amount * a*a*a / 1000;
3898                         for(s16 i=0; i<amount; i++)
3899                         {
3900                                 v3s16 cp(
3901                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3902                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3903                                         (myrand()%(MAP_BLOCKSIZE-2))+1
3904                                 );
3905
3906                                 MapNode n;
3907                                 n.d = CONTENT_STONE;
3908                                 n.param = MINERAL_COAL;
3909
3910                                 for(u16 i=0; i<27; i++)
3911                                 {
3912                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3913                                                 if(myrand()%8 == 0)
3914                                                         block->setNode(cp+g_27dirs[i], n);
3915                                 }
3916                         }
3917                 }
3918
3919                 /*
3920                         Add iron
3921                 */
3922                 //TODO: change to iron_amount or whatever
3923                 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
3924                 u16 iron_rareness = 60 / iron_amount;
3925                 if(iron_rareness == 0)
3926                         iron_rareness = 1;
3927                 if(myrand()%iron_rareness == 0)
3928                 {
3929                         u16 a = myrand() % 16;
3930                         u16 amount = iron_amount * a*a*a / 1000;
3931                         for(s16 i=0; i<amount; i++)
3932                         {
3933                                 v3s16 cp(
3934                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3935                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3936                                         (myrand()%(MAP_BLOCKSIZE-2))+1
3937                                 );
3938
3939                                 MapNode n;
3940                                 n.d = CONTENT_STONE;
3941                                 n.param = MINERAL_IRON;
3942
3943                                 for(u16 i=0; i<27; i++)
3944                                 {
3945                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3946                                                 if(myrand()%8 == 0)
3947                                                         block->setNode(cp+g_27dirs[i], n);
3948                                 }
3949                         }
3950                 }
3951         }
3952         
3953         /*
3954                 Create a few rats in empty blocks underground
3955         */
3956         if(completely_underground)
3957         {
3958                 //for(u16 i=0; i<2; i++)
3959                 {
3960                         v3s16 cp(
3961                                 (myrand()%(MAP_BLOCKSIZE-2))+1,
3962                                 (myrand()%(MAP_BLOCKSIZE-2))+1,
3963                                 (myrand()%(MAP_BLOCKSIZE-2))+1
3964                         );
3965
3966                         // Check that the place is empty
3967                         //if(!is_ground_content(block->getNode(cp).d))
3968                         if(1)
3969                         {
3970                                 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
3971                                 block->addObject(obj);
3972                         }
3973                 }
3974         }
3975         
3976         /*
3977                 Add block to sector.
3978         */
3979         sector->insertBlock(block);
3980         
3981         /*
3982                 Sector object stuff
3983         */
3984                 
3985         // An y-wise container of changed blocks
3986         core::map<s16, MapBlock*> changed_blocks_sector;
3987
3988         /*
3989                 Check if any sector's objects can be placed now.
3990                 If so, place them.
3991         */
3992         core::map<v3s16, u8> *objects = sector->getObjects();
3993         core::list<v3s16> objects_to_remove;
3994         for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
3995                         i.atEnd() == false; i++)
3996         {
3997                 v3s16 p = i.getNode()->getKey();
3998                 v2s16 p2d(p.X,p.Z);
3999                 u8 d = i.getNode()->getValue();
4000
4001                 // Ground level point (user for stuff that is on ground)
4002                 v3s16 gp = p;
4003                 bool ground_found = true;
4004                 
4005                 // Search real ground level
4006                 try{
4007                         for(;;)
4008                         {
4009                                 MapNode n = sector->getNode(gp);
4010
4011                                 // If not air, go one up and continue to placing the tree
4012                                 if(n.d != CONTENT_AIR)
4013                                 {
4014                                         gp += v3s16(0,1,0);
4015                                         break;
4016                                 }
4017
4018                                 // If air, go one down
4019                                 gp += v3s16(0,-1,0);
4020                         }
4021                 }catch(InvalidPositionException &e)
4022                 {
4023                         // Ground not found.
4024                         ground_found = false;
4025                         // This is most close to ground
4026                         gp += v3s16(0,1,0);
4027                 }
4028
4029                 try
4030                 {
4031
4032                 if(d == SECTOR_OBJECT_TEST)
4033                 {
4034                         if(sector->isValidArea(p + v3s16(0,0,0),
4035                                         p + v3s16(0,0,0), &changed_blocks_sector))
4036                         {
4037                                 MapNode n;
4038                                 n.d = CONTENT_TORCH;
4039                                 sector->setNode(p, n);
4040                                 objects_to_remove.push_back(p);
4041                         }
4042                 }
4043                 else if(d == SECTOR_OBJECT_TREE_1)
4044                 {
4045                         if(ground_found == false)
4046                                 continue;
4047
4048                         v3s16 p_min = gp + v3s16(-1,0,-1);
4049                         v3s16 p_max = gp + v3s16(1,5,1);
4050                         if(sector->isValidArea(p_min, p_max,
4051                                         &changed_blocks_sector))
4052                         {
4053                                 MapNode n;
4054                                 n.d = CONTENT_TREE;
4055                                 sector->setNode(gp+v3s16(0,0,0), n);
4056                                 sector->setNode(gp+v3s16(0,1,0), n);
4057                                 sector->setNode(gp+v3s16(0,2,0), n);
4058                                 sector->setNode(gp+v3s16(0,3,0), n);
4059
4060                                 n.d = CONTENT_LEAVES;
4061
4062                                 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4063
4064                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4065                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4066                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4067                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4068                                 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4069                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4070                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4071                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4072
4073                                 sector->setNode(gp+v3s16(0,4,0), n);
4074                                 
4075                                 sector->setNode(gp+v3s16(-1,4,0), n);
4076                                 sector->setNode(gp+v3s16(1,4,0), n);
4077                                 sector->setNode(gp+v3s16(0,4,-1), n);
4078                                 sector->setNode(gp+v3s16(0,4,1), n);
4079                                 sector->setNode(gp+v3s16(1,4,1), n);
4080                                 sector->setNode(gp+v3s16(-1,4,1), n);
4081                                 sector->setNode(gp+v3s16(-1,4,-1), n);
4082                                 sector->setNode(gp+v3s16(1,4,-1), n);
4083
4084                                 sector->setNode(gp+v3s16(-1,3,0), n);
4085                                 sector->setNode(gp+v3s16(1,3,0), n);
4086                                 sector->setNode(gp+v3s16(0,3,-1), n);
4087                                 sector->setNode(gp+v3s16(0,3,1), n);
4088                                 sector->setNode(gp+v3s16(1,3,1), n);
4089                                 sector->setNode(gp+v3s16(-1,3,1), n);
4090                                 sector->setNode(gp+v3s16(-1,3,-1), n);
4091                                 sector->setNode(gp+v3s16(1,3,-1), n);
4092                                 
4093                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4094                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4095                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4096                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4097                                 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4098                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4099                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4100                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4101                                 
4102                                 // Objects are identified by wanted position
4103                                 objects_to_remove.push_back(p);
4104                                 
4105                                 // Lighting has to be recalculated for this one.
4106                                 sector->getBlocksInArea(p_min, p_max, 
4107                                                 lighting_invalidated_blocks);
4108                         }
4109                 }
4110                 else if(d == SECTOR_OBJECT_BUSH_1)
4111                 {
4112                         if(ground_found == false)
4113                                 continue;
4114                         
4115                         if(sector->isValidArea(gp + v3s16(0,0,0),
4116                                         gp + v3s16(0,0,0), &changed_blocks_sector))
4117                         {
4118                                 MapNode n;
4119                                 n.d = CONTENT_LEAVES;
4120                                 sector->setNode(gp+v3s16(0,0,0), n);
4121                                 
4122                                 // Objects are identified by wanted position
4123                                 objects_to_remove.push_back(p);
4124                         }
4125                 }
4126                 else if(d == SECTOR_OBJECT_RAVINE)
4127                 {
4128                         s16 maxdepth = -20;
4129                         v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4130                         v3s16 p_max = p + v3s16(6,6,6);
4131                         if(sector->isValidArea(p_min, p_max,
4132                                         &changed_blocks_sector))
4133                         {
4134                                 MapNode n;
4135                                 n.d = CONTENT_STONE;
4136                                 MapNode n2;
4137                                 n2.d = CONTENT_AIR;
4138                                 s16 depth = maxdepth + (myrand()%10);
4139                                 s16 z = 0;
4140                                 s16 minz = -6 - (-2);
4141                                 s16 maxz = 6 -1;
4142                                 for(s16 x=-6; x<=6; x++)
4143                                 {
4144                                         z += -1 + (myrand()%3);
4145                                         if(z < minz)
4146                                                 z = minz;
4147                                         if(z > maxz)
4148                                                 z = maxz;
4149                                         for(s16 y=depth+(myrand()%2); y<=6; y++)
4150                                         {
4151                                                 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4152                                                                 <<std::endl;*/
4153                                                 {
4154                                                         v3s16 p2 = p + v3s16(x,y,z-2);
4155                                                         //if(is_ground_content(sector->getNode(p2).d))
4156                                                         if(content_features(sector->getNode(p2).d).walkable)
4157                                                                 sector->setNode(p2, n);
4158                                                 }
4159                                                 {
4160                                                         v3s16 p2 = p + v3s16(x,y,z-1);
4161                                                         if(content_features(sector->getNode(p2).d).walkable)
4162                                                                 sector->setNode(p2, n2);
4163                                                 }
4164                                                 {
4165                                                         v3s16 p2 = p + v3s16(x,y,z+0);
4166                                                         if(content_features(sector->getNode(p2).d).walkable)
4167                                                                 sector->setNode(p2, n2);
4168                                                 }
4169                                                 {
4170                                                         v3s16 p2 = p + v3s16(x,y,z+1);
4171                                                         if(content_features(sector->getNode(p2).d).walkable)
4172                                                                 sector->setNode(p2, n);
4173                                                 }
4174
4175                                                 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4176                                                 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4177                                         }
4178                                 }
4179                                 
4180                                 objects_to_remove.push_back(p);
4181                                 
4182                                 // Lighting has to be recalculated for this one.
4183                                 sector->getBlocksInArea(p_min, p_max, 
4184                                                 lighting_invalidated_blocks);
4185                         }
4186                 }
4187                 else
4188                 {
4189                         dstream<<"ServerMap::generateBlock(): "
4190                                         "Invalid heightmap object"
4191                                         <<std::endl;
4192                 }
4193
4194                 }//try
4195                 catch(InvalidPositionException &e)
4196                 {
4197                         dstream<<"WARNING: "<<__FUNCTION_NAME
4198                                         <<": while inserting object "<<(int)d
4199                                         <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4200                                         <<" InvalidPositionException.what()="
4201                                         <<e.what()<<std::endl;
4202                         // This is not too fatal and seems to happen sometimes.
4203                         assert(0);
4204                 }
4205         }
4206
4207         for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4208                         i != objects_to_remove.end(); i++)
4209         {
4210                 objects->remove(*i);
4211         }
4212         
4213         /*
4214                 Translate sector's changed blocks to global changed blocks
4215         */
4216         
4217         for(core::map<s16, MapBlock*>::Iterator
4218                         i = changed_blocks_sector.getIterator();
4219                         i.atEnd() == false; i++)
4220         {
4221                 MapBlock *block = i.getNode()->getValue();
4222
4223                 changed_blocks.insert(block->getPos(), block);
4224         }
4225
4226         block->setLightingExpired(true);
4227         
4228 #if 0
4229         /*
4230                 Debug information
4231         */
4232         dstream
4233         <<"lighting_invalidated_blocks.size()"
4234         <<", has_dungeons"
4235         <<", completely_ug"
4236         <<", some_part_ug"
4237         <<"  "<<lighting_invalidated_blocks.size()
4238         <<", "<<has_dungeons
4239         <<", "<<completely_underground
4240         <<", "<<some_part_underground
4241         <<std::endl;
4242 #endif
4243
4244         return block;
4245 }
4246
4247 MapBlock * ServerMap::createBlock(v3s16 p)
4248 {
4249         DSTACK("%s: p=(%d,%d,%d)",
4250                         __FUNCTION_NAME, p.X, p.Y, p.Z);
4251         
4252         v2s16 p2d(p.X, p.Z);
4253         s16 block_y = p.Y;
4254         /*
4255                 This will create or load a sector if not found in memory.
4256                 If block exists on disk, it will be loaded.
4257
4258                 NOTE: On old save formats, this will be slow, as it generates
4259                       lighting on blocks for them.
4260         */
4261         ServerMapSector *sector;
4262         try{
4263                 sector = (ServerMapSector*)createSector(p2d);
4264                 assert(sector->getId() == MAPSECTOR_SERVER);
4265         }
4266         /*catch(InvalidPositionException &e)
4267         {
4268                 dstream<<"createBlock: createSector() failed"<<std::endl;
4269                 throw e;
4270         }*/
4271         catch(std::exception &e)
4272         {
4273                 dstream<<"createBlock: createSector() failed: "
4274                                 <<e.what()<<std::endl;
4275                 throw e;
4276         }
4277
4278         /*
4279                 Try to get a block from the sector
4280         */
4281
4282         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4283         if(block)
4284                 return block;
4285         // Create blank
4286         block = sector->createBlankBlock(block_y);
4287         return block;
4288 }
4289
4290 MapBlock * ServerMap::emergeBlock(
4291                 v3s16 p,
4292                 bool only_from_disk,
4293                 core::map<v3s16, MapBlock*> &changed_blocks,
4294                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4295 )
4296 {
4297         DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4298                         __FUNCTION_NAME,
4299                         p.X, p.Y, p.Z, only_from_disk);
4300         
4301         v2s16 p2d(p.X, p.Z);
4302         s16 block_y = p.Y;
4303         /*
4304                 This will create or load a sector if not found in memory.
4305                 If block exists on disk, it will be loaded.
4306
4307                 NOTE: On old save formats, this will be slow, as it generates
4308                       lighting on blocks for them.
4309         */
4310         ServerMapSector *sector;
4311         try{
4312                 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4313                 assert(sector->getId() == MAPSECTOR_SERVER);
4314         }
4315         catch(std::exception &e)
4316         {
4317                 dstream<<"emergeBlock: emergeSector() failed: "
4318                                 <<e.what()<<std::endl;
4319                 throw e;
4320         }
4321
4322         /*
4323                 Try to get a block from the sector
4324         */
4325
4326         bool does_not_exist = false;
4327         bool lighting_expired = false;
4328         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4329
4330         if(block == NULL)
4331         {
4332                 does_not_exist = true;
4333         }
4334         else if(block->isDummy() == true)
4335         {
4336                 does_not_exist = true;
4337         }
4338         else if(block->getLightingExpired())
4339         {
4340                 lighting_expired = true;
4341         }
4342         else
4343         {
4344                 // Valid block
4345                 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4346                 return block;
4347         }
4348         
4349         /*
4350                 If block was not found on disk and not going to generate a
4351                 new one, make sure there is a dummy block in place.
4352         */
4353         if(only_from_disk && (does_not_exist || lighting_expired))
4354         {
4355                 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4356
4357                 if(block == NULL)
4358                 {
4359                         // Create dummy block
4360                         block = new MapBlock(this, p, true);
4361
4362                         // Add block to sector
4363                         sector->insertBlock(block);
4364                 }
4365                 // Done.
4366                 return block;
4367         }
4368
4369         //dstream<<"Not found on disk, generating."<<std::endl;
4370         // 0ms
4371         //TimeTaker("emergeBlock() generate");
4372
4373         //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4374
4375         /*
4376                 If the block doesn't exist, generate the block.
4377         */
4378         if(does_not_exist)
4379         {
4380                 block = generateBlock(p, block, sector, changed_blocks,
4381                                 lighting_invalidated_blocks); 
4382         }
4383
4384         if(lighting_expired)
4385         {
4386                 lighting_invalidated_blocks.insert(p, block);
4387         }
4388
4389         /*
4390                 Initially update sunlight
4391         */
4392         
4393         {
4394                 core::map<v3s16, bool> light_sources;
4395                 bool black_air_left = false;
4396                 bool bottom_invalid =
4397                                 block->propagateSunlight(light_sources, true,
4398                                 &black_air_left, true);
4399
4400                 // If sunlight didn't reach everywhere and part of block is
4401                 // above ground, lighting has to be properly updated
4402                 //if(black_air_left && some_part_underground)
4403                 if(black_air_left)
4404                 {
4405                         lighting_invalidated_blocks[block->getPos()] = block;
4406                 }
4407
4408                 if(bottom_invalid)
4409                 {
4410                         lighting_invalidated_blocks[block->getPos()] = block;
4411                 }
4412         }
4413         
4414         /*
4415                 Debug mode operation
4416         */
4417         bool haxmode = g_settings.getBool("haxmode");
4418         if(haxmode)
4419         {
4420                 // Don't calculate lighting at all
4421                 //lighting_invalidated_blocks.clear();
4422         }
4423
4424         return block;
4425 }
4426
4427 void ServerMap::createDir(std::string path)
4428 {
4429         if(fs::CreateDir(path) == false)
4430         {
4431                 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4432                                 <<"\""<<path<<"\""<<std::endl;
4433                 throw BaseException("ServerMap failed to create directory");
4434         }
4435 }
4436
4437 std::string ServerMap::getSectorSubDir(v2s16 pos)
4438 {
4439         char cc[9];
4440         snprintf(cc, 9, "%.4x%.4x",
4441                         (unsigned int)pos.X&0xffff,
4442                         (unsigned int)pos.Y&0xffff);
4443
4444         return std::string(cc);
4445 }
4446
4447 std::string ServerMap::getSectorDir(v2s16 pos)
4448 {
4449         return m_savedir + "/sectors/" + getSectorSubDir(pos);
4450 }
4451
4452 v2s16 ServerMap::getSectorPos(std::string dirname)
4453 {
4454         if(dirname.size() != 8)
4455                 throw InvalidFilenameException("Invalid sector directory name");
4456         unsigned int x, y;
4457         int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4458         if(r != 2)
4459                 throw InvalidFilenameException("Invalid sector directory name");
4460         v2s16 pos((s16)x, (s16)y);
4461         return pos;
4462 }
4463
4464 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4465 {
4466         v2s16 p2d = getSectorPos(sectordir);
4467
4468         if(blockfile.size() != 4){
4469                 throw InvalidFilenameException("Invalid block filename");
4470         }
4471         unsigned int y;
4472         int r = sscanf(blockfile.c_str(), "%4x", &y);
4473         if(r != 1)
4474                 throw InvalidFilenameException("Invalid block filename");
4475         return v3s16(p2d.X, y, p2d.Y);
4476 }
4477
4478 // Debug helpers
4479 #define ENABLE_SECTOR_SAVING 1
4480 #define ENABLE_SECTOR_LOADING 1
4481 #define ENABLE_BLOCK_SAVING 1
4482 #define ENABLE_BLOCK_LOADING 1
4483
4484 void ServerMap::save(bool only_changed)
4485 {
4486         DSTACK(__FUNCTION_NAME);
4487         if(m_map_saving_enabled == false)
4488         {
4489                 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4490                 return;
4491         }
4492         
4493         if(only_changed == false)
4494                 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4495                                 <<std::endl;
4496         
4497         saveMasterHeightmap();
4498         
4499         u32 sector_meta_count = 0;
4500         u32 block_count = 0;
4501         
4502         { //sectorlock
4503         JMutexAutoLock lock(m_sector_mutex);
4504         
4505         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4506         for(; i.atEnd() == false; i++)
4507         {
4508                 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4509                 assert(sector->getId() == MAPSECTOR_SERVER);
4510                 
4511                 if(ENABLE_SECTOR_SAVING)
4512                 {
4513                         if(sector->differs_from_disk || only_changed == false)
4514                         {
4515                                 saveSectorMeta(sector);
4516                                 sector_meta_count++;
4517                         }
4518                 }
4519                 if(ENABLE_BLOCK_SAVING)
4520                 {
4521                         core::list<MapBlock*> blocks;
4522                         sector->getBlocks(blocks);
4523                         core::list<MapBlock*>::Iterator j;
4524                         for(j=blocks.begin(); j!=blocks.end(); j++)
4525                         {
4526                                 MapBlock *block = *j;
4527                                 if(block->getChangedFlag() || only_changed == false)
4528                                 {
4529                                         saveBlock(block);
4530                                         block_count++;
4531                                 }
4532                         }
4533                 }
4534         }
4535
4536         }//sectorlock
4537         
4538         /*
4539                 Only print if something happened or saved whole map
4540         */
4541         if(only_changed == false || sector_meta_count != 0
4542                         || block_count != 0)
4543         {
4544                 dstream<<DTIME<<"ServerMap: Written: "
4545                                 <<sector_meta_count<<" sector metadata files, "
4546                                 <<block_count<<" block files"
4547                                 <<std::endl;
4548         }
4549 }
4550
4551 void ServerMap::loadAll()
4552 {
4553         DSTACK(__FUNCTION_NAME);
4554         dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4555
4556         loadMasterHeightmap();
4557
4558         std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4559
4560         dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4561         
4562         JMutexAutoLock lock(m_sector_mutex);
4563         
4564         s32 counter = 0;
4565         s32 printed_counter = -100000;
4566         s32 count = list.size();
4567
4568         std::vector<fs::DirListNode>::iterator i;
4569         for(i=list.begin(); i!=list.end(); i++)
4570         {
4571                 if(counter > printed_counter + 10)
4572                 {
4573                         dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4574                         printed_counter = counter;
4575                 }
4576                 counter++;
4577
4578                 MapSector *sector = NULL;
4579
4580                 // We want directories
4581                 if(i->dir == false)
4582                         continue;
4583                 try{
4584                         sector = loadSectorMeta(i->name);
4585                 }
4586                 catch(InvalidFilenameException &e)
4587                 {
4588                         // This catches unknown crap in directory
4589                 }
4590                 
4591                 if(ENABLE_BLOCK_LOADING)
4592                 {
4593                         std::vector<fs::DirListNode> list2 = fs::GetDirListing
4594                                         (m_savedir+"/sectors/"+i->name);
4595                         std::vector<fs::DirListNode>::iterator i2;
4596                         for(i2=list2.begin(); i2!=list2.end(); i2++)
4597                         {
4598                                 // We want files
4599                                 if(i2->dir)
4600                                         continue;
4601                                 try{
4602                                         loadBlock(i->name, i2->name, sector);
4603                                 }
4604                                 catch(InvalidFilenameException &e)
4605                                 {
4606                                         // This catches unknown crap in directory
4607                                 }
4608                         }
4609                 }
4610         }
4611         dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4612 }
4613
4614 void ServerMap::saveMasterHeightmap()
4615 {
4616         DSTACK(__FUNCTION_NAME);
4617         createDir(m_savedir);
4618         
4619         std::string fullpath = m_savedir + "/master_heightmap";
4620         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4621         if(o.good() == false)
4622                 throw FileNotGoodException("Cannot open master heightmap");
4623         
4624         // Format used for writing
4625         u8 version = SER_FMT_VER_HIGHEST;
4626
4627 #if 0
4628         SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4629         /*
4630                 [0] u8 serialization version
4631                 [1] X master heightmap
4632         */
4633         u32 fullsize = 1 + hmdata.getSize();
4634         SharedBuffer<u8> data(fullsize);
4635
4636         data[0] = version;
4637         memcpy(&data[1], *hmdata, hmdata.getSize());
4638
4639         o.write((const char*)*data, fullsize);
4640 #endif
4641         
4642         m_heightmap->serialize(o, version);
4643 }
4644
4645 void ServerMap::loadMasterHeightmap()
4646 {
4647         DSTACK(__FUNCTION_NAME);
4648         std::string fullpath = m_savedir + "/master_heightmap";
4649         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4650         if(is.good() == false)
4651                 throw FileNotGoodException("Cannot open master heightmap");
4652         
4653         if(m_heightmap != NULL)
4654                 delete m_heightmap;
4655                 
4656         m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4657 }
4658
4659 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4660 {
4661         DSTACK(__FUNCTION_NAME);
4662         // Format used for writing
4663         u8 version = SER_FMT_VER_HIGHEST;
4664         // Get destination
4665         v2s16 pos = sector->getPos();
4666         createDir(m_savedir);
4667         createDir(m_savedir+"/sectors");
4668         std::string dir = getSectorDir(pos);
4669         createDir(dir);
4670         
4671         std::string fullpath = dir + "/heightmap";
4672         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4673         if(o.good() == false)
4674                 throw FileNotGoodException("Cannot open master heightmap");
4675
4676         sector->serialize(o, version);
4677         
4678         sector->differs_from_disk = false;
4679 }
4680
4681 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4682 {
4683         DSTACK(__FUNCTION_NAME);
4684         // Get destination
4685         v2s16 p2d = getSectorPos(dirname);
4686         std::string dir = m_savedir + "/sectors/" + dirname;
4687         
4688         std::string fullpath = dir + "/heightmap";
4689         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4690         if(is.good() == false)
4691                 throw FileNotGoodException("Cannot open sector heightmap");
4692
4693         ServerMapSector *sector = ServerMapSector::deSerialize
4694                         (is, this, p2d, &m_hwrapper, m_sectors);
4695         
4696         sector->differs_from_disk = false;
4697
4698         return sector;
4699 }
4700
4701 bool ServerMap::loadSectorFull(v2s16 p2d)
4702 {
4703         DSTACK(__FUNCTION_NAME);
4704         std::string sectorsubdir = getSectorSubDir(p2d);
4705
4706         MapSector *sector = NULL;
4707
4708         JMutexAutoLock lock(m_sector_mutex);
4709
4710         try{
4711                 sector = loadSectorMeta(sectorsubdir);
4712         }
4713         catch(InvalidFilenameException &e)
4714         {
4715                 return false;
4716         }
4717         catch(FileNotGoodException &e)
4718         {
4719                 return false;
4720         }
4721         catch(std::exception &e)
4722         {
4723                 return false;
4724         }
4725
4726         if(ENABLE_BLOCK_LOADING)
4727         {
4728                 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4729                                 (m_savedir+"/sectors/"+sectorsubdir);
4730                 std::vector<fs::DirListNode>::iterator i2;
4731                 for(i2=list2.begin(); i2!=list2.end(); i2++)
4732                 {
4733                         // We want files
4734                         if(i2->dir)
4735                                 continue;
4736                         try{
4737                                 loadBlock(sectorsubdir, i2->name, sector);
4738                         }
4739                         catch(InvalidFilenameException &e)
4740                         {
4741                                 // This catches unknown crap in directory
4742                         }
4743                 }
4744         }
4745         return true;
4746 }
4747
4748 #if 0
4749 bool ServerMap::deFlushSector(v2s16 p2d)
4750 {
4751         DSTACK(__FUNCTION_NAME);
4752         // See if it already exists in memory
4753         try{
4754                 MapSector *sector = getSectorNoGenerate(p2d);
4755                 return true;
4756         }
4757         catch(InvalidPositionException &e)
4758         {
4759                 /*
4760                         Try to load the sector from disk.
4761                 */
4762                 if(loadSectorFull(p2d) == true)
4763                 {
4764                         return true;
4765                 }
4766         }
4767         return false;
4768 }
4769 #endif
4770
4771 void ServerMap::saveBlock(MapBlock *block)
4772 {
4773         DSTACK(__FUNCTION_NAME);
4774         /*
4775                 Dummy blocks are not written
4776         */
4777         if(block->isDummy())
4778         {
4779                 /*v3s16 p = block->getPos();
4780                 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4781                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4782                 return;
4783         }
4784
4785         // Format used for writing
4786         u8 version = SER_FMT_VER_HIGHEST;
4787         // Get destination
4788         v3s16 p3d = block->getPos();
4789         v2s16 p2d(p3d.X, p3d.Z);
4790         createDir(m_savedir);
4791         createDir(m_savedir+"/sectors");
4792         std::string dir = getSectorDir(p2d);
4793         createDir(dir);
4794         
4795         // Block file is map/sectors/xxxxxxxx/xxxx
4796         char cc[5];
4797         snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4798         std::string fullpath = dir + "/" + cc;
4799         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4800         if(o.good() == false)
4801                 throw FileNotGoodException("Cannot open block data");
4802
4803         /*
4804                 [0] u8 serialization version
4805                 [1] data
4806         */
4807         o.write((char*)&version, 1);
4808         
4809         block->serialize(o, version);
4810
4811         /*
4812                 Versions up from 9 have block objects.
4813         */
4814         if(version >= 9)
4815         {
4816                 block->serializeObjects(o, version);
4817         }
4818         
4819         // We just wrote it to the disk
4820         block->resetChangedFlag();
4821 }
4822
4823 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4824 {
4825         DSTACK(__FUNCTION_NAME);
4826
4827         try{
4828
4829         // Block file is map/sectors/xxxxxxxx/xxxx
4830         std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4831         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4832         if(is.good() == false)
4833                 throw FileNotGoodException("Cannot open block file");
4834
4835         v3s16 p3d = getBlockPos(sectordir, blockfile);
4836         v2s16 p2d(p3d.X, p3d.Z);
4837         
4838         assert(sector->getPos() == p2d);
4839         
4840         u8 version = SER_FMT_VER_INVALID;
4841         is.read((char*)&version, 1);
4842
4843         /*u32 block_size = MapBlock::serializedLength(version);
4844         SharedBuffer<u8> data(block_size);
4845         is.read((char*)*data, block_size);*/
4846
4847         // This will always return a sector because we're the server
4848         //MapSector *sector = emergeSector(p2d);
4849
4850         MapBlock *block = NULL;
4851         bool created_new = false;
4852         try{
4853                 block = sector->getBlockNoCreate(p3d.Y);
4854         }
4855         catch(InvalidPositionException &e)
4856         {
4857                 block = sector->createBlankBlockNoInsert(p3d.Y);
4858                 created_new = true;
4859         }
4860         
4861         // deserialize block data
4862         block->deSerialize(is, version);
4863         
4864         /*
4865                 Versions up from 9 have block objects.
4866         */
4867         if(version >= 9)
4868         {
4869                 block->updateObjects(is, version, NULL, 0);
4870         }
4871
4872         if(created_new)
4873                 sector->insertBlock(block);
4874         
4875         /*
4876                 Convert old formats to new and save
4877         */
4878
4879         // Save old format blocks in new format
4880         if(version < SER_FMT_VER_HIGHEST)
4881         {
4882                 saveBlock(block);
4883         }
4884         
4885         // We just loaded it from the disk, so it's up-to-date.
4886         block->resetChangedFlag();
4887
4888         }
4889         catch(SerializationError &e)
4890         {
4891                 dstream<<"WARNING: Invalid block data on disk "
4892                                 "(SerializationError). Ignoring."
4893                                 <<std::endl;
4894         }
4895 }
4896
4897 // Gets from master heightmap
4898 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4899 {
4900         assert(m_heightmap != NULL);
4901         /*
4902                 Corner definition:
4903                 v2s16(0,0),
4904                 v2s16(1,0),
4905                 v2s16(1,1),
4906                 v2s16(0,1),
4907         */
4908         corners[0] = m_heightmap->getGroundHeight
4909                         ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4910         corners[1] = m_heightmap->getGroundHeight
4911                         ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4912         corners[2] = m_heightmap->getGroundHeight
4913                         ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4914         corners[3] = m_heightmap->getGroundHeight
4915                         ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
4916 }
4917
4918 void ServerMap::PrintInfo(std::ostream &out)
4919 {
4920         out<<"ServerMap: ";
4921 }
4922
4923 #ifndef SERVER
4924
4925 /*
4926         ClientMap
4927 */
4928
4929 ClientMap::ClientMap(
4930                 Client *client,
4931                 MapDrawControl &control,
4932                 scene::ISceneNode* parent,
4933                 scene::ISceneManager* mgr,
4934                 s32 id
4935 ):
4936         Map(dout_client),
4937         scene::ISceneNode(parent, mgr, id),
4938         m_client(client),
4939         mesh(NULL),
4940         m_control(control)
4941 {
4942         mesh_mutex.Init();
4943
4944         /*m_box = core::aabbox3d<f32>(0,0,0,
4945                         map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4946         /*m_box = core::aabbox3d<f32>(0,0,0,
4947                         map->getSizeNodes().X * BS,
4948                         map->getSizeNodes().Y * BS,
4949                         map->getSizeNodes().Z * BS);*/
4950         m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4951                         BS*1000000,BS*1000000,BS*1000000);
4952         
4953         //setPosition(v3f(BS,BS,BS));
4954 }
4955
4956 ClientMap::~ClientMap()
4957 {
4958         JMutexAutoLock lock(mesh_mutex);
4959         
4960         if(mesh != NULL)
4961         {
4962                 mesh->drop();
4963                 mesh = NULL;
4964         }
4965 }
4966
4967 MapSector * ClientMap::emergeSector(v2s16 p2d)
4968 {
4969         DSTACK(__FUNCTION_NAME);
4970         // Check that it doesn't exist already
4971         try{
4972                 return getSectorNoGenerate(p2d);
4973         }
4974         catch(InvalidPositionException &e)
4975         {
4976         }
4977         
4978         // Create a sector with no heightmaps
4979         ClientMapSector *sector = new ClientMapSector(this, p2d);
4980         
4981         {
4982                 JMutexAutoLock lock(m_sector_mutex);
4983                 m_sectors.insert(p2d, sector);
4984         }
4985         
4986         return sector;
4987 }
4988
4989 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
4990 {
4991         DSTACK(__FUNCTION_NAME);
4992         ClientMapSector *sector = NULL;
4993
4994         JMutexAutoLock lock(m_sector_mutex);
4995         
4996         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
4997
4998         if(n != NULL)
4999         {
5000                 sector = (ClientMapSector*)n->getValue();
5001                 assert(sector->getId() == MAPSECTOR_CLIENT);
5002         }
5003         else
5004         {
5005                 sector = new ClientMapSector(this, p2d);
5006                 {
5007                         JMutexAutoLock lock(m_sector_mutex);
5008                         m_sectors.insert(p2d, sector);
5009                 }
5010         }
5011
5012         sector->deSerialize(is);
5013 }
5014
5015 void ClientMap::OnRegisterSceneNode()
5016 {
5017         if(IsVisible)
5018         {
5019                 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5020                 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5021         }
5022
5023         ISceneNode::OnRegisterSceneNode();
5024 }
5025
5026 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5027 {
5028         //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5029         DSTACK(__FUNCTION_NAME);
5030
5031         bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5032
5033         /*
5034                 Get time for measuring timeout.
5035                 
5036                 Measuring time is very useful for long delays when the
5037                 machine is swapping a lot.
5038         */
5039         int time1 = time(0);
5040
5041         u32 daynight_ratio = m_client->getDayNightRatio();
5042
5043         m_camera_mutex.Lock();
5044         v3f camera_position = m_camera_position;
5045         v3f camera_direction = m_camera_direction;
5046         m_camera_mutex.Unlock();
5047
5048         /*
5049                 Get all blocks and draw all visible ones
5050         */
5051
5052         v3s16 cam_pos_nodes(
5053                         camera_position.X / BS,
5054                         camera_position.Y / BS,
5055                         camera_position.Z / BS);
5056
5057         v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5058
5059         v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5060         v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5061
5062         // Take a fair amount as we will be dropping more out later
5063         v3s16 p_blocks_min(
5064                         p_nodes_min.X / MAP_BLOCKSIZE - 1,
5065                         p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5066                         p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5067         v3s16 p_blocks_max(
5068                         p_nodes_max.X / MAP_BLOCKSIZE + 1,
5069                         p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5070                         p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5071         
5072         u32 vertex_count = 0;
5073         
5074         // For limiting number of mesh updates per frame
5075         u32 mesh_update_count = 0;
5076         
5077         u32 blocks_would_have_drawn = 0;
5078         u32 blocks_drawn = 0;
5079
5080         //NOTE: The sectors map should be locked but we're not doing it
5081         // because it'd cause too much delays
5082
5083         int timecheck_counter = 0;
5084         core::map<v2s16, MapSector*>::Iterator si;
5085         si = m_sectors.getIterator();
5086         for(; si.atEnd() == false; si++)
5087         {
5088                 {
5089                         timecheck_counter++;
5090                         if(timecheck_counter > 50)
5091                         {
5092                                 int time2 = time(0);
5093                                 if(time2 > time1 + 4)
5094                                 {
5095                                         dstream<<"ClientMap::renderMap(): "
5096                                                 "Rendering takes ages, returning."
5097                                                 <<std::endl;
5098                                         return;
5099                                 }
5100                         }
5101                 }
5102
5103                 MapSector *sector = si.getNode()->getValue();
5104                 v2s16 sp = sector->getPos();
5105                 
5106                 if(m_control.range_all == false)
5107                 {
5108                         if(sp.X < p_blocks_min.X
5109                         || sp.X > p_blocks_max.X
5110                         || sp.Y < p_blocks_min.Z
5111                         || sp.Y > p_blocks_max.Z)
5112                                 continue;
5113                 }
5114
5115                 core::list< MapBlock * > sectorblocks;
5116                 sector->getBlocks(sectorblocks);
5117                 
5118                 /*
5119                         Draw blocks
5120                 */
5121
5122                 core::list< MapBlock * >::Iterator i;
5123                 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5124                 {
5125                         MapBlock *block = *i;
5126
5127                         /*
5128                                 Compare block position to camera position, skip
5129                                 if not seen on display
5130                         */
5131                         
5132                         float range = 100000 * BS;
5133                         if(m_control.range_all == false)
5134                                 range = m_control.wanted_range * BS;
5135
5136                         if(isBlockInSight(block->getPos(), camera_position,
5137                                         camera_direction, range) == false)
5138                         {
5139                                 continue;
5140                         }
5141
5142 #if 0                   
5143                         v3s16 blockpos_nodes = block->getPosRelative();
5144                         
5145                         // Block center position
5146                         v3f blockpos(
5147                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5148                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5149                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5150                         );
5151
5152                         // Block position relative to camera
5153                         v3f blockpos_relative = blockpos - camera_position;
5154
5155                         // Distance in camera direction (+=front, -=back)
5156                         f32 dforward = blockpos_relative.dotProduct(camera_direction);
5157
5158                         // Total distance
5159                         f32 d = blockpos_relative.getLength();
5160                         
5161                         if(m_control.range_all == false)
5162                         {
5163                                 // If block is far away, don't draw it
5164                                 if(d > m_control.wanted_range * BS)
5165                                         continue;
5166                         }
5167                         
5168                         // Maximum radius of a block
5169                         f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5170                         
5171                         // If block is (nearly) touching the camera, don't
5172                         // bother validating further (that is, render it anyway)
5173                         if(d > block_max_radius * 1.5)
5174                         {
5175                                 // Cosine of the angle between the camera direction
5176                                 // and the block direction (camera_direction is an unit vector)
5177                                 f32 cosangle = dforward / d;
5178                                 
5179                                 // Compensate for the size of the block
5180                                 // (as the block has to be shown even if it's a bit off FOV)
5181                                 // This is an estimate.
5182                                 cosangle += block_max_radius / dforward;
5183
5184                                 // If block is not in the field of view, skip it
5185                                 //if(cosangle < cos(FOV_ANGLE/2))
5186                                 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5187                                         continue;
5188                         }
5189 #endif                  
5190
5191                         v3s16 blockpos_nodes = block->getPosRelative();
5192                         
5193                         // Block center position
5194                         v3f blockpos(
5195                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5196                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5197                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5198                         );
5199
5200                         // Block position relative to camera
5201                         v3f blockpos_relative = blockpos - camera_position;
5202
5203                         // Total distance
5204                         f32 d = blockpos_relative.getLength();
5205                         
5206 #if 1
5207                         /*
5208                                 Update expired mesh
5209                         */
5210
5211                         bool mesh_expired = false;
5212                         
5213                         {
5214                                 JMutexAutoLock lock(block->mesh_mutex);
5215
5216                                 mesh_expired = block->getMeshExpired();
5217
5218                                 // Mesh has not been expired and there is no mesh:
5219                                 // block has no content
5220                                 if(block->mesh == NULL && mesh_expired == false)
5221                                         continue;
5222                         }
5223
5224                         f32 faraway = BS*50;
5225                         //f32 faraway = m_control.wanted_range * BS;
5226                         
5227                         /*
5228                                 This has to be done with the mesh_mutex unlocked
5229                         */
5230                         // Pretty random but this should work somewhat nicely
5231                         if(mesh_expired && (
5232                                         (mesh_update_count < 3
5233                                                 && (d < faraway || mesh_update_count < 2)
5234                                         )
5235                                         || 
5236                                         (m_control.range_all && mesh_update_count < 20)
5237                                 )
5238                         )
5239                         /*if(mesh_expired && mesh_update_count < 6
5240                                         && (d < faraway || mesh_update_count < 3))*/
5241                         {
5242                                 mesh_update_count++;
5243
5244                                 // Mesh has been expired: generate new mesh
5245                                 //block->updateMeshes(daynight_i);
5246                                 block->updateMesh(daynight_ratio);
5247
5248                                 mesh_expired = false;
5249                         }
5250                         
5251                         /*
5252                                 Don't draw an expired mesh that is far away
5253                         */
5254                         /*if(mesh_expired && d >= faraway)
5255                         //if(mesh_expired)
5256                         {
5257                                 // Instead, delete it
5258                                 JMutexAutoLock lock(block->mesh_mutex);
5259                                 if(block->mesh)
5260                                 {
5261                                         block->mesh->drop();
5262                                         block->mesh = NULL;
5263                                 }
5264                                 // And continue to next block
5265                                 continue;
5266                         }*/
5267 #endif
5268                         /*
5269                                 Draw the faces of the block
5270                         */
5271                         {
5272                                 JMutexAutoLock lock(block->mesh_mutex);
5273
5274                                 scene::SMesh *mesh = block->mesh;
5275
5276                                 if(mesh == NULL)
5277                                         continue;
5278                                 
5279                                 blocks_would_have_drawn++;
5280                                 if(blocks_drawn >= m_control.wanted_max_blocks
5281                                                 && m_control.range_all == false
5282                                                 && d > m_control.wanted_min_range * BS)
5283                                         continue;
5284                                 blocks_drawn++;
5285
5286                                 u32 c = mesh->getMeshBufferCount();
5287
5288                                 for(u32 i=0; i<c; i++)
5289                                 {
5290                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5291                                         const video::SMaterial& material = buf->getMaterial();
5292                                         video::IMaterialRenderer* rnd =
5293                                                         driver->getMaterialRenderer(material.MaterialType);
5294                                         bool transparent = (rnd && rnd->isTransparent());
5295                                         // Render transparent on transparent pass and likewise.
5296                                         if(transparent == is_transparent_pass)
5297                                         {
5298                                                 driver->setMaterial(buf->getMaterial());
5299                                                 driver->drawMeshBuffer(buf);
5300                                                 vertex_count += buf->getVertexCount();
5301                                         }
5302                                 }
5303                         }
5304                 } // foreach sectorblocks
5305         }
5306         
5307         m_control.blocks_drawn = blocks_drawn;
5308         m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5309
5310         /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5311                         <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5312 }
5313
5314 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5315                 core::map<v3s16, MapBlock*> *affected_blocks)
5316 {
5317         bool changed = false;
5318         /*
5319                 Add it to all blocks touching it
5320         */
5321         v3s16 dirs[7] = {
5322                 v3s16(0,0,0), // this
5323                 v3s16(0,0,1), // back
5324                 v3s16(0,1,0), // top
5325                 v3s16(1,0,0), // right
5326                 v3s16(0,0,-1), // front
5327                 v3s16(0,-1,0), // bottom
5328                 v3s16(-1,0,0), // left
5329         };
5330         for(u16 i=0; i<7; i++)
5331         {
5332                 v3s16 p2 = p + dirs[i];
5333                 // Block position of neighbor (or requested) node
5334                 v3s16 blockpos = getNodeBlockPos(p2);
5335                 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5336                 if(blockref == NULL)
5337                         continue;
5338                 // Relative position of requested node
5339                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5340                 if(blockref->setTempMod(relpos, mod))
5341                 {
5342                         changed = true;
5343                 }
5344         }
5345         if(changed && affected_blocks!=NULL)
5346         {
5347                 for(u16 i=0; i<7; i++)
5348                 {
5349                         v3s16 p2 = p + dirs[i];
5350                         // Block position of neighbor (or requested) node
5351                         v3s16 blockpos = getNodeBlockPos(p2);
5352                         MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5353                         if(blockref == NULL)
5354                                 continue;
5355                         affected_blocks->insert(blockpos, blockref);
5356                 }
5357         }
5358         return changed;
5359 }
5360
5361 bool ClientMap::clearTempMod(v3s16 p,
5362                 core::map<v3s16, MapBlock*> *affected_blocks)
5363 {
5364         bool changed = false;
5365         v3s16 dirs[7] = {
5366                 v3s16(0,0,0), // this
5367                 v3s16(0,0,1), // back
5368                 v3s16(0,1,0), // top
5369                 v3s16(1,0,0), // right
5370                 v3s16(0,0,-1), // front
5371                 v3s16(0,-1,0), // bottom
5372                 v3s16(-1,0,0), // left
5373         };
5374         for(u16 i=0; i<7; i++)
5375         {
5376                 v3s16 p2 = p + dirs[i];
5377                 // Block position of neighbor (or requested) node
5378                 v3s16 blockpos = getNodeBlockPos(p2);
5379                 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5380                 if(blockref == NULL)
5381                         continue;
5382                 // Relative position of requested node
5383                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5384                 if(blockref->clearTempMod(relpos))
5385                 {
5386                         changed = true;
5387                 }
5388         }
5389         if(changed && affected_blocks!=NULL)
5390         {
5391                 for(u16 i=0; i<7; i++)
5392                 {
5393                         v3s16 p2 = p + dirs[i];
5394                         // Block position of neighbor (or requested) node
5395                         v3s16 blockpos = getNodeBlockPos(p2);
5396                         MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5397                         if(blockref == NULL)
5398                                 continue;
5399                         affected_blocks->insert(blockpos, blockref);
5400                 }
5401         }
5402         return changed;
5403 }
5404
5405 void ClientMap::PrintInfo(std::ostream &out)
5406 {
5407         out<<"ClientMap: ";
5408 }
5409
5410 #endif // !SERVER
5411
5412 /*
5413         MapVoxelManipulator
5414 */
5415
5416 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5417 {
5418         m_map = map;
5419 }
5420
5421 MapVoxelManipulator::~MapVoxelManipulator()
5422 {
5423         /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5424                         <<std::endl;*/
5425 }
5426
5427 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5428 {
5429         TimeTaker timer1("emerge", &emerge_time);
5430
5431         // Units of these are MapBlocks
5432         v3s16 p_min = getNodeBlockPos(a.MinEdge);
5433         v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5434
5435         VoxelArea block_area_nodes
5436                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5437
5438         addArea(block_area_nodes);
5439
5440         for(s32 z=p_min.Z; z<=p_max.Z; z++)
5441         for(s32 y=p_min.Y; y<=p_max.Y; y++)
5442         for(s32 x=p_min.X; x<=p_max.X; x++)
5443         {
5444                 v3s16 p(x,y,z);
5445                 core::map<v3s16, bool>::Node *n;
5446                 n = m_loaded_blocks.find(p);
5447                 if(n != NULL)
5448                         continue;
5449                 
5450                 bool block_data_inexistent = false;
5451                 try
5452                 {
5453                         TimeTaker timer1("emerge load", &emerge_load_time);
5454
5455                         /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5456                                         <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5457                                         <<" wanted area: ";
5458                         a.print(dstream);
5459                         dstream<<std::endl;*/
5460                         
5461                         MapBlock *block = m_map->getBlockNoCreate(p);
5462                         if(block->isDummy())
5463                                 block_data_inexistent = true;
5464                         else
5465                                 block->copyTo(*this);
5466                 }
5467                 catch(InvalidPositionException &e)
5468                 {
5469                         block_data_inexistent = true;
5470                 }
5471
5472                 if(block_data_inexistent)
5473                 {
5474                         VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5475                         // Fill with VOXELFLAG_INEXISTENT
5476                         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5477                         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5478                         {
5479                                 s32 i = m_area.index(a.MinEdge.X,y,z);
5480                                 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5481                         }
5482                 }
5483
5484                 m_loaded_blocks.insert(p, !block_data_inexistent);
5485         }
5486
5487         //dstream<<"emerge done"<<std::endl;
5488 }
5489
5490 /*
5491         SUGG: Add an option to only update eg. water and air nodes.
5492               This will make it interfere less with important stuff if
5493                   run on background.
5494 */
5495 void MapVoxelManipulator::blitBack
5496                 (core::map<v3s16, MapBlock*> & modified_blocks)
5497 {
5498         if(m_area.getExtent() == v3s16(0,0,0))
5499                 return;
5500         
5501         //TimeTaker timer1("blitBack");
5502
5503         /*dstream<<"blitBack(): m_loaded_blocks.size()="
5504                         <<m_loaded_blocks.size()<<std::endl;*/
5505         
5506         /*
5507                 Initialize block cache
5508         */
5509         v3s16 blockpos_last;
5510         MapBlock *block = NULL;
5511         bool block_checked_in_modified = false;
5512
5513         for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5514         for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5515         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5516         {
5517                 v3s16 p(x,y,z);
5518
5519                 u8 f = m_flags[m_area.index(p)];
5520                 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5521                         continue;
5522
5523                 MapNode &n = m_data[m_area.index(p)];
5524                         
5525                 v3s16 blockpos = getNodeBlockPos(p);
5526                 
5527                 try
5528                 {
5529                         // Get block
5530                         if(block == NULL || blockpos != blockpos_last){
5531                                 block = m_map->getBlockNoCreate(blockpos);
5532                                 blockpos_last = blockpos;
5533                                 block_checked_in_modified = false;
5534                         }
5535                         
5536                         // Calculate relative position in block
5537                         v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5538
5539                         // Don't continue if nothing has changed here
5540                         if(block->getNode(relpos) == n)
5541                                 continue;
5542
5543                         //m_map->setNode(m_area.MinEdge + p, n);
5544                         block->setNode(relpos, n);
5545                         
5546                         /*
5547                                 Make sure block is in modified_blocks
5548                         */
5549                         if(block_checked_in_modified == false)
5550                         {
5551                                 modified_blocks[blockpos] = block;
5552                                 block_checked_in_modified = true;
5553                         }
5554                 }
5555                 catch(InvalidPositionException &e)
5556                 {
5557                 }
5558         }
5559 }
5560
5561 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5562                 MapVoxelManipulator(map)
5563 {
5564 }
5565
5566 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5567 {
5568 }
5569
5570 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5571 {
5572         // Just create the area so that it can be pointed to
5573         VoxelManipulator::emerge(a, caller_id);
5574 }
5575
5576 void ManualMapVoxelManipulator::initialEmerge(
5577                 v3s16 blockpos_min, v3s16 blockpos_max)
5578 {
5579         TimeTaker timer1("initialEmerge", &emerge_time);
5580
5581         // Units of these are MapBlocks
5582         v3s16 p_min = blockpos_min;
5583         v3s16 p_max = blockpos_max;
5584
5585         VoxelArea block_area_nodes
5586                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5587         
5588         u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5589         if(size_MB >= 1)
5590         {
5591                 dstream<<"initialEmerge: area: ";
5592                 block_area_nodes.print(dstream);
5593                 dstream<<" ("<<size_MB<<"MB)";
5594                 dstream<<std::endl;
5595         }
5596
5597         addArea(block_area_nodes);
5598
5599         for(s32 z=p_min.Z; z<=p_max.Z; z++)
5600         for(s32 y=p_min.Y; y<=p_max.Y; y++)
5601         for(s32 x=p_min.X; x<=p_max.X; x++)
5602         {
5603                 v3s16 p(x,y,z);
5604                 core::map<v3s16, bool>::Node *n;
5605                 n = m_loaded_blocks.find(p);
5606                 if(n != NULL)
5607                         continue;
5608                 
5609                 bool block_data_inexistent = false;
5610                 try
5611                 {
5612                         TimeTaker timer1("emerge load", &emerge_load_time);
5613
5614                         MapBlock *block = m_map->getBlockNoCreate(p);
5615                         if(block->isDummy())
5616                                 block_data_inexistent = true;
5617                         else
5618                                 block->copyTo(*this);
5619                 }
5620                 catch(InvalidPositionException &e)
5621                 {
5622                         block_data_inexistent = true;
5623                 }
5624
5625                 if(block_data_inexistent)
5626                 {
5627                         /*
5628                                 Mark area inexistent
5629                         */
5630                         VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5631                         // Fill with VOXELFLAG_INEXISTENT
5632                         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5633                         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5634                         {
5635                                 s32 i = m_area.index(a.MinEdge.X,y,z);
5636                                 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5637                         }
5638                 }
5639
5640                 m_loaded_blocks.insert(p, !block_data_inexistent);
5641         }
5642 }
5643
5644 void ManualMapVoxelManipulator::blitBackAll(
5645                 core::map<v3s16, MapBlock*> * modified_blocks)
5646 {
5647         if(m_area.getExtent() == v3s16(0,0,0))
5648                 return;
5649         
5650         /*
5651                 Copy data of all blocks
5652         */
5653         for(core::map<v3s16, bool>::Iterator
5654                         i = m_loaded_blocks.getIterator();
5655                         i.atEnd() == false; i++)
5656         {
5657                 bool existed = i.getNode()->getValue();
5658                 if(existed == false)
5659                         continue;
5660                 v3s16 p = i.getNode()->getKey();
5661                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5662                 if(block == NULL)
5663                 {
5664                         dstream<<"WARNING: "<<__FUNCTION_NAME
5665                                         <<": got NULL block "
5666                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5667                                         <<std::endl;
5668                         continue;
5669                 }
5670
5671                 block->copyFrom(*this);
5672
5673                 if(modified_blocks)
5674                         modified_blocks->insert(p, block);
5675         }
5676 }
5677
5678 //END