]> git.lizzy.rs Git - dragonfireclient.git/blob - src/map.cpp
b51f1dac2e068323da9fb1fb279dc0e693dd013b
[dragonfireclient.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 #include "noise.h"
30
31 /*
32         Map
33 */
34
35 Map::Map(std::ostream &dout):
36         m_dout(dout),
37         m_camera_position(0,0,0),
38         m_camera_direction(0,0,1),
39         m_sector_cache(NULL)
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         /*if(initial_size != 0)
1459                 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1460
1461         while(m_transforming_liquid.size() != 0)
1462         {
1463                 /*
1464                         Get a queued transforming liquid node
1465                 */
1466                 v3s16 p0 = m_transforming_liquid.pop_front();
1467
1468                 MapNode n0 = getNode(p0);
1469                 
1470                 // Don't deal with non-liquids
1471                 if(content_liquid(n0.d) == false)
1472                         continue;
1473
1474                 bool is_source = !content_flowing_liquid(n0.d);
1475                 
1476                 u8 liquid_level = 8;
1477                 if(is_source == false)
1478                         liquid_level = n0.param2 & 0x0f;
1479                 
1480                 // Turn possible source into non-source
1481                 u8 nonsource_c = make_liquid_flowing(n0.d);
1482
1483                 /*
1484                         If not source, check that some node flows into this one
1485                         and what is the level of liquid in this one
1486                 */
1487                 if(is_source == false)
1488                 {
1489                         s8 new_liquid_level_max = -1;
1490
1491                         v3s16 dirs_from[5] = {
1492                                 v3s16(0,1,0), // top
1493                                 v3s16(0,0,1), // back
1494                                 v3s16(1,0,0), // right
1495                                 v3s16(0,0,-1), // front
1496                                 v3s16(-1,0,0), // left
1497                         };
1498                         for(u16 i=0; i<5; i++)
1499                         {
1500                                 try
1501                                 {
1502
1503                                 bool from_top = (i==0);
1504
1505                                 v3s16 p2 = p0 + dirs_from[i];
1506                                 MapNode n2 = getNode(p2);
1507
1508                                 if(content_liquid(n2.d))
1509                                 {
1510                                         u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1511                                         // Check that the liquids are the same type
1512                                         if(n2_nonsource_c != nonsource_c)
1513                                         {
1514                                                 dstream<<"WARNING: Not handling: different liquids"
1515                                                                 " collide"<<std::endl;
1516                                                 continue;
1517                                         }
1518                                         bool n2_is_source = !content_flowing_liquid(n2.d);
1519                                         s8 n2_liquid_level = 8;
1520                                         if(n2_is_source == false)
1521                                                 n2_liquid_level = n2.param2 & 0x07;
1522                                         
1523                                         s8 new_liquid_level = -1;
1524                                         if(from_top)
1525                                         {
1526                                                 //new_liquid_level = 7;
1527                                                 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1528                                                         new_liquid_level = 7;
1529                                                 else
1530                                                         new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1531                                         }
1532                                         else if(n2_liquid_level > 0)
1533                                         {
1534                                                 new_liquid_level = n2_liquid_level - 1;
1535                                         }
1536
1537                                         if(new_liquid_level > new_liquid_level_max)
1538                                                 new_liquid_level_max = new_liquid_level;
1539                                 }
1540
1541                                 }catch(InvalidPositionException &e)
1542                                 {
1543                                 }
1544                         } //for
1545                         
1546                         /*
1547                                 If liquid level should be something else, update it and
1548                                 add all the neighboring water nodes to the transform queue.
1549                         */
1550                         if(new_liquid_level_max != liquid_level)
1551                         {
1552                                 if(new_liquid_level_max == -1)
1553                                 {
1554                                         // Remove water alltoghether
1555                                         n0.d = CONTENT_AIR;
1556                                         n0.param2 = 0;
1557                                         setNode(p0, n0);
1558                                 }
1559                                 else
1560                                 {
1561                                         n0.param2 = new_liquid_level_max;
1562                                         setNode(p0, n0);
1563                                 }
1564                                 
1565                                 // Block has been modified
1566                                 {
1567                                         v3s16 blockpos = getNodeBlockPos(p0);
1568                                         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1569                                         if(block != NULL)
1570                                                 modified_blocks.insert(blockpos, block);
1571                                 }
1572                                 
1573                                 /*
1574                                         Add neighboring non-source liquid nodes to transform queue.
1575                                 */
1576                                 v3s16 dirs[6] = {
1577                                         v3s16(0,0,1), // back
1578                                         v3s16(0,1,0), // top
1579                                         v3s16(1,0,0), // right
1580                                         v3s16(0,0,-1), // front
1581                                         v3s16(0,-1,0), // bottom
1582                                         v3s16(-1,0,0), // left
1583                                 };
1584                                 for(u16 i=0; i<6; i++)
1585                                 {
1586                                         try
1587                                         {
1588
1589                                         v3s16 p2 = p0 + dirs[i];
1590                                         
1591                                         MapNode n2 = getNode(p2);
1592                                         if(content_flowing_liquid(n2.d))
1593                                         {
1594                                                 m_transforming_liquid.push_back(p2);
1595                                         }
1596                                         
1597                                         }catch(InvalidPositionException &e)
1598                                         {
1599                                         }
1600                                 }
1601                         }
1602                 }
1603                 
1604                 // Get a new one from queue if the node has turned into non-water
1605                 if(content_liquid(n0.d) == false)
1606                         continue;
1607
1608                 /*
1609                         Flow water from this node
1610                 */
1611                 v3s16 dirs_to[5] = {
1612                         v3s16(0,-1,0), // bottom
1613                         v3s16(0,0,1), // back
1614                         v3s16(1,0,0), // right
1615                         v3s16(0,0,-1), // front
1616                         v3s16(-1,0,0), // left
1617                 };
1618                 for(u16 i=0; i<5; i++)
1619                 {
1620                         try
1621                         {
1622
1623                         bool to_bottom = (i == 0);
1624
1625                         // If liquid is at lowest possible height, it's not going
1626                         // anywhere except down
1627                         if(liquid_level == 0 && to_bottom == false)
1628                                 continue;
1629                         
1630                         u8 liquid_next_level = 0;
1631                         // If going to bottom
1632                         if(to_bottom)
1633                         {
1634                                 //liquid_next_level = 7;
1635                                 if(liquid_level >= 7 - WATER_DROP_BOOST)
1636                                         liquid_next_level = 7;
1637                                 else
1638                                         liquid_next_level = liquid_level + WATER_DROP_BOOST;
1639                         }
1640                         else
1641                                 liquid_next_level = liquid_level - 1;
1642
1643                         bool n2_changed = false;
1644                         bool flowed = false;
1645                         
1646                         v3s16 p2 = p0 + dirs_to[i];
1647
1648                         MapNode n2 = getNode(p2);
1649                         //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1650
1651                         if(content_liquid(n2.d))
1652                         {
1653                                 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1654                                 // Check that the liquids are the same type
1655                                 if(n2_nonsource_c != nonsource_c)
1656                                 {
1657                                         dstream<<"WARNING: Not handling: different liquids"
1658                                                         " collide"<<std::endl;
1659                                         continue;
1660                                 }
1661                                 bool n2_is_source = !content_flowing_liquid(n2.d);
1662                                 u8 n2_liquid_level = 8;
1663                                 if(n2_is_source == false)
1664                                         n2_liquid_level = n2.param2 & 0x07;
1665                                 
1666                                 if(to_bottom)
1667                                 {
1668                                         flowed = true;
1669                                 }
1670
1671                                 if(n2_is_source)
1672                                 {
1673                                         // Just flow into the source, nothing changes.
1674                                         // n2_changed is not set because destination didn't change
1675                                         flowed = true;
1676                                 }
1677                                 else
1678                                 {
1679                                         if(liquid_next_level > liquid_level)
1680                                         {
1681                                                 n2.param2 = liquid_next_level;
1682                                                 setNode(p2, n2);
1683
1684                                                 n2_changed = true;
1685                                                 flowed = true;
1686                                         }
1687                                 }
1688                         }
1689                         else if(n2.d == CONTENT_AIR)
1690                         {
1691                                 n2.d = nonsource_c;
1692                                 n2.param2 = liquid_next_level;
1693                                 setNode(p2, n2);
1694                                 
1695                                 n2_changed = true;
1696                                 flowed = true;
1697                         }
1698                         
1699                         //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1700
1701                         if(n2_changed)
1702                         {
1703                                 m_transforming_liquid.push_back(p2);
1704                                 
1705                                 v3s16 blockpos = getNodeBlockPos(p2);
1706                                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1707                                 if(block != NULL)
1708                                         modified_blocks.insert(blockpos, block);
1709                         }
1710                         
1711                         // If n2_changed to bottom, don't flow anywhere else
1712                         if(to_bottom && flowed && !is_source)
1713                                 break;
1714                                 
1715                         }catch(InvalidPositionException &e)
1716                         {
1717                         }
1718                 }
1719
1720                 loopcount++;
1721                 //if(loopcount >= 100000)
1722                 if(loopcount >= initial_size * 1)
1723                         break;
1724         }
1725         //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1726 }
1727
1728 /*
1729         ServerMap
1730 */
1731
1732 ServerMap::ServerMap(std::string savedir):
1733         Map(dout_server),
1734         m_seed(0)
1735 {
1736         
1737         //m_chunksize = 64;
1738         //m_chunksize = 16; // Too slow
1739         m_chunksize = 8; // Takes a few seconds
1740         //m_chunksize = 4;
1741         //m_chunksize = 2;
1742         
1743         // TODO: Save to and load from a file
1744         m_seed = (((u64)myrand()<<0)%0x7fff)
1745                         + (((u64)myrand()<<16)%0x7fff)
1746                         + (((u64)myrand()<<32)%0x7fff)
1747                         + (((u64)myrand()<<48)%0x7fff);
1748
1749         /*
1750                 Experimental and debug stuff
1751         */
1752         
1753         {
1754         }
1755         
1756         /*
1757                 Try to load map; if not found, create a new one.
1758         */
1759
1760         m_savedir = savedir;
1761         m_map_saving_enabled = false;
1762         
1763         try
1764         {
1765                 // If directory exists, check contents and load if possible
1766                 if(fs::PathExists(m_savedir))
1767                 {
1768                         // If directory is empty, it is safe to save into it.
1769                         if(fs::GetDirListing(m_savedir).size() == 0)
1770                         {
1771                                 dstream<<DTIME<<"Server: Empty save directory is valid."
1772                                                 <<std::endl;
1773                                 m_map_saving_enabled = true;
1774                         }
1775                         else
1776                         {
1777                                 // Load master heightmap
1778                                 loadMasterHeightmap();
1779                                 
1780                                 // Load sector (0,0) and throw and exception on fail
1781                                 if(loadSectorFull(v2s16(0,0)) == false)
1782                                         throw LoadError("Failed to load sector (0,0)");
1783
1784                                 dstream<<DTIME<<"Server: Successfully loaded master "
1785                                                 "heightmap and sector (0,0) from "<<savedir<<
1786                                                 ", assuming valid save directory."
1787                                                 <<std::endl;
1788
1789                                 m_map_saving_enabled = true;
1790                                 // Map loaded, not creating new one
1791                                 return;
1792                         }
1793                 }
1794                 // If directory doesn't exist, it is safe to save to it
1795                 else{
1796                         m_map_saving_enabled = true;
1797                 }
1798         }
1799         catch(std::exception &e)
1800         {
1801                 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1802                                 <<", exception: "<<e.what()<<std::endl;
1803                 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1804                 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1805         }
1806
1807         dstream<<DTIME<<"Initializing new map."<<std::endl;
1808         
1809         // Create zero sector
1810         emergeSector(v2s16(0,0));
1811
1812         // Initially write whole map
1813         save(false);
1814 }
1815
1816 ServerMap::~ServerMap()
1817 {
1818         try
1819         {
1820                 if(m_map_saving_enabled)
1821                 {
1822                         //save(false);
1823                         // Save only changed parts
1824                         save(true);
1825                         dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1826                 }
1827                 else
1828                 {
1829                         dstream<<DTIME<<"Server: map not saved"<<std::endl;
1830                 }
1831         }
1832         catch(std::exception &e)
1833         {
1834                 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1835                                 <<", exception: "<<e.what()<<std::endl;
1836         }
1837         
1838         /*
1839                 Free all MapChunks
1840         */
1841         core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1842         for(; i.atEnd() == false; i++)
1843         {
1844                 MapChunk *chunk = i.getNode()->getValue();
1845                 delete chunk;
1846         }
1847 }
1848
1849 /*
1850         Some helper functions for the map generator
1851 */
1852
1853 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1854 {
1855         v3s16 em = vmanip.m_area.getExtent();
1856         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1857         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1858         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1859         s16 y;
1860         for(y=y_nodes_max; y>=y_nodes_min; y--)
1861         {
1862                 MapNode &n = vmanip.m_data[i];
1863                 if(content_walkable(n.d))
1864                         break;
1865                         
1866                 vmanip.m_area.add_y(em, i, -1);
1867         }
1868         if(y >= y_nodes_min)
1869                 return y;
1870         else
1871                 return y_nodes_min;
1872 }
1873
1874 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1875 {
1876         v3s16 em = vmanip.m_area.getExtent();
1877         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1878         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1879         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1880         s16 y;
1881         for(y=y_nodes_max; y>=y_nodes_min; y--)
1882         {
1883                 MapNode &n = vmanip.m_data[i];
1884                 if(content_walkable(n.d)
1885                                 && n.d != CONTENT_TREE
1886                                 && n.d != CONTENT_LEAVES)
1887                         break;
1888                         
1889                 vmanip.m_area.add_y(em, i, -1);
1890         }
1891         if(y >= y_nodes_min)
1892                 return y;
1893         else
1894                 return y_nodes_min;
1895 }
1896
1897 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1898 {
1899         MapNode treenode(CONTENT_TREE);
1900         MapNode leavesnode(CONTENT_LEAVES);
1901
1902         s16 trunk_h = myrand_range(3, 6);
1903         v3s16 p1 = p0;
1904         for(s16 ii=0; ii<trunk_h; ii++)
1905         {
1906                 if(vmanip.m_area.contains(p1))
1907                         vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1908                 p1.Y++;
1909         }
1910         
1911         // p1 is now the last piece of the trunk
1912         p1.Y -= 1;
1913
1914         VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1915         //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1916         Buffer<u8> leaves_d(leaves_a.getVolume());
1917         for(s32 i=0; i<leaves_a.getVolume(); i++)
1918                 leaves_d[i] = 0;
1919         
1920         // Force leaves at near the end of the trunk
1921         {
1922                 s16 d = 1;
1923                 for(s16 z=-d; z<=d; z++)
1924                 for(s16 y=-d; y<=d; y++)
1925                 for(s16 x=-d; x<=d; x++)
1926                 {
1927                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
1928                 }
1929         }
1930         
1931         // Add leaves randomly
1932         for(u32 iii=0; iii<7; iii++)
1933         {
1934                 s16 d = 1;
1935
1936                 v3s16 p(
1937                         myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
1938                         myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
1939                         myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
1940                 );
1941                 
1942                 for(s16 z=0; z<=d; z++)
1943                 for(s16 y=0; y<=d; y++)
1944                 for(s16 x=0; x<=d; x++)
1945                 {
1946                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
1947                 }
1948         }
1949         
1950         // Blit leaves to vmanip
1951         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
1952         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
1953         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
1954         {
1955                 v3s16 p(x,y,z);
1956                 p += p1;
1957                 if(vmanip.m_area.contains(p) == false)
1958                         continue;
1959                 u32 vi = vmanip.m_area.index(p);
1960                 if(vmanip.m_data[vi].d != CONTENT_AIR)
1961                         continue;
1962                 u32 i = leaves_a.index(x,y,z);
1963                 if(leaves_d[i] == 1)
1964                         vmanip.m_data[vi] = leavesnode;
1965         }
1966 }
1967
1968 /*
1969         Noise functions. Make sure seed is mangled differently in each one.
1970 */
1971
1972 // Amount of trees per area in nodes
1973 double tree_amount_2d(u64 seed, v2s16 p)
1974 {
1975         double noise = noise2d_perlin(
1976                         0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1977                         seed+2, 5, 0.6);
1978         double zeroval = -0.3;
1979         if(noise < zeroval)
1980                 return 0;
1981         else
1982                 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1983 }
1984
1985 /*double base_rock_level_2d(u64 seed, v2s16 p)
1986 {
1987         return WATER_LEVEL - 6.0 + 25. * noise2d_perlin(
1988                         0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1989                         seed, 6, 0.6);
1990 }*/
1991
1992 /*double highlands_level_2d(u64 seed, v2s16 p)
1993 {
1994         double a = noise2d_perlin(
1995                         0.5+(float)p.X/1000., 0.5+(float)p.Y/1000.,
1996                         seed-359, 6, 0.65);
1997         if(a > 0.0)
1998         //if(1)
1999         {
2000                 return WATER_LEVEL + 25;
2001                 return WATER_LEVEL + 55. * noise2d_perlin(
2002                                 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2003                                 seed+85039, 6, 0.69);
2004         }
2005         else
2006                 return -100000;
2007 }*/
2008
2009 double base_rock_level_2d(u64 seed, v2s16 p)
2010 {
2011         // The base ground level
2012         double base = WATER_LEVEL - 4.0 + 25. * noise2d_perlin(
2013                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2014                         (seed>>32)+654879876, 6, 0.6);
2015         /*// A bit hillier one
2016         double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2017                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2018                         (seed>>27)+90340, 6, 0.69);
2019         if(base2 > base)
2020                 base = base2;*/
2021 #if 1
2022         // Higher ground level
2023         double higher = WATER_LEVEL + 13. + 50. * noise2d_perlin(
2024                         0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2025                         seed+85039, 6, 0.69);
2026         //higher = 30; // For debugging
2027
2028         // Limit higher to at least base
2029         if(higher < base)
2030                 higher = base;
2031                 
2032         // Steepness factor of cliffs
2033         double b = 1.0 + 1.0 * noise2d_perlin(
2034                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2035                         seed-932, 7, 0.7);
2036         b = rangelim(b, 0.0, 1000.0);
2037         b = pow(b, 5);
2038         b *= 7;
2039         b = rangelim(b, 3.0, 1000.0);
2040         //dstream<<"b="<<b<<std::endl;
2041         //double b = 20;
2042
2043         // Offset to more low
2044         double a_off = -0.3;
2045         // High/low selector
2046         /*double a = 0.5 + b * (a_off + noise2d_perlin(
2047                         0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2048                         seed-359, 6, 0.7));*/
2049         double a = 0.5 + b * (a_off + noise2d_perlin(
2050                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2051                         seed-359, 5, 0.60));
2052         // Limit
2053         a = rangelim(a, 0.0, 1.0);
2054
2055         //dstream<<"a="<<a<<std::endl;
2056         
2057         double h = base*(1.0-a) + higher*a;
2058 #else
2059         double h = base;
2060 #endif
2061         return h;
2062 }
2063
2064 /*
2065         This is the main map generation method
2066 */
2067
2068 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2069                 core::map<v3s16, MapBlock*> &changed_blocks)
2070 {
2071         DSTACK(__FUNCTION_NAME);
2072
2073         /*
2074                 Don't generate if already fully generated
2075         */
2076         {
2077                 MapChunk *chunk = getChunk(chunkpos);
2078                 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2079                 {
2080                         dstream<<"generateChunkRaw(): Chunk "
2081                                         <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2082                                         <<" already generated"<<std::endl;
2083                         return chunk;
2084                 }
2085         }
2086
2087         dstream<<"generateChunkRaw(): Generating chunk "
2088                         <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2089                         <<std::endl;
2090         
2091         TimeTaker timer("generateChunkRaw()");
2092         
2093         // The distance how far into the neighbors the generator is allowed to go.
2094         s16 max_spread_amount_sectors = 2;
2095         assert(max_spread_amount_sectors <= m_chunksize);
2096         s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2097
2098         // Minimum amount of space left on sides for mud to fall in
2099         //s16 min_mud_fall_space = 2;
2100         
2101         // Maximum diameter of stone obstacles in X and Z
2102         /*s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2103         assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);*/
2104         
2105         s16 y_blocks_min = -4;
2106         s16 y_blocks_max = 3;
2107         s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2108         s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2109         s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2110
2111         v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2112         s16 sectorpos_base_size = m_chunksize;
2113
2114         /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2115         s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2116         v2s16 sectorpos_bigbase =
2117                         sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2118         s16 sectorpos_bigbase_size =
2119                         sectorpos_base_size + 2 * max_spread_amount_sectors;
2120
2121         v3s16 bigarea_blocks_min(
2122                 sectorpos_bigbase.X,
2123                 y_blocks_min,
2124                 sectorpos_bigbase.Y
2125         );
2126
2127         v3s16 bigarea_blocks_max(
2128                 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2129                 y_blocks_max,
2130                 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2131         );
2132         
2133         // Relative values to control amount of stuff in one chunk
2134         /*u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2135                         *(u32)sectorpos_base_size*MAP_BLOCKSIZE;*/
2136         u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2137                         *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2138                         *(u32)h_blocks*MAP_BLOCKSIZE;
2139                 
2140         /*
2141                 The limiting edges of the lighting update, inclusive.
2142         */
2143         s16 lighting_min_d = 0-max_spread_amount;
2144         s16 lighting_max_d = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2145
2146         /*
2147                 Create the whole area of this and the neighboring chunks
2148         */
2149         {
2150                 TimeTaker timer("generateChunkRaw() create area");
2151                 
2152                 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2153                 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2154                 {
2155                         v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2156                         ServerMapSector *sector = createSector(sectorpos);
2157                         assert(sector);
2158
2159                         for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2160                         {
2161                                 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2162                                 MapBlock *block = createBlock(blockpos);
2163
2164                                 // Lighting won't be calculated
2165                                 //block->setLightingExpired(true);
2166                                 // Lighting will be calculated
2167                                 block->setLightingExpired(false);
2168
2169                                 /*
2170                                         Block gets sunlight if this is true.
2171
2172                                         This should be set to true when the top side of a block
2173                                         is completely exposed to the sky.
2174
2175                                         Actually this doesn't matter now because the
2176                                         initial lighting is done here.
2177                                 */
2178                                 block->setIsUnderground(y != y_blocks_max);
2179                         }
2180                 }
2181         }
2182         
2183         /*
2184                 Now we have a big empty area.
2185
2186                 Make a ManualMapVoxelManipulator that contains this and the
2187                 neighboring chunks
2188         */
2189
2190         ManualMapVoxelManipulator vmanip(this);
2191         // Add the area we just generated
2192         {
2193                 TimeTaker timer("generateChunkRaw() initialEmerge");
2194                 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2195         }
2196
2197         TimeTaker timer_generate("generateChunkRaw() generate");
2198
2199         // Maximum height of the stone surface and obstacles.
2200         // This is used to disable dungeon generation from going too high.
2201         s16 stone_surface_max_y = 0;
2202
2203         /*
2204                 Generate general ground level to full area
2205         */
2206         
2207         {
2208         // 22ms @cs=8
2209         //TimeTaker timer1("ground level");
2210
2211         for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2212         for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2213         {
2214                 // Node position
2215                 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2216                 
2217                 /*
2218                         Skip of already generated
2219                 */
2220                 {
2221                         v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2222                         if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2223                                 continue;
2224                 }
2225
2226                 // Ground height at this point
2227                 float surface_y_f = 0.0;
2228
2229                 // Use perlin noise for ground height
2230                 surface_y_f = base_rock_level_2d(m_seed, p2d);
2231                 
2232                 /*// Experimental stuff
2233                 {
2234                         float a = highlands_level_2d(m_seed, p2d);
2235                         if(a > surface_y_f)
2236                                 surface_y_f = a;
2237                 }*/
2238
2239                 // Convert to integer
2240                 s16 surface_y = (s16)surface_y_f;
2241                 
2242                 // Log it
2243                 if(surface_y > stone_surface_max_y)
2244                         stone_surface_max_y = surface_y;
2245
2246                 /*
2247                         Fill ground with stone
2248                 */
2249                 {
2250                         // Use fast index incrementing
2251                         v3s16 em = vmanip.m_area.getExtent();
2252                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2253                         for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2254                         {
2255                                 vmanip.m_data[i].d = CONTENT_STONE;
2256
2257                                 vmanip.m_area.add_y(em, i, 1);
2258                         }
2259                 }
2260         }
2261         
2262         }//timer1
2263
2264         /*
2265                 Randomize some parameters
2266         */
2267         
2268         s32 stone_obstacle_count = 0;
2269         /*s32 stone_obstacle_count =
2270                         rangelim((1.0+noise2d(m_seed+897,
2271                         sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2272         
2273         s16 stone_obstacle_max_height = 0;
2274         /*s16 stone_obstacle_max_height =
2275                         rangelim((1.0+noise2d(m_seed+5902,
2276                         sectorpos_base.X, sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2277
2278         /*
2279                 Loop this part, it will make stuff look older and newer nicely
2280         */
2281         //for(u32 i_age=0; i_age<1; i_age++)
2282         for(u32 i_age=0; i_age<2; i_age++)
2283         { // Aging loop
2284
2285         {
2286         // 8ms @cs=8
2287         //TimeTaker timer1("stone obstacles");
2288
2289         /*
2290                 Add some random stone obstacles
2291         */
2292         
2293         for(s32 ri=0; ri<stone_obstacle_count; ri++)
2294         {
2295                 // Randomize max height so usually stuff will be quite low
2296                 s16 maxheight_randomized = myrand_range(0, stone_obstacle_max_height);
2297
2298                 //s16 stone_obstacle_max_size = sectorpos_base_size * MAP_BLOCKSIZE - 10;
2299                 s16 stone_obstacle_max_size = MAP_BLOCKSIZE*4-4;
2300
2301                 v3s16 ob_size(
2302                         myrand_range(5, stone_obstacle_max_size),
2303                         myrand_range(0, maxheight_randomized),
2304                         myrand_range(5, stone_obstacle_max_size)
2305                 );
2306                 
2307                 // Don't make stupid small rectangle bumps
2308                 if(ob_size.Y < 5)
2309                         continue;
2310                 
2311                 v2s16 ob_place(
2312                         myrand_range(1+ob_size.X/2+2,
2313                                         sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.X/2-2),
2314                         myrand_range(1+ob_size.Z/2+2,
2315                                         sectorpos_base_size*MAP_BLOCKSIZE-1-1-ob_size.Z/2-2)
2316                 );
2317                 
2318                 // Minimum space left on top of the obstacle
2319                 s16 min_head_space = 12;
2320                 
2321                 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2322                 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2323                 {
2324                         // Node position in 2d
2325                         v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2326                         
2327                         // Find stone ground level
2328                         // (ignore everything else than mud in already generated chunks)
2329                         // and mud amount over the stone level
2330                         s16 surface_y = 0;
2331                         s16 mud_amount = 0;
2332                         {
2333                                 v3s16 em = vmanip.m_area.getExtent();
2334                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2335                                 s16 y;
2336                                 // Go to ground level
2337                                 for(y=y_nodes_max; y>=y_nodes_min; y--)
2338                                 {
2339                                         MapNode *n = &vmanip.m_data[i];
2340                                         /*if(content_walkable(n.d)
2341                                                         && n.d != CONTENT_MUD
2342                                                         && n.d != CONTENT_GRASS)
2343                                                 break;*/
2344                                         if(n->d == CONTENT_STONE)
2345                                                 break;
2346                                         
2347                                         if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2348                                         {
2349                                                 mud_amount++;
2350                                                 /*
2351                                                         Change to mud because otherwise we might
2352                                                         be throwing mud on grass at the next
2353                                                         step
2354                                                 */
2355                                                 n->d = CONTENT_MUD;
2356                                         }
2357                                                 
2358                                         vmanip.m_area.add_y(em, i, -1);
2359                                 }
2360                                 if(y >= y_nodes_min)
2361                                         surface_y = y;
2362                                 else
2363                                         surface_y = y_nodes_min;
2364                         }
2365
2366
2367                         /*
2368                                 Add stone on ground
2369                         */
2370                         {
2371                                 v3s16 em = vmanip.m_area.getExtent();
2372                                 s16 y_start = surface_y+1;
2373                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2374                                 s16 y;
2375                                 // Add stone
2376                                 s16 count = 0;
2377                                 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2378                                 {
2379                                         MapNode &n = vmanip.m_data[i];
2380                                         n.d = CONTENT_STONE;
2381
2382                                         if(y > stone_surface_max_y)
2383                                                 stone_surface_max_y = y;
2384
2385                                         count++;
2386                                         if(count >= ob_size.Y)
2387                                                 break;
2388                                                 
2389                                         vmanip.m_area.add_y(em, i, 1);
2390                                 }
2391                                 // Add mud
2392                                 count = 0;
2393                                 for(; y<=y_nodes_max - min_head_space; y++)
2394                                 {
2395                                         MapNode &n = vmanip.m_data[i];
2396                                         n.d = CONTENT_MUD;
2397                                         count++;
2398                                         if(count >= mud_amount)
2399                                                 break;
2400                                                 
2401                                         vmanip.m_area.add_y(em, i, 1);
2402                                 }
2403                         }
2404
2405                 }
2406         }
2407
2408         }//timer1
2409         {
2410         // 24ms @cs=8
2411         //TimeTaker timer1("dungeons");
2412
2413         /*
2414                 Make dungeons
2415         */
2416         //u32 dungeons_count = relative_volume / 600000;
2417         /*u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2418         if(stone_surface_max_y < WATER_LEVEL)
2419                 bruises_count = 0;*/
2420         u32 dungeons_count = 0;
2421         u32 bruises_count = 0;
2422         for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
2423         {
2424                 s16 min_tunnel_diameter = 2;
2425                 s16 max_tunnel_diameter = 6;
2426                 u16 tunnel_routepoints = 15;
2427                 
2428                 bool bruise_surface = (jj < bruises_count);
2429
2430                 if(bruise_surface)
2431                 {
2432                         min_tunnel_diameter = 5;
2433                         max_tunnel_diameter = myrand_range(10, 20);
2434                         /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2435                         max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2436                         
2437                         /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(m_seed+42,
2438                                         sectorpos_base.X, sectorpos_base.Y)), 0, 15);*/
2439
2440                         tunnel_routepoints = 5;
2441                 }
2442
2443                 // Allowed route area size in nodes
2444                 v3s16 ar(
2445                         sectorpos_base_size*MAP_BLOCKSIZE,
2446                         h_blocks*MAP_BLOCKSIZE,
2447                         sectorpos_base_size*MAP_BLOCKSIZE
2448                 );
2449
2450                 // Area starting point in nodes
2451                 v3s16 of(
2452                         sectorpos_base.X*MAP_BLOCKSIZE,
2453                         y_blocks_min*MAP_BLOCKSIZE,
2454                         sectorpos_base.Y*MAP_BLOCKSIZE
2455                 );
2456
2457                 // Allow a bit more
2458                 //(this should be more than the maximum radius of the tunnel)
2459                 //s16 insure = 5; // Didn't work with max_d = 20
2460                 s16 insure = 10;
2461                 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2462                 ar += v3s16(1,0,1) * more * 2;
2463                 of -= v3s16(1,0,1) * more;
2464                 
2465                 s16 route_y_min = 0;
2466                 //s16 route_y_max = ar.Y-1;
2467                 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2;
2468                 // If dungeons
2469                 if(bruise_surface == false)
2470                 {
2471                         // Don't go through surface too often
2472                         route_y_max -= myrand_range(0, max_tunnel_diameter*2);
2473                 }
2474                 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2475
2476                 if(bruise_surface)
2477                 {
2478                         /*// Minimum is at y=0
2479                         route_y_min = -of.Y - 0;*/
2480                         // Minimum is at y=max_tunnel_diameter/4
2481                         //route_y_min = -of.Y + max_tunnel_diameter/4;
2482                         //s16 min = -of.Y + max_tunnel_diameter/4;
2483                         s16 min = -of.Y + 0;
2484                         route_y_min = myrand_range(min, min + max_tunnel_diameter);
2485                         route_y_min = rangelim(route_y_min, 0, route_y_max);
2486                 }
2487
2488                 /*dstream<<"route_y_min = "<<route_y_min
2489                                 <<", route_y_max = "<<route_y_max<<std::endl;*/
2490
2491                 // Randomize starting position
2492                 v3f orp(
2493                         (float)(myrand()%ar.X)+0.5,
2494                         (float)(myrand_range(route_y_min, route_y_max))+0.5,
2495                         (float)(myrand()%ar.Z)+0.5
2496                 );
2497
2498                 MapNode airnode(CONTENT_AIR);
2499                 
2500                 /*
2501                         Generate some tunnel starting from orp
2502                 */
2503                 
2504                 for(u16 j=0; j<tunnel_routepoints; j++)
2505                 {
2506                         // Randomize size
2507                         s16 min_d = min_tunnel_diameter;
2508                         s16 max_d = max_tunnel_diameter;
2509                         s16 rs = myrand_range(min_d, max_d);
2510                         
2511                         v3s16 maxlen(15, 5, 15);
2512
2513                         if(bruise_surface)
2514                         {
2515                                 maxlen = v3s16(rs*7,rs*7,rs*7);
2516                         }
2517
2518                         v3f vec(
2519                                 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2520                                 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2521                                 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2522                         );
2523                         v3f rp = orp + vec;
2524                         if(rp.X < 0)
2525                                 rp.X = 0;
2526                         else if(rp.X >= ar.X)
2527                                 rp.X = ar.X-1;
2528                         if(rp.Y < route_y_min)
2529                                 rp.Y = route_y_min;
2530                         else if(rp.Y >= route_y_max)
2531                                 rp.Y = route_y_max-1;
2532                         if(rp.Z < 0)
2533                                 rp.Z = 0;
2534                         else if(rp.Z >= ar.Z)
2535                                 rp.Z = ar.Z-1;
2536                         vec = rp - orp;
2537
2538                         for(float f=0; f<1.0; f+=1.0/vec.getLength())
2539                         {
2540                                 v3f fp = orp + vec * f;
2541                                 v3s16 cp(fp.X, fp.Y, fp.Z);
2542
2543                                 s16 d0 = -rs/2;
2544                                 s16 d1 = d0 + rs - 1;
2545                                 for(s16 z0=d0; z0<=d1; z0++)
2546                                 {
2547                                         //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2548                                         s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2549                                         for(s16 x0=-si; x0<=si-1; x0++)
2550                                         {
2551                                                 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2552                                                 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2553                                                 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2554                                                 //s16 si2 = rs - abs(x0);
2555                                                 for(s16 y0=-si2+1+1; y0<=si2-1; y0++)
2556                                                 {
2557                                                         s16 z = cp.Z + z0;
2558                                                         s16 y = cp.Y + y0;
2559                                                         s16 x = cp.X + x0;
2560                                                         v3s16 p(x,y,z);
2561                                                         /*if(isInArea(p, ar) == false)
2562                                                                 continue;*/
2563                                                         // Check only height
2564                                                         if(y < 0 || y >= ar.Y)
2565                                                                 continue;
2566                                                         p += of;
2567                                                         
2568                                                         //assert(vmanip.m_area.contains(p));
2569                                                         if(vmanip.m_area.contains(p) == false)
2570                                                         {
2571                                                                 dstream<<"WARNING: "<<__FUNCTION_NAME
2572                                                                                 <<":"<<__LINE__<<": "
2573                                                                                 <<"point not in area"
2574                                                                                 <<std::endl;
2575                                                                 continue;
2576                                                         }
2577                                                         
2578                                                         // Just set it to air, it will be changed to
2579                                                         // water afterwards
2580                                                         u32 i = vmanip.m_area.index(p);
2581                                                         vmanip.m_data[i] = airnode;
2582                                                 }
2583                                         }
2584                                 }
2585                         }
2586
2587                         orp = rp;
2588                 }
2589         
2590         }
2591
2592         }//timer1
2593         {
2594         // 46ms @cs=8
2595         //TimeTaker timer1("ore veins");
2596
2597         /*
2598                 Make ore veins
2599         */
2600         for(u32 jj=0; jj<relative_volume/2000; jj++)
2601         {
2602                 s16 max_vein_diameter = 3;
2603
2604                 // Allowed route area size in nodes
2605                 v3s16 ar(
2606                         sectorpos_base_size*MAP_BLOCKSIZE,
2607                         h_blocks*MAP_BLOCKSIZE,
2608                         sectorpos_base_size*MAP_BLOCKSIZE
2609                 );
2610
2611                 // Area starting point in nodes
2612                 v3s16 of(
2613                         sectorpos_base.X*MAP_BLOCKSIZE,
2614                         y_blocks_min*MAP_BLOCKSIZE,
2615                         sectorpos_base.Y*MAP_BLOCKSIZE
2616                 );
2617
2618                 // Allow a bit more
2619                 //(this should be more than the maximum radius of the tunnel)
2620                 s16 insure = 3;
2621                 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2622                 ar += v3s16(1,0,1) * more * 2;
2623                 of -= v3s16(1,0,1) * more;
2624                 
2625                 // Randomize starting position
2626                 v3f orp(
2627                         (float)(myrand()%ar.X)+0.5,
2628                         (float)(myrand()%ar.Y)+0.5,
2629                         (float)(myrand()%ar.Z)+0.5
2630                 );
2631
2632                 // Randomize mineral
2633                 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2634
2635                 /*
2636                         Generate some vein starting from orp
2637                 */
2638
2639                 for(u16 j=0; j<2; j++)
2640                 {
2641                         /*v3f rp(
2642                                 (float)(myrand()%ar.X)+0.5,
2643                                 (float)(myrand()%ar.Y)+0.5,
2644                                 (float)(myrand()%ar.Z)+0.5
2645                         );
2646                         v3f vec = rp - orp;*/
2647                         
2648                         v3s16 maxlen(10, 10, 10);
2649                         v3f vec(
2650                                 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2651                                 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2652                                 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2653                         );
2654                         v3f rp = orp + vec;
2655                         if(rp.X < 0)
2656                                 rp.X = 0;
2657                         else if(rp.X >= ar.X)
2658                                 rp.X = ar.X;
2659                         if(rp.Y < 0)
2660                                 rp.Y = 0;
2661                         else if(rp.Y >= ar.Y)
2662                                 rp.Y = ar.Y;
2663                         if(rp.Z < 0)
2664                                 rp.Z = 0;
2665                         else if(rp.Z >= ar.Z)
2666                                 rp.Z = ar.Z;
2667                         vec = rp - orp;
2668
2669                         // Randomize size
2670                         s16 min_d = 0;
2671                         s16 max_d = max_vein_diameter;
2672                         s16 rs = myrand_range(min_d, max_d);
2673                         
2674                         for(float f=0; f<1.0; f+=1.0/vec.getLength())
2675                         {
2676                                 v3f fp = orp + vec * f;
2677                                 v3s16 cp(fp.X, fp.Y, fp.Z);
2678                                 s16 d0 = -rs/2;
2679                                 s16 d1 = d0 + rs - 1;
2680                                 for(s16 z0=d0; z0<=d1; z0++)
2681                                 {
2682                                         s16 si = rs - abs(z0);
2683                                         for(s16 x0=-si; x0<=si-1; x0++)
2684                                         {
2685                                                 s16 si2 = rs - abs(x0);
2686                                                 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2687                                                 {
2688                                                         // Don't put mineral to every place
2689                                                         if(myrand()%5 != 0)
2690                                                                 continue;
2691
2692                                                         s16 z = cp.Z + z0;
2693                                                         s16 y = cp.Y + y0;
2694                                                         s16 x = cp.X + x0;
2695                                                         v3s16 p(x,y,z);
2696                                                         /*if(isInArea(p, ar) == false)
2697                                                                 continue;*/
2698                                                         // Check only height
2699                                                         if(y < 0 || y >= ar.Y)
2700                                                                 continue;
2701                                                         p += of;
2702                                                         
2703                                                         assert(vmanip.m_area.contains(p));
2704                                                         
2705                                                         // Just set it to air, it will be changed to
2706                                                         // water afterwards
2707                                                         u32 i = vmanip.m_area.index(p);
2708                                                         MapNode *n = &vmanip.m_data[i];
2709                                                         if(n->d == CONTENT_STONE)
2710                                                                 n->param = mineral;
2711                                                 }
2712                                         }
2713                                 }
2714                         }
2715
2716                         orp = rp;
2717                 }
2718         
2719         }
2720
2721         }//timer1
2722         {
2723         // 15ms @cs=8
2724         //TimeTaker timer1("add mud");
2725
2726         /*
2727                 Add mud to the central chunk
2728         */
2729         
2730         for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2731         for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2732         {
2733                 // Node position in 2d
2734                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2735                 
2736                 // Randomize mud amount
2737                 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2738                                 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2739                                 m_seed+1, 3, 0.55));
2740
2741                 // Find ground level
2742                 s16 surface_y = find_ground_level_clever(vmanip, p2d);
2743
2744                 /*
2745                         If topmost node is grass, change it to mud.
2746                         It might be if it was flown to there from a neighboring
2747                         chunk and then converted.
2748                 */
2749                 {
2750                         u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2751                         MapNode *n = &vmanip.m_data[i];
2752                         if(n->d == CONTENT_GRASS)
2753                                 n->d = CONTENT_MUD;
2754                 }
2755
2756                 /*
2757                         Add mud on ground
2758                 */
2759                 {
2760                         s16 mudcount = 0;
2761                         v3s16 em = vmanip.m_area.getExtent();
2762                         s16 y_start = surface_y+1;
2763                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2764                         for(s16 y=y_start; y<=y_nodes_max; y++)
2765                         {
2766                                 if(mudcount >= mud_add_amount)
2767                                         break;
2768                                         
2769                                 MapNode &n = vmanip.m_data[i];
2770                                 n.d = CONTENT_MUD;
2771                                 mudcount++;
2772
2773                                 vmanip.m_area.add_y(em, i, 1);
2774                         }
2775                 }
2776
2777         }
2778
2779         }//timer1
2780         {
2781         // 340ms @cs=8
2782         TimeTaker timer1("flow mud");
2783
2784         /*
2785                 Flow mud away from steep edges
2786         */
2787
2788         // Limit area by 1 because mud is flown into neighbors.
2789         s16 mudflow_minpos = 0-max_spread_amount+1;
2790         s16 mudflow_maxpos = sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-2;
2791
2792         // Iterate a few times
2793         for(s16 k=0; k<3; k++)
2794         {
2795
2796         for(s16 x=mudflow_minpos;
2797                         x<=mudflow_maxpos;
2798                         x++)
2799         for(s16 z=mudflow_minpos;
2800                         z<=mudflow_maxpos;
2801                         z++)
2802         {
2803                 // Invert coordinates every 2nd iteration
2804                 if(k%2 == 0)
2805                 {
2806                         x = mudflow_maxpos - (x-mudflow_minpos);
2807                         z = mudflow_maxpos - (z-mudflow_minpos);
2808                 }
2809
2810                 // Node position in 2d
2811                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2812                 
2813                 v3s16 em = vmanip.m_area.getExtent();
2814                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2815                 s16 y=y_nodes_max;
2816
2817                 for(;; y--)
2818                 {
2819                         MapNode *n = NULL;
2820                         // Find mud
2821                         for(; y>=y_nodes_min; y--)
2822                         {
2823                                 n = &vmanip.m_data[i];
2824                                 //if(content_walkable(n->d))
2825                                 //      break;
2826                                 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2827                                         break;
2828                                         
2829                                 vmanip.m_area.add_y(em, i, -1);
2830                         }
2831
2832                         // Stop if out of area
2833                         //if(vmanip.m_area.contains(i) == false)
2834                         if(y < y_nodes_min)
2835                                 break;
2836
2837                         /*// If not mud, do nothing to it
2838                         MapNode *n = &vmanip.m_data[i];
2839                         if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2840                                 continue;*/
2841
2842                         /*
2843                                 Don't flow it if the stuff under it is not mud
2844                         */
2845                         {
2846                                 u32 i2 = i;
2847                                 vmanip.m_area.add_y(em, i2, -1);
2848                                 // Cancel if out of area
2849                                 if(vmanip.m_area.contains(i2) == false)
2850                                         continue;
2851                                 MapNode *n2 = &vmanip.m_data[i2];
2852                                 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2853                                         continue;
2854                         }
2855
2856                         // Make it exactly mud
2857                         n->d = CONTENT_MUD;
2858                         
2859                         /*s16 recurse_count = 0;
2860         mudflow_recurse:*/
2861
2862                         v3s16 dirs4[4] = {
2863                                 v3s16(0,0,1), // back
2864                                 v3s16(1,0,0), // right
2865                                 v3s16(0,0,-1), // front
2866                                 v3s16(-1,0,0), // left
2867                         };
2868
2869                         // Theck that upper is air or doesn't exist.
2870                         // Cancel dropping if upper keeps it in place
2871                         u32 i3 = i;
2872                         vmanip.m_area.add_y(em, i3, 1);
2873                         if(vmanip.m_area.contains(i3) == true
2874                                         && content_walkable(vmanip.m_data[i3].d) == true)
2875                         {
2876                                 continue;
2877                         }
2878
2879                         // Drop mud on side
2880                         
2881                         for(u32 di=0; di<4; di++)
2882                         {
2883                                 v3s16 dirp = dirs4[di];
2884                                 u32 i2 = i;
2885                                 // Move to side
2886                                 vmanip.m_area.add_p(em, i2, dirp);
2887                                 // Fail if out of area
2888                                 if(vmanip.m_area.contains(i2) == false)
2889                                         continue;
2890                                 // Check that side is air
2891                                 MapNode *n2 = &vmanip.m_data[i2];
2892                                 if(content_walkable(n2->d))
2893                                         continue;
2894                                 // Check that under side is air
2895                                 vmanip.m_area.add_y(em, i2, -1);
2896                                 if(vmanip.m_area.contains(i2) == false)
2897                                         continue;
2898                                 n2 = &vmanip.m_data[i2];
2899                                 if(content_walkable(n2->d))
2900                                         continue;
2901                                 /*// Check that under that is air (need a drop of 2)
2902                                 vmanip.m_area.add_y(em, i2, -1);
2903                                 if(vmanip.m_area.contains(i2) == false)
2904                                         continue;
2905                                 n2 = &vmanip.m_data[i2];
2906                                 if(content_walkable(n2->d))
2907                                         continue;*/
2908                                 // Loop further down until not air
2909                                 do{
2910                                         vmanip.m_area.add_y(em, i2, -1);
2911                                         // Fail if out of area
2912                                         if(vmanip.m_area.contains(i2) == false)
2913                                                 continue;
2914                                         n2 = &vmanip.m_data[i2];
2915                                 }while(content_walkable(n2->d) == false);
2916                                 // Loop one up so that we're in air
2917                                 vmanip.m_area.add_y(em, i2, 1);
2918                                 n2 = &vmanip.m_data[i2];
2919
2920                                 // Move mud to new place
2921                                 *n2 = *n;
2922                                 // Set old place to be air
2923                                 *n = MapNode(CONTENT_AIR);
2924
2925                                 // Done
2926                                 break;
2927                         }
2928                 }
2929         }
2930         
2931         }
2932
2933         }//timer1
2934         {
2935         // 50ms @cs=8
2936         //TimeTaker timer1("add water");
2937
2938         /*
2939                 Add water to the central chunk (and a bit more)
2940         */
2941         
2942         for(s16 x=0-max_spread_amount;
2943                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2944                         x++)
2945         for(s16 z=0-max_spread_amount;
2946                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2947                         z++)
2948         {
2949                 // Node position in 2d
2950                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2951                 
2952                 // Find ground level
2953                 //s16 surface_y = find_ground_level(vmanip, p2d);
2954
2955                 /*
2956                         If ground level is over water level, skip.
2957                         NOTE: This leaves caves near water without water,
2958                         which looks especially crappy when the nearby water
2959                         won't start flowing either for some reason
2960                 */
2961                 /*if(surface_y > WATER_LEVEL)
2962                         continue;*/
2963
2964                 /*
2965                         Add water on ground
2966                 */
2967                 {
2968                         v3s16 em = vmanip.m_area.getExtent();
2969                         u8 light = LIGHT_MAX;
2970                         // Start at global water surface level
2971                         s16 y_start = WATER_LEVEL;
2972                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2973                         MapNode *n = &vmanip.m_data[i];
2974
2975                         /*// Add first one to transforming liquid queue, if water
2976                         if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2977                         {
2978                                 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2979                                 m_transforming_liquid.push_back(p);
2980                         }*/
2981
2982                         for(s16 y=y_start; y>=y_nodes_min; y--)
2983                         {
2984                                 n = &vmanip.m_data[i];
2985                                 
2986                                 // Stop when there is no water and no air
2987                                 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2988                                                 && n->d != CONTENT_WATER)
2989                                 {
2990                                         /*// Add bottom one to transforming liquid queue
2991                                         vmanip.m_area.add_y(em, i, 1);
2992                                         n = &vmanip.m_data[i];
2993                                         if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2994                                         {
2995                                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2996                                                 m_transforming_liquid.push_back(p);
2997                                         }*/
2998
2999                                         break;
3000                                 }
3001                                 
3002                                 n->d = CONTENT_WATERSOURCE;
3003                                 n->setLight(LIGHTBANK_DAY, light);
3004
3005                                 // Add to transforming liquid queue (in case it'd
3006                                 // start flowing)
3007                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3008                                 m_transforming_liquid.push_back(p);
3009                                 
3010                                 // Next one
3011                                 vmanip.m_area.add_y(em, i, -1);
3012                                 if(light > 0)
3013                                         light--;
3014                         }
3015                 }
3016
3017         }
3018
3019         }//timer1
3020         
3021         } // Aging loop
3022
3023         {
3024         //TimeTaker timer1("convert mud to sand");
3025
3026         /*
3027                 Convert mud to sand
3028         */
3029         
3030         //s16 mud_add_amount = myrand_range(2, 4);
3031         //s16 mud_add_amount = 0;
3032         
3033         /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3034         for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3035         for(s16 x=0-max_spread_amount+1;
3036                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3037                         x++)
3038         for(s16 z=0-max_spread_amount+1;
3039                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3040                         z++)
3041         {
3042                 // Node position in 2d
3043                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3044                 
3045                 // Determine whether to have sand here
3046                 double sandnoise = noise2d_perlin(
3047                                 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3048                                 m_seed+59420, 3, 0.50);
3049
3050                 bool have_sand = (sandnoise > -0.15);
3051
3052                 if(have_sand == false)
3053                         continue;
3054
3055                 // Find ground level
3056                 s16 surface_y = find_ground_level_clever(vmanip, p2d);
3057                 
3058                 if(surface_y > WATER_LEVEL + 2)
3059                         continue;
3060
3061                 {
3062                         v3s16 em = vmanip.m_area.getExtent();
3063                         s16 y_start = surface_y;
3064                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3065                         u32 not_sand_counter = 0;
3066                         for(s16 y=y_start; y>=y_nodes_min; y--)
3067                         {
3068                                 MapNode *n = &vmanip.m_data[i];
3069                                 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3070                                 {
3071                                         n->d = CONTENT_SAND;
3072                                 }
3073                                 else
3074                                 {
3075                                         not_sand_counter++;
3076                                         if(not_sand_counter > 3)
3077                                                 break;
3078                                 }
3079
3080                                 vmanip.m_area.add_y(em, i, -1);
3081                         }
3082                 }
3083
3084         }
3085
3086         }//timer1
3087         {
3088         // 1ms @cs=8
3089         //TimeTaker timer1("generate trees");
3090
3091         /*
3092                 Generate some trees
3093         */
3094         {
3095                 // Divide area into parts
3096                 s16 div = 8;
3097                 s16 sidelen = sectorpos_base_size*MAP_BLOCKSIZE / div;
3098                 double area = sidelen * sidelen;
3099                 for(s16 x0=0; x0<div; x0++)
3100                 for(s16 z0=0; z0<div; z0++)
3101                 {
3102                         // Center position of part of division
3103                         v2s16 p2d_center(
3104                                 sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3105                                 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3106                         );
3107                         // Minimum edge of part of division
3108                         v2s16 p2d_min(
3109                                 sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3110                                 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3111                         );
3112                         // Maximum edge of part of division
3113                         v2s16 p2d_max(
3114                                 sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3115                                 sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3116                         );
3117                         // Amount of trees
3118                         u32 tree_count = area * tree_amount_2d(m_seed, p2d_center);
3119                         // Put trees in random places on part of division
3120                         for(u32 i=0; i<tree_count; i++)
3121                         {
3122                                 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3123                                 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3124                                 s16 y = find_ground_level(vmanip, v2s16(x,z));
3125                                 // Don't make a tree under water level
3126                                 if(y < WATER_LEVEL)
3127                                         continue;
3128                                 v3s16 p(x,y,z);
3129                                 /*
3130                                         Trees grow only on mud and grass
3131                                 */
3132                                 {
3133                                         u32 i = vmanip.m_area.index(v3s16(p));
3134                                         MapNode *n = &vmanip.m_data[i];
3135                                         if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3136                                                 continue;
3137                                 }
3138                                 p.Y++;
3139                                 // Make a tree
3140                                 make_tree(vmanip, p);
3141                         }
3142                 }
3143                 /*u32 tree_max = relative_area / 60;
3144                 //u32 count = myrand_range(0, tree_max);
3145                 for(u32 i=0; i<count; i++)
3146                 {
3147                         s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3148                         s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
3149                         x += sectorpos_base.X*MAP_BLOCKSIZE;
3150                         z += sectorpos_base.Y*MAP_BLOCKSIZE;
3151                         s16 y = find_ground_level(vmanip, v2s16(x,z));
3152                         // Don't make a tree under water level
3153                         if(y < WATER_LEVEL)
3154                                 continue;
3155                         v3s16 p(x,y+1,z);
3156                         // Make a tree
3157                         make_tree(vmanip, p);
3158                 }*/
3159         }
3160
3161         }//timer1
3162
3163         {
3164         // 19ms @cs=8
3165         //TimeTaker timer1("grow grass");
3166
3167         /*
3168                 Grow grass
3169         */
3170
3171         /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3172         for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3173         for(s16 x=0-max_spread_amount;
3174                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3175                         x++)
3176         for(s16 z=0-max_spread_amount;
3177                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
3178                         z++)
3179         {
3180                 // Node position in 2d
3181                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3182                 
3183                 /*
3184                         Find the lowest surface to which enough light ends up
3185                         to make grass grow.
3186
3187                         Basically just wait until not air and not leaves.
3188                 */
3189                 s16 surface_y = 0;
3190                 {
3191                         v3s16 em = vmanip.m_area.getExtent();
3192                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3193                         s16 y;
3194                         // Go to ground level
3195                         for(y=y_nodes_max; y>=y_nodes_min; y--)
3196                         {
3197                                 MapNode &n = vmanip.m_data[i];
3198                                 if(n.d != CONTENT_AIR
3199                                                 && n.d != CONTENT_LEAVES)
3200                                         break;
3201                                 vmanip.m_area.add_y(em, i, -1);
3202                         }
3203                         if(y >= y_nodes_min)
3204                                 surface_y = y;
3205                         else
3206                                 surface_y = y_nodes_min;
3207                 }
3208                 
3209                 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3210                 MapNode *n = &vmanip.m_data[i];
3211                 if(n->d == CONTENT_MUD)
3212                         n->d = CONTENT_GRASS;
3213         }
3214
3215         }//timer1
3216
3217         /*
3218                 Initial lighting (sunlight)
3219         */
3220
3221         core::map<v3s16, bool> light_sources;
3222
3223         {
3224         // 750ms @cs=8, can't optimize more
3225         TimeTaker timer1("initial lighting");
3226
3227 #if 0
3228         /*
3229                 Go through the edges and add all nodes that have light to light_sources
3230         */
3231         
3232         // Four edges
3233         for(s16 i=0; i<4; i++)
3234         // Edge length
3235         for(s16 j=lighting_min_d;
3236                         j<=lighting_max_d;
3237                         j++)
3238         {
3239                 s16 x;
3240                 s16 z;
3241                 // +-X
3242                 if(i == 0 || i == 1)
3243                 {
3244                         x = (i==0) ? lighting_min_d : lighting_max_d;
3245                         if(i == 0)
3246                                 z = lighting_min_d;
3247                         else
3248                                 z = lighting_max_d;
3249                 }
3250                 // +-Z
3251                 else
3252                 {
3253                         z = (i==0) ? lighting_min_d : lighting_max_d;
3254                         if(i == 0)
3255                                 x = lighting_min_d;
3256                         else
3257                                 x = lighting_max_d;
3258                 }
3259                 
3260                 // Node position in 2d
3261                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3262
3263                 {
3264                         v3s16 em = vmanip.m_area.getExtent();
3265                         s16 y_start = y_nodes_max;
3266                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3267                         for(s16 y=y_start; y>=y_nodes_min; y--)
3268                         {
3269                                 MapNode *n = &vmanip.m_data[i];
3270                                 if(n->getLight(LIGHTBANK_DAY) != 0)
3271                                 {
3272                                         light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3273                                 }
3274                                 //NOTE: This is broken, at least the index has to
3275                                 // be incremented
3276                         }
3277                 }
3278         }
3279 #endif
3280
3281 #if 1
3282         /*
3283                 Go through the edges and apply sunlight to them, not caring
3284                 about neighbors
3285         */
3286         
3287         // Four edges
3288         for(s16 i=0; i<4; i++)
3289         // Edge length
3290         for(s16 j=lighting_min_d;
3291                         j<=lighting_max_d;
3292                         j++)
3293         {
3294                 s16 x;
3295                 s16 z;
3296                 // +-X
3297                 if(i == 0 || i == 1)
3298                 {
3299                         x = (i==0) ? lighting_min_d : lighting_max_d;
3300                         if(i == 0)
3301                                 z = lighting_min_d;
3302                         else
3303                                 z = lighting_max_d;
3304                 }
3305                 // +-Z
3306                 else
3307                 {
3308                         z = (i==0) ? lighting_min_d : lighting_max_d;
3309                         if(i == 0)
3310                                 x = lighting_min_d;
3311                         else
3312                                 x = lighting_max_d;
3313                 }
3314                 
3315                 // Node position in 2d
3316                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3317                 
3318                 // Loop from top to down
3319                 {
3320                         u8 light = LIGHT_SUN;
3321                         v3s16 em = vmanip.m_area.getExtent();
3322                         s16 y_start = y_nodes_max;
3323                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3324                         for(s16 y=y_start; y>=y_nodes_min; y--)
3325                         {
3326                                 MapNode *n = &vmanip.m_data[i];
3327                                 if(light_propagates_content(n->d) == false)
3328                                 {
3329                                         light = 0;
3330                                 }
3331                                 else if(light != LIGHT_SUN
3332                                         || sunlight_propagates_content(n->d) == false)
3333                                 {
3334                                         if(light > 0)
3335                                                 light--;
3336                                 }
3337                                 
3338                                 n->setLight(LIGHTBANK_DAY, light);
3339                                 n->setLight(LIGHTBANK_NIGHT, 0);
3340                                 
3341                                 if(light != 0)
3342                                 {
3343                                         // Insert light source
3344                                         light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3345                                 }
3346                                 
3347                                 // Increment index by y
3348                                 vmanip.m_area.add_y(em, i, -1);
3349                         }
3350                 }
3351         }
3352 #endif
3353
3354         /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3355         for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3356         /*for(s16 x=0-max_spread_amount+1;
3357                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3358                         x++)
3359         for(s16 z=0-max_spread_amount+1;
3360                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3361                         z++)*/
3362 #if 1
3363         /*
3364                 This has to be 1 smaller than the actual area, because
3365                 neighboring nodes are checked.
3366         */
3367         for(s16 x=lighting_min_d+1;
3368                         x<=lighting_max_d-1;
3369                         x++)
3370         for(s16 z=lighting_min_d+1;
3371                         z<=lighting_max_d-1;
3372                         z++)
3373         {
3374                 // Node position in 2d
3375                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3376                 
3377                 /*
3378                         Apply initial sunlight
3379                 */
3380                 {
3381                         u8 light = LIGHT_SUN;
3382                         bool add_to_sources = false;
3383                         v3s16 em = vmanip.m_area.getExtent();
3384                         s16 y_start = y_nodes_max;
3385                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3386                         for(s16 y=y_start; y>=y_nodes_min; y--)
3387                         {
3388                                 MapNode *n = &vmanip.m_data[i];
3389
3390                                 if(light_propagates_content(n->d) == false)
3391                                 {
3392                                         light = 0;
3393                                 }
3394                                 else if(light != LIGHT_SUN
3395                                         || sunlight_propagates_content(n->d) == false)
3396                                 {
3397                                         if(light > 0)
3398                                                 light--;
3399                                 }
3400                                 
3401                                 // This doesn't take much time
3402                                 if(add_to_sources == false)
3403                                 {
3404                                         /*
3405                                                 Check sides. If side is not air or water, start
3406                                                 adding to light_sources.
3407                                         */
3408                                         v3s16 dirs4[4] = {
3409                                                 v3s16(0,0,1), // back
3410                                                 v3s16(1,0,0), // right
3411                                                 v3s16(0,0,-1), // front
3412                                                 v3s16(-1,0,0), // left
3413                                         };
3414                                         for(u32 di=0; di<4; di++)
3415                                         {
3416                                                 v3s16 dirp = dirs4[di];
3417                                                 u32 i2 = i;
3418                                                 vmanip.m_area.add_p(em, i2, dirp);
3419                                                 MapNode *n2 = &vmanip.m_data[i2];
3420                                                 if(
3421                                                         n2->d != CONTENT_AIR
3422                                                         && n2->d != CONTENT_WATERSOURCE
3423                                                         && n2->d != CONTENT_WATER
3424                                                 ){
3425                                                         add_to_sources = true;
3426                                                         break;
3427                                                 }
3428                                         }
3429                                 }
3430                                 
3431                                 n->setLight(LIGHTBANK_DAY, light);
3432                                 n->setLight(LIGHTBANK_NIGHT, 0);
3433                                 
3434                                 // This doesn't take much time
3435                                 if(light != 0 && add_to_sources)
3436                                 {
3437                                         // Insert light source
3438                                         light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3439                                 }
3440                                 
3441                                 // Increment index by y
3442                                 vmanip.m_area.add_y(em, i, -1);
3443                         }
3444                 }
3445         }
3446 #endif
3447
3448 #if 0
3449         for(s16 x=lighting_min_d+1;
3450                         x<=lighting_max_d-1;
3451                         x++)
3452         for(s16 z=lighting_min_d+1;
3453                         z<=lighting_max_d-1;
3454                         z++)
3455         {
3456                 // Node position in 2d
3457                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3458                 
3459                 /*
3460                         Apply initial sunlight
3461                 */
3462                 {
3463                         u8 light = LIGHT_SUN;
3464                         v3s16 em = vmanip.m_area.getExtent();
3465                         s16 y_start = y_nodes_max;
3466                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3467                         for(s16 y=y_start; y>=y_nodes_min; y--)
3468                         {
3469                                 MapNode *n = &vmanip.m_data[i];
3470
3471                                 if(light_propagates_content(n->d) == false)
3472                                 {
3473                                         light = 0;
3474                                 }
3475                                 else if(light != LIGHT_SUN
3476                                         || sunlight_propagates_content(n->d) == false)
3477                                 {
3478                                         if(light > 0)
3479                                                 light--;
3480                                 }
3481                                 
3482                                 n->setLight(LIGHTBANK_DAY, light);
3483                                 n->setLight(LIGHTBANK_NIGHT, 0);
3484                                 
3485                                 // This doesn't take much time
3486                                 if(light != 0)
3487                                 {
3488                                         // Insert light source
3489                                         light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3490                                 }
3491                                 
3492                                 // Increment index by y
3493                                 vmanip.m_area.add_y(em, i, -1);
3494                         }
3495                 }
3496         }
3497 #endif
3498
3499         }//timer1
3500
3501         // Spread light around
3502         {
3503                 TimeTaker timer("generateChunkRaw() spreadLight");
3504                 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3505         }
3506         
3507         /*
3508                 Generation ended
3509         */
3510
3511         timer_generate.stop();
3512
3513         /*
3514                 Blit generated stuff to map
3515         */
3516         {
3517                 // 70ms @cs=8
3518                 //TimeTaker timer("generateChunkRaw() blitBackAll");
3519                 vmanip.blitBackAll(&changed_blocks);
3520         }
3521         /*
3522                 Update day/night difference cache of the MapBlocks
3523         */
3524         {
3525                 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3526                                 i.atEnd() == false; i++)
3527                 {
3528                         MapBlock *block = i.getNode()->getValue();
3529                         block->updateDayNightDiff();
3530                 }
3531         }
3532
3533         
3534         /*
3535                 Create chunk metadata
3536         */
3537
3538         for(s16 x=-1; x<=1; x++)
3539         for(s16 y=-1; y<=1; y++)
3540         {
3541                 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3542                 // Add chunk meta information
3543                 MapChunk *chunk = getChunk(chunkpos0);
3544                 if(chunk == NULL)
3545                 {
3546                         chunk = new MapChunk();
3547                         m_chunks.insert(chunkpos0, chunk);
3548                 }
3549                 //chunk->setIsVolatile(true);
3550                 if(chunk->getGenLevel() > GENERATED_PARTLY)
3551                         chunk->setGenLevel(GENERATED_PARTLY);
3552         }
3553
3554         /*
3555                 Set central chunk non-volatile and return it
3556         */
3557         MapChunk *chunk = getChunk(chunkpos);
3558         assert(chunk);
3559         // Set non-volatile
3560         //chunk->setIsVolatile(false);
3561         chunk->setGenLevel(GENERATED_FULLY);
3562         // Return it
3563         return chunk;
3564 }
3565
3566 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3567                 core::map<v3s16, MapBlock*> &changed_blocks)
3568 {
3569         dstream<<"generateChunk(): Generating chunk "
3570                         <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3571                         <<std::endl;
3572         
3573         /*for(s16 x=-1; x<=1; x++)
3574         for(s16 y=-1; y<=1; y++)*/
3575         for(s16 x=-0; x<=0; x++)
3576         for(s16 y=-0; y<=0; y++)
3577         {
3578                 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3579                 MapChunk *chunk = getChunk(chunkpos0);
3580                 // Skip if already generated
3581                 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3582                         continue;
3583                 generateChunkRaw(chunkpos0, changed_blocks);
3584         }
3585         
3586         assert(chunkNonVolatile(chunkpos1));
3587
3588         MapChunk *chunk = getChunk(chunkpos1);
3589         return chunk;
3590 }
3591
3592 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3593 {
3594         DSTACK("%s: p2d=(%d,%d)",
3595                         __FUNCTION_NAME,
3596                         p2d.X, p2d.Y);
3597         
3598         /*
3599                 Check if it exists already in memory
3600         */
3601         ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3602         if(sector != NULL)
3603                 return sector;
3604         
3605         /*
3606                 Try to load it from disk (with blocks)
3607         */
3608         if(loadSectorFull(p2d) == true)
3609         {
3610                 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3611                 if(sector == NULL)
3612                 {
3613                         dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3614                         throw InvalidPositionException("");
3615                 }
3616                 return sector;
3617         }
3618
3619         /*
3620                 Do not create over-limit
3621         */
3622         if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3623         || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3624         || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3625         || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3626                 throw InvalidPositionException("createSector(): pos. over limit");
3627
3628         /*
3629                 Generate blank sector
3630         */
3631         
3632         sector = new ServerMapSector(this, p2d);
3633         
3634         // Sector position on map in nodes
3635         v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3636
3637         /*
3638                 Insert to container
3639         */
3640         m_sectors.insert(p2d, sector);
3641         
3642         return sector;
3643 }
3644
3645 MapSector * ServerMap::emergeSector(v2s16 p2d,
3646                 core::map<v3s16, MapBlock*> &changed_blocks)
3647 {
3648         DSTACK("%s: p2d=(%d,%d)",
3649                         __FUNCTION_NAME,
3650                         p2d.X, p2d.Y);
3651         
3652         /*
3653                 Check chunk status
3654         */
3655         v2s16 chunkpos = sector_to_chunk(p2d);
3656         /*bool chunk_nonvolatile = false;
3657         MapChunk *chunk = getChunk(chunkpos);
3658         if(chunk && chunk->getIsVolatile() == false)
3659                 chunk_nonvolatile = true;*/
3660         bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3661
3662         /*
3663                 If chunk is not fully generated, generate chunk
3664         */
3665         if(chunk_nonvolatile == false)
3666         {
3667                 // Generate chunk and neighbors
3668                 generateChunk(chunkpos, changed_blocks);
3669         }
3670         
3671         /*
3672                 Return sector if it exists now
3673         */
3674         MapSector *sector = getSectorNoGenerateNoEx(p2d);
3675         if(sector != NULL)
3676                 return sector;
3677         
3678         /*
3679                 Try to load it from disk
3680         */
3681         if(loadSectorFull(p2d) == true)
3682         {
3683                 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3684                 if(sector == NULL)
3685                 {
3686                         dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3687                         throw InvalidPositionException("");
3688                 }
3689                 return sector;
3690         }
3691
3692         /*
3693                 generateChunk should have generated the sector
3694         */
3695         assert(0);
3696
3697         /*
3698                 Generate directly
3699         */
3700         //return generateSector();
3701 }
3702
3703 /*
3704         NOTE: This is not used for main map generation, only for blocks
3705         that are very high or low
3706 */
3707 MapBlock * ServerMap::generateBlock(
3708                 v3s16 p,
3709                 MapBlock *original_dummy,
3710                 ServerMapSector *sector,
3711                 core::map<v3s16, MapBlock*> &changed_blocks,
3712                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3713 )
3714 {
3715         DSTACK("%s: p=(%d,%d,%d)",
3716                         __FUNCTION_NAME,
3717                         p.X, p.Y, p.Z);
3718         
3719         /*dstream<<"generateBlock(): "
3720                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3721                         <<std::endl;*/
3722         
3723         MapBlock *block = original_dummy;
3724                         
3725         v2s16 p2d(p.X, p.Z);
3726         s16 block_y = p.Y;
3727         
3728         /*
3729                 Do not generate over-limit
3730         */
3731         if(blockpos_over_limit(p))
3732         {
3733                 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3734                 throw InvalidPositionException("generateBlock(): pos. over limit");
3735         }
3736
3737         /*
3738                 If block doesn't exist, create one.
3739                 If it exists, it is a dummy. In that case unDummify() it.
3740
3741                 NOTE: This already sets the map as the parent of the block
3742         */
3743         if(block == NULL)
3744         {
3745                 block = sector->createBlankBlockNoInsert(block_y);
3746         }
3747         else
3748         {
3749                 // Remove the block so that nobody can get a half-generated one.
3750                 sector->removeBlock(block);
3751                 // Allocate the block to contain the generated data
3752                 block->unDummify();
3753         }
3754         
3755         u8 water_material = CONTENT_WATERSOURCE;
3756         
3757         s32 lowest_ground_y = 32767;
3758         s32 highest_ground_y = -32768;
3759         
3760         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3761         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3762         {
3763                 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3764
3765                 s16 surface_y = 0;
3766
3767                 if(surface_y < lowest_ground_y)
3768                         lowest_ground_y = surface_y;
3769                 if(surface_y > highest_ground_y)
3770                         highest_ground_y = surface_y;
3771
3772                 s32 surface_depth = 2;
3773                 
3774                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3775                 {
3776                         s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3777                         MapNode n;
3778                         /*
3779                                 Calculate lighting
3780                                 
3781                                 NOTE: If there are some man-made structures above the
3782                                 newly created block, they won't be taken into account.
3783                         */
3784                         if(real_y > surface_y)
3785                                 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3786
3787                         /*
3788                                 Calculate material
3789                         */
3790
3791                         // If node is over heightmap y, it's air or water
3792                         if(real_y > surface_y)
3793                         {
3794                                 // If under water level, it's water
3795                                 if(real_y < WATER_LEVEL)
3796                                 {
3797                                         n.d = water_material;
3798                                         n.setLight(LIGHTBANK_DAY,
3799                                                         diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3800                                         /*
3801                                                 Add to transforming liquid queue (in case it'd
3802                                                 start flowing)
3803                                         */
3804                                         v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3805                                         m_transforming_liquid.push_back(real_pos);
3806                                 }
3807                                 // else air
3808                                 else
3809                                         n.d = CONTENT_AIR;
3810                         }
3811                         // Else it's ground or dungeons (air)
3812                         else
3813                         {
3814                                 // If it's surface_depth under ground, it's stone
3815                                 if(real_y <= surface_y - surface_depth)
3816                                 {
3817                                         n.d = CONTENT_STONE;
3818                                 }
3819                                 else
3820                                 {
3821                                         // It is mud if it is under the first ground
3822                                         // level or under water
3823                                         if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3824                                         {
3825                                                 n.d = CONTENT_MUD;
3826                                         }
3827                                         else
3828                                         {
3829                                                 n.d = CONTENT_GRASS;
3830                                         }
3831
3832                                         //n.d = CONTENT_MUD;
3833                                         
3834                                         /*// If under water level, it's mud
3835                                         if(real_y < WATER_LEVEL)
3836                                                 n.d = CONTENT_MUD;
3837                                         // Only the topmost node is grass
3838                                         else if(real_y <= surface_y - 1)
3839                                                 n.d = CONTENT_MUD;
3840                                         else
3841                                                 n.d = CONTENT_GRASS;*/
3842                                 }
3843                         }
3844
3845                         block->setNode(v3s16(x0,y0,z0), n);
3846                 }
3847         }
3848         
3849         /*
3850                 Calculate some helper variables
3851         */
3852         
3853         // Completely underground if the highest part of block is under lowest
3854         // ground height.
3855         // This has to be very sure; it's probably one too strict now but
3856         // that's just better.
3857         bool completely_underground =
3858                         block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3859
3860         bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3861
3862         bool mostly_underwater_surface = false;
3863         if(highest_ground_y < WATER_LEVEL
3864                         && some_part_underground && !completely_underground)
3865                 mostly_underwater_surface = true;
3866
3867         /*
3868                 Get local attributes
3869         */
3870
3871         //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3872
3873         float caves_amount = 0.5;
3874
3875 #if 0
3876         {
3877                 /*
3878                         NOTE: BEWARE: Too big amount of attribute points slows verything
3879                         down by a lot.
3880                         1 interpolation from 5000 points takes 2-3ms.
3881                 */
3882                 //TimeTaker timer("generateBlock() local attribute retrieval");
3883                 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3884                 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3885                 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3886         }
3887 #endif
3888
3889         //dstream<<"generateBlock(): Done"<<std::endl;
3890
3891         /*
3892                 Generate dungeons
3893         */
3894
3895         // Initialize temporary table
3896         const s32 ued = MAP_BLOCKSIZE;
3897         bool underground_emptiness[ued*ued*ued];
3898         for(s32 i=0; i<ued*ued*ued; i++)
3899         {
3900                 underground_emptiness[i] = 0;
3901         }
3902         
3903         // Fill table
3904 #if 1
3905         {
3906                 /*
3907                         Initialize orp and ors. Try to find if some neighboring
3908                         MapBlock has a tunnel ended in its side
3909                 */
3910
3911                 v3f orp(
3912                         (float)(myrand()%ued)+0.5,
3913                         (float)(myrand()%ued)+0.5,
3914                         (float)(myrand()%ued)+0.5
3915                 );
3916                 
3917                 bool found_existing = false;
3918
3919                 // Check z-
3920                 try
3921                 {
3922                         s16 z = -1;
3923                         for(s16 y=0; y<ued; y++)
3924                         for(s16 x=0; x<ued; x++)
3925                         {
3926                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3927                                 if(getNode(ap).d == CONTENT_AIR)
3928                                 {
3929                                         orp = v3f(x+1,y+1,0);
3930                                         found_existing = true;
3931                                         goto continue_generating;
3932                                 }
3933                         }
3934                 }
3935                 catch(InvalidPositionException &e){}
3936                 
3937                 // Check z+
3938                 try
3939                 {
3940                         s16 z = ued;
3941                         for(s16 y=0; y<ued; y++)
3942                         for(s16 x=0; x<ued; x++)
3943                         {
3944                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3945                                 if(getNode(ap).d == CONTENT_AIR)
3946                                 {
3947                                         orp = v3f(x+1,y+1,ued-1);
3948                                         found_existing = true;
3949                                         goto continue_generating;
3950                                 }
3951                         }
3952                 }
3953                 catch(InvalidPositionException &e){}
3954                 
3955                 // Check x-
3956                 try
3957                 {
3958                         s16 x = -1;
3959                         for(s16 y=0; y<ued; y++)
3960                         for(s16 z=0; z<ued; z++)
3961                         {
3962                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3963                                 if(getNode(ap).d == CONTENT_AIR)
3964                                 {
3965                                         orp = v3f(0,y+1,z+1);
3966                                         found_existing = true;
3967                                         goto continue_generating;
3968                                 }
3969                         }
3970                 }
3971                 catch(InvalidPositionException &e){}
3972                 
3973                 // Check x+
3974                 try
3975                 {
3976                         s16 x = ued;
3977                         for(s16 y=0; y<ued; y++)
3978                         for(s16 z=0; z<ued; z++)
3979                         {
3980                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3981                                 if(getNode(ap).d == CONTENT_AIR)
3982                                 {
3983                                         orp = v3f(ued-1,y+1,z+1);
3984                                         found_existing = true;
3985                                         goto continue_generating;
3986                                 }
3987                         }
3988                 }
3989                 catch(InvalidPositionException &e){}
3990
3991                 // Check y-
3992                 try
3993                 {
3994                         s16 y = -1;
3995                         for(s16 x=0; x<ued; x++)
3996                         for(s16 z=0; z<ued; z++)
3997                         {
3998                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3999                                 if(getNode(ap).d == CONTENT_AIR)
4000                                 {
4001                                         orp = v3f(x+1,0,z+1);
4002                                         found_existing = true;
4003                                         goto continue_generating;
4004                                 }
4005                         }
4006                 }
4007                 catch(InvalidPositionException &e){}
4008                 
4009                 // Check y+
4010                 try
4011                 {
4012                         s16 y = ued;
4013                         for(s16 x=0; x<ued; x++)
4014                         for(s16 z=0; z<ued; z++)
4015                         {
4016                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4017                                 if(getNode(ap).d == CONTENT_AIR)
4018                                 {
4019                                         orp = v3f(x+1,ued-1,z+1);
4020                                         found_existing = true;
4021                                         goto continue_generating;
4022                                 }
4023                         }
4024                 }
4025                 catch(InvalidPositionException &e){}
4026
4027 continue_generating:
4028                 
4029                 /*
4030                         Choose whether to actually generate dungeon
4031                 */
4032                 bool do_generate_dungeons = true;
4033                 // Don't generate if no part is underground
4034                 if(!some_part_underground)
4035                 {
4036                         do_generate_dungeons = false;
4037                 }
4038                 // Don't generate if mostly underwater surface
4039                 /*else if(mostly_underwater_surface)
4040                 {
4041                         do_generate_dungeons = false;
4042                 }*/
4043                 // Partly underground = cave
4044                 else if(!completely_underground)
4045                 {
4046                         do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4047                 }
4048                 // Found existing dungeon underground
4049                 else if(found_existing && completely_underground)
4050                 {
4051                         do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
4052                 }
4053                 // Underground and no dungeons found
4054                 else
4055                 {
4056                         do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
4057                 }
4058
4059                 if(do_generate_dungeons)
4060                 {
4061                         /*
4062                                 Generate some tunnel starting from orp and ors
4063                         */
4064                         for(u16 i=0; i<3; i++)
4065                         {
4066                                 v3f rp(
4067                                         (float)(myrand()%ued)+0.5,
4068                                         (float)(myrand()%ued)+0.5,
4069                                         (float)(myrand()%ued)+0.5
4070                                 );
4071                                 s16 min_d = 0;
4072                                 s16 max_d = 4;
4073                                 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4074                                 
4075                                 v3f vec = rp - orp;
4076
4077                                 for(float f=0; f<1.0; f+=0.04)
4078                                 {
4079                                         v3f fp = orp + vec * f;
4080                                         v3s16 cp(fp.X, fp.Y, fp.Z);
4081                                         s16 d0 = -rs/2;
4082                                         s16 d1 = d0 + rs - 1;
4083                                         for(s16 z0=d0; z0<=d1; z0++)
4084                                         {
4085                                                 s16 si = rs - abs(z0);
4086                                                 for(s16 x0=-si; x0<=si-1; x0++)
4087                                                 {
4088                                                         s16 si2 = rs - abs(x0);
4089                                                         for(s16 y0=-si2+1; y0<=si2-1; y0++)
4090                                                         {
4091                                                                 s16 z = cp.Z + z0;
4092                                                                 s16 y = cp.Y + y0;
4093                                                                 s16 x = cp.X + x0;
4094                                                                 v3s16 p(x,y,z);
4095                                                                 if(isInArea(p, ued) == false)
4096                                                                         continue;
4097                                                                 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4098                                                         }
4099                                                 }
4100                                         }
4101                                 }
4102
4103                                 orp = rp;
4104                         }
4105                 }
4106         }
4107 #endif
4108
4109         // Set to true if has caves.
4110         // Set when some non-air is changed to air when making caves.
4111         bool has_dungeons = false;
4112
4113         /*
4114                 Apply temporary cave data to block
4115         */
4116
4117         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4118         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4119         {
4120                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4121                 {
4122                         MapNode n = block->getNode(v3s16(x0,y0,z0));
4123
4124                         // Create dungeons
4125                         if(underground_emptiness[
4126                                         ued*ued*(z0*ued/MAP_BLOCKSIZE)
4127                                         +ued*(y0*ued/MAP_BLOCKSIZE)
4128                                         +(x0*ued/MAP_BLOCKSIZE)])
4129                         {
4130                                 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4131                                 {
4132                                         // Has now caves
4133                                         has_dungeons = true;
4134                                         // Set air to node
4135                                         n.d = CONTENT_AIR;
4136                                 }
4137                         }
4138
4139                         block->setNode(v3s16(x0,y0,z0), n);
4140                 }
4141         }
4142         
4143         /*
4144                 This is used for guessing whether or not the block should
4145                 receive sunlight from the top if the block above doesn't exist
4146         */
4147         block->setIsUnderground(completely_underground);
4148
4149         /*
4150                 Force lighting update if some part of block is partly
4151                 underground and has caves.
4152         */
4153         /*if(some_part_underground && !completely_underground && has_dungeons)
4154         {
4155                 //dstream<<"Half-ground caves"<<std::endl;
4156                 lighting_invalidated_blocks[block->getPos()] = block;
4157         }*/
4158         
4159         // DEBUG: Always update lighting
4160         //lighting_invalidated_blocks[block->getPos()] = block;
4161
4162         /*
4163                 Add some minerals
4164         */
4165
4166         if(some_part_underground)
4167         {
4168                 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4169
4170                 /*
4171                         Add meseblocks
4172                 */
4173                 for(s16 i=0; i<underground_level/4 + 1; i++)
4174                 {
4175                         if(myrand()%50 == 0)
4176                         {
4177                                 v3s16 cp(
4178                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
4179                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
4180                                         (myrand()%(MAP_BLOCKSIZE-2))+1
4181                                 );
4182
4183                                 MapNode n;
4184                                 n.d = CONTENT_MESE;
4185                                 
4186                                 for(u16 i=0; i<27; i++)
4187                                 {
4188                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4189                                                 if(myrand()%8 == 0)
4190                                                         block->setNode(cp+g_27dirs[i], n);
4191                                 }
4192                         }
4193                 }
4194
4195                 /*
4196                         Add coal
4197                 */
4198                 u16 coal_amount = 30;
4199                 u16 coal_rareness = 60 / coal_amount;
4200                 if(coal_rareness == 0)
4201                         coal_rareness = 1;
4202                 if(myrand()%coal_rareness == 0)
4203                 {
4204                         u16 a = myrand() % 16;
4205                         u16 amount = coal_amount * a*a*a / 1000;
4206                         for(s16 i=0; i<amount; i++)
4207                         {
4208                                 v3s16 cp(
4209                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
4210                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
4211                                         (myrand()%(MAP_BLOCKSIZE-2))+1
4212                                 );
4213
4214                                 MapNode n;
4215                                 n.d = CONTENT_STONE;
4216                                 n.param = MINERAL_COAL;
4217
4218                                 for(u16 i=0; i<27; i++)
4219                                 {
4220                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4221                                                 if(myrand()%8 == 0)
4222                                                         block->setNode(cp+g_27dirs[i], n);
4223                                 }
4224                         }
4225                 }
4226
4227                 /*
4228                         Add iron
4229                 */
4230                 //TODO: change to iron_amount or whatever
4231                 u16 iron_amount = 15;
4232                 u16 iron_rareness = 60 / iron_amount;
4233                 if(iron_rareness == 0)
4234                         iron_rareness = 1;
4235                 if(myrand()%iron_rareness == 0)
4236                 {
4237                         u16 a = myrand() % 16;
4238                         u16 amount = iron_amount * a*a*a / 1000;
4239                         for(s16 i=0; i<amount; i++)
4240                         {
4241                                 v3s16 cp(
4242                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
4243                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
4244                                         (myrand()%(MAP_BLOCKSIZE-2))+1
4245                                 );
4246
4247                                 MapNode n;
4248                                 n.d = CONTENT_STONE;
4249                                 n.param = MINERAL_IRON;
4250
4251                                 for(u16 i=0; i<27; i++)
4252                                 {
4253                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4254                                                 if(myrand()%8 == 0)
4255                                                         block->setNode(cp+g_27dirs[i], n);
4256                                 }
4257                         }
4258                 }
4259         }
4260         
4261         /*
4262                 Create a few rats in empty blocks underground
4263         */
4264         if(completely_underground)
4265         {
4266                 //for(u16 i=0; i<2; i++)
4267                 {
4268                         v3s16 cp(
4269                                 (myrand()%(MAP_BLOCKSIZE-2))+1,
4270                                 (myrand()%(MAP_BLOCKSIZE-2))+1,
4271                                 (myrand()%(MAP_BLOCKSIZE-2))+1
4272                         );
4273
4274                         // Check that the place is empty
4275                         //if(!is_ground_content(block->getNode(cp).d))
4276                         if(1)
4277                         {
4278                                 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
4279                                 block->addObject(obj);
4280                         }
4281                 }
4282         }
4283         
4284         /*
4285                 Add block to sector.
4286         */
4287         sector->insertBlock(block);
4288         
4289         // Lighting is invalid after generation.
4290         block->setLightingExpired(true);
4291         
4292 #if 0
4293         /*
4294                 Debug information
4295         */
4296         dstream
4297         <<"lighting_invalidated_blocks.size()"
4298         <<", has_dungeons"
4299         <<", completely_ug"
4300         <<", some_part_ug"
4301         <<"  "<<lighting_invalidated_blocks.size()
4302         <<", "<<has_dungeons
4303         <<", "<<completely_underground
4304         <<", "<<some_part_underground
4305         <<std::endl;
4306 #endif
4307
4308         return block;
4309 }
4310
4311 MapBlock * ServerMap::createBlock(v3s16 p)
4312 {
4313         DSTACK("%s: p=(%d,%d,%d)",
4314                         __FUNCTION_NAME, p.X, p.Y, p.Z);
4315         
4316         v2s16 p2d(p.X, p.Z);
4317         s16 block_y = p.Y;
4318         /*
4319                 This will create or load a sector if not found in memory.
4320                 If block exists on disk, it will be loaded.
4321
4322                 NOTE: On old save formats, this will be slow, as it generates
4323                       lighting on blocks for them.
4324         */
4325         ServerMapSector *sector;
4326         try{
4327                 sector = (ServerMapSector*)createSector(p2d);
4328                 assert(sector->getId() == MAPSECTOR_SERVER);
4329         }
4330         /*catch(InvalidPositionException &e)
4331         {
4332                 dstream<<"createBlock: createSector() failed"<<std::endl;
4333                 throw e;
4334         }*/
4335         catch(std::exception &e)
4336         {
4337                 dstream<<"createBlock: createSector() failed: "
4338                                 <<e.what()<<std::endl;
4339                 throw e;
4340         }
4341
4342         /*
4343                 Try to get a block from the sector
4344         */
4345
4346         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4347         if(block)
4348                 return block;
4349         // Create blank
4350         block = sector->createBlankBlock(block_y);
4351         return block;
4352 }
4353
4354 MapBlock * ServerMap::emergeBlock(
4355                 v3s16 p,
4356                 bool only_from_disk,
4357                 core::map<v3s16, MapBlock*> &changed_blocks,
4358                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4359 )
4360 {
4361         DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4362                         __FUNCTION_NAME,
4363                         p.X, p.Y, p.Z, only_from_disk);
4364         
4365         v2s16 p2d(p.X, p.Z);
4366         s16 block_y = p.Y;
4367         /*
4368                 This will create or load a sector if not found in memory.
4369                 If block exists on disk, it will be loaded.
4370
4371                 NOTE: On old save formats, this will be slow, as it generates
4372                       lighting on blocks for them.
4373         */
4374         ServerMapSector *sector;
4375         try{
4376                 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4377                 assert(sector->getId() == MAPSECTOR_SERVER);
4378         }
4379         catch(std::exception &e)
4380         {
4381                 dstream<<"emergeBlock: emergeSector() failed: "
4382                                 <<e.what()<<std::endl;
4383                 throw e;
4384         }
4385
4386         /*
4387                 Try to get a block from the sector
4388         */
4389
4390         bool does_not_exist = false;
4391         bool lighting_expired = false;
4392         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4393
4394         if(block == NULL)
4395         {
4396                 does_not_exist = true;
4397         }
4398         else if(block->isDummy() == true)
4399         {
4400                 does_not_exist = true;
4401         }
4402         else if(block->getLightingExpired())
4403         {
4404                 lighting_expired = true;
4405         }
4406         else
4407         {
4408                 // Valid block
4409                 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4410                 return block;
4411         }
4412         
4413         /*
4414                 If block was not found on disk and not going to generate a
4415                 new one, make sure there is a dummy block in place.
4416         */
4417         if(only_from_disk && (does_not_exist || lighting_expired))
4418         {
4419                 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4420
4421                 if(block == NULL)
4422                 {
4423                         // Create dummy block
4424                         block = new MapBlock(this, p, true);
4425
4426                         // Add block to sector
4427                         sector->insertBlock(block);
4428                 }
4429                 // Done.
4430                 return block;
4431         }
4432
4433         //dstream<<"Not found on disk, generating."<<std::endl;
4434         // 0ms
4435         //TimeTaker("emergeBlock() generate");
4436
4437         //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4438
4439         /*
4440                 If the block doesn't exist, generate the block.
4441         */
4442         if(does_not_exist)
4443         {
4444                 block = generateBlock(p, block, sector, changed_blocks,
4445                                 lighting_invalidated_blocks); 
4446         }
4447
4448         if(lighting_expired)
4449         {
4450                 lighting_invalidated_blocks.insert(p, block);
4451         }
4452
4453         /*
4454                 Initially update sunlight
4455         */
4456         
4457         {
4458                 core::map<v3s16, bool> light_sources;
4459                 bool black_air_left = false;
4460                 bool bottom_invalid =
4461                                 block->propagateSunlight(light_sources, true,
4462                                 &black_air_left, true);
4463
4464                 // If sunlight didn't reach everywhere and part of block is
4465                 // above ground, lighting has to be properly updated
4466                 //if(black_air_left && some_part_underground)
4467                 if(black_air_left)
4468                 {
4469                         lighting_invalidated_blocks[block->getPos()] = block;
4470                 }
4471
4472                 if(bottom_invalid)
4473                 {
4474                         lighting_invalidated_blocks[block->getPos()] = block;
4475                 }
4476         }
4477         
4478         return block;
4479 }
4480
4481 void ServerMap::createDir(std::string path)
4482 {
4483         if(fs::CreateDir(path) == false)
4484         {
4485                 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4486                                 <<"\""<<path<<"\""<<std::endl;
4487                 throw BaseException("ServerMap failed to create directory");
4488         }
4489 }
4490
4491 std::string ServerMap::getSectorSubDir(v2s16 pos)
4492 {
4493         char cc[9];
4494         snprintf(cc, 9, "%.4x%.4x",
4495                         (unsigned int)pos.X&0xffff,
4496                         (unsigned int)pos.Y&0xffff);
4497
4498         return std::string(cc);
4499 }
4500
4501 std::string ServerMap::getSectorDir(v2s16 pos)
4502 {
4503         return m_savedir + "/sectors/" + getSectorSubDir(pos);
4504 }
4505
4506 v2s16 ServerMap::getSectorPos(std::string dirname)
4507 {
4508         if(dirname.size() != 8)
4509                 throw InvalidFilenameException("Invalid sector directory name");
4510         unsigned int x, y;
4511         int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4512         if(r != 2)
4513                 throw InvalidFilenameException("Invalid sector directory name");
4514         v2s16 pos((s16)x, (s16)y);
4515         return pos;
4516 }
4517
4518 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4519 {
4520         v2s16 p2d = getSectorPos(sectordir);
4521
4522         if(blockfile.size() != 4){
4523                 throw InvalidFilenameException("Invalid block filename");
4524         }
4525         unsigned int y;
4526         int r = sscanf(blockfile.c_str(), "%4x", &y);
4527         if(r != 1)
4528                 throw InvalidFilenameException("Invalid block filename");
4529         return v3s16(p2d.X, y, p2d.Y);
4530 }
4531
4532 void ServerMap::save(bool only_changed)
4533 {
4534         DSTACK(__FUNCTION_NAME);
4535         if(m_map_saving_enabled == false)
4536         {
4537                 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4538                 return;
4539         }
4540         
4541         if(only_changed == false)
4542                 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4543                                 <<std::endl;
4544         
4545         saveMasterHeightmap();
4546         
4547         u32 sector_meta_count = 0;
4548         u32 block_count = 0;
4549         
4550         { //sectorlock
4551         JMutexAutoLock lock(m_sector_mutex);
4552         
4553         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4554         for(; i.atEnd() == false; i++)
4555         {
4556                 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4557                 assert(sector->getId() == MAPSECTOR_SERVER);
4558         
4559                 if(sector->differs_from_disk || only_changed == false)
4560                 {
4561                         saveSectorMeta(sector);
4562                         sector_meta_count++;
4563                 }
4564                 core::list<MapBlock*> blocks;
4565                 sector->getBlocks(blocks);
4566                 core::list<MapBlock*>::Iterator j;
4567                 for(j=blocks.begin(); j!=blocks.end(); j++)
4568                 {
4569                         MapBlock *block = *j;
4570                         if(block->getChangedFlag() || only_changed == false)
4571                         {
4572                                 saveBlock(block);
4573                                 block_count++;
4574
4575                                 /*dstream<<"ServerMap: Written block ("
4576                                                 <<block->getPos().X<<","
4577                                                 <<block->getPos().Y<<","
4578                                                 <<block->getPos().Z<<")"
4579                                                 <<std::endl;*/
4580                         }
4581                 }
4582         }
4583
4584         }//sectorlock
4585         
4586         /*
4587                 Only print if something happened or saved whole map
4588         */
4589         if(only_changed == false || sector_meta_count != 0
4590                         || block_count != 0)
4591         {
4592                 dstream<<DTIME<<"ServerMap: Written: "
4593                                 <<sector_meta_count<<" sector metadata files, "
4594                                 <<block_count<<" block files"
4595                                 <<std::endl;
4596         }
4597 }
4598
4599 void ServerMap::loadAll()
4600 {
4601         DSTACK(__FUNCTION_NAME);
4602         dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4603
4604         loadMasterHeightmap();
4605
4606         std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4607
4608         dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4609         
4610         JMutexAutoLock lock(m_sector_mutex);
4611         
4612         s32 counter = 0;
4613         s32 printed_counter = -100000;
4614         s32 count = list.size();
4615
4616         std::vector<fs::DirListNode>::iterator i;
4617         for(i=list.begin(); i!=list.end(); i++)
4618         {
4619                 if(counter > printed_counter + 10)
4620                 {
4621                         dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4622                         printed_counter = counter;
4623                 }
4624                 counter++;
4625
4626                 MapSector *sector = NULL;
4627
4628                 // We want directories
4629                 if(i->dir == false)
4630                         continue;
4631                 try{
4632                         sector = loadSectorMeta(i->name);
4633                 }
4634                 catch(InvalidFilenameException &e)
4635                 {
4636                         // This catches unknown crap in directory
4637                 }
4638                 
4639                 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4640                                 (m_savedir+"/sectors/"+i->name);
4641                 std::vector<fs::DirListNode>::iterator i2;
4642                 for(i2=list2.begin(); i2!=list2.end(); i2++)
4643                 {
4644                         // We want files
4645                         if(i2->dir)
4646                                 continue;
4647                         try{
4648                                 loadBlock(i->name, i2->name, sector);
4649                         }
4650                         catch(InvalidFilenameException &e)
4651                         {
4652                                 // This catches unknown crap in directory
4653                         }
4654                 }
4655         }
4656         dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4657 }
4658
4659 void ServerMap::saveMasterHeightmap()
4660 {
4661         DSTACK(__FUNCTION_NAME);
4662         
4663         dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4664
4665         createDir(m_savedir);
4666         
4667         /*std::string fullpath = m_savedir + "/master_heightmap";
4668         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4669         if(o.good() == false)
4670                 throw FileNotGoodException("Cannot open master heightmap");*/
4671         
4672         // Format used for writing
4673         //u8 version = SER_FMT_VER_HIGHEST;
4674 }
4675
4676 void ServerMap::loadMasterHeightmap()
4677 {
4678         DSTACK(__FUNCTION_NAME);
4679         
4680         dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4681
4682         /*std::string fullpath = m_savedir + "/master_heightmap";
4683         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4684         if(is.good() == false)
4685                 throw FileNotGoodException("Cannot open master heightmap");*/
4686 }
4687
4688 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4689 {
4690         DSTACK(__FUNCTION_NAME);
4691         // Format used for writing
4692         u8 version = SER_FMT_VER_HIGHEST;
4693         // Get destination
4694         v2s16 pos = sector->getPos();
4695         createDir(m_savedir);
4696         createDir(m_savedir+"/sectors");
4697         std::string dir = getSectorDir(pos);
4698         createDir(dir);
4699         
4700         std::string fullpath = dir + "/heightmap";
4701         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4702         if(o.good() == false)
4703                 throw FileNotGoodException("Cannot open master heightmap");
4704
4705         sector->serialize(o, version);
4706         
4707         sector->differs_from_disk = false;
4708 }
4709
4710 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4711 {
4712         DSTACK(__FUNCTION_NAME);
4713         // Get destination
4714         v2s16 p2d = getSectorPos(dirname);
4715         std::string dir = m_savedir + "/sectors/" + dirname;
4716         
4717         std::string fullpath = dir + "/heightmap";
4718         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4719         if(is.good() == false)
4720                 throw FileNotGoodException("Cannot open sector heightmap");
4721
4722         ServerMapSector *sector = ServerMapSector::deSerialize
4723                         (is, this, p2d, m_sectors);
4724         
4725         sector->differs_from_disk = false;
4726
4727         return sector;
4728 }
4729
4730 bool ServerMap::loadSectorFull(v2s16 p2d)
4731 {
4732         DSTACK(__FUNCTION_NAME);
4733         std::string sectorsubdir = getSectorSubDir(p2d);
4734
4735         MapSector *sector = NULL;
4736
4737         JMutexAutoLock lock(m_sector_mutex);
4738
4739         try{
4740                 sector = loadSectorMeta(sectorsubdir);
4741         }
4742         catch(InvalidFilenameException &e)
4743         {
4744                 return false;
4745         }
4746         catch(FileNotGoodException &e)
4747         {
4748                 return false;
4749         }
4750         catch(std::exception &e)
4751         {
4752                 return false;
4753         }
4754
4755         std::vector<fs::DirListNode> list2 = fs::GetDirListing
4756                         (m_savedir+"/sectors/"+sectorsubdir);
4757         std::vector<fs::DirListNode>::iterator i2;
4758         for(i2=list2.begin(); i2!=list2.end(); i2++)
4759         {
4760                 // We want files
4761                 if(i2->dir)
4762                         continue;
4763                 try{
4764                         loadBlock(sectorsubdir, i2->name, sector);
4765                 }
4766                 catch(InvalidFilenameException &e)
4767                 {
4768                         // This catches unknown crap in directory
4769                 }
4770         }
4771         return true;
4772 }
4773
4774 #if 0
4775 bool ServerMap::deFlushSector(v2s16 p2d)
4776 {
4777         DSTACK(__FUNCTION_NAME);
4778         // See if it already exists in memory
4779         try{
4780                 MapSector *sector = getSectorNoGenerate(p2d);
4781                 return true;
4782         }
4783         catch(InvalidPositionException &e)
4784         {
4785                 /*
4786                         Try to load the sector from disk.
4787                 */
4788                 if(loadSectorFull(p2d) == true)
4789                 {
4790                         return true;
4791                 }
4792         }
4793         return false;
4794 }
4795 #endif
4796
4797 void ServerMap::saveBlock(MapBlock *block)
4798 {
4799         DSTACK(__FUNCTION_NAME);
4800         /*
4801                 Dummy blocks are not written
4802         */
4803         if(block->isDummy())
4804         {
4805                 /*v3s16 p = block->getPos();
4806                 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4807                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4808                 return;
4809         }
4810
4811         // Format used for writing
4812         u8 version = SER_FMT_VER_HIGHEST;
4813         // Get destination
4814         v3s16 p3d = block->getPos();
4815         v2s16 p2d(p3d.X, p3d.Z);
4816         createDir(m_savedir);
4817         createDir(m_savedir+"/sectors");
4818         std::string dir = getSectorDir(p2d);
4819         createDir(dir);
4820         
4821         // Block file is map/sectors/xxxxxxxx/xxxx
4822         char cc[5];
4823         snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4824         std::string fullpath = dir + "/" + cc;
4825         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4826         if(o.good() == false)
4827                 throw FileNotGoodException("Cannot open block data");
4828
4829         /*
4830                 [0] u8 serialization version
4831                 [1] data
4832         */
4833         o.write((char*)&version, 1);
4834         
4835         block->serialize(o, version);
4836
4837         /*
4838                 Versions up from 9 have block objects.
4839         */
4840         if(version >= 9)
4841         {
4842                 block->serializeObjects(o, version);
4843         }
4844         
4845         // We just wrote it to the disk
4846         block->resetChangedFlag();
4847 }
4848
4849 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4850 {
4851         DSTACK(__FUNCTION_NAME);
4852
4853         try{
4854
4855         // Block file is map/sectors/xxxxxxxx/xxxx
4856         std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4857         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4858         if(is.good() == false)
4859                 throw FileNotGoodException("Cannot open block file");
4860
4861         v3s16 p3d = getBlockPos(sectordir, blockfile);
4862         v2s16 p2d(p3d.X, p3d.Z);
4863         
4864         assert(sector->getPos() == p2d);
4865         
4866         u8 version = SER_FMT_VER_INVALID;
4867         is.read((char*)&version, 1);
4868
4869         /*u32 block_size = MapBlock::serializedLength(version);
4870         SharedBuffer<u8> data(block_size);
4871         is.read((char*)*data, block_size);*/
4872
4873         // This will always return a sector because we're the server
4874         //MapSector *sector = emergeSector(p2d);
4875
4876         MapBlock *block = NULL;
4877         bool created_new = false;
4878         try{
4879                 block = sector->getBlockNoCreate(p3d.Y);
4880         }
4881         catch(InvalidPositionException &e)
4882         {
4883                 block = sector->createBlankBlockNoInsert(p3d.Y);
4884                 created_new = true;
4885         }
4886         
4887         // deserialize block data
4888         block->deSerialize(is, version);
4889         
4890         /*
4891                 Versions up from 9 have block objects.
4892         */
4893         if(version >= 9)
4894         {
4895                 block->updateObjects(is, version, NULL, 0);
4896         }
4897
4898         if(created_new)
4899                 sector->insertBlock(block);
4900         
4901         /*
4902                 Convert old formats to new and save
4903         */
4904
4905         // Save old format blocks in new format
4906         if(version < SER_FMT_VER_HIGHEST)
4907         {
4908                 saveBlock(block);
4909         }
4910         
4911         // We just loaded it from the disk, so it's up-to-date.
4912         block->resetChangedFlag();
4913
4914         }
4915         catch(SerializationError &e)
4916         {
4917                 dstream<<"WARNING: Invalid block data on disk "
4918                                 "(SerializationError). Ignoring."
4919                                 <<std::endl;
4920         }
4921 }
4922
4923 // Gets from master heightmap
4924 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4925 {
4926         dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4927         //assert(m_heightmap != NULL);
4928         /*
4929                 Corner definition:
4930                 v2s16(0,0),
4931                 v2s16(1,0),
4932                 v2s16(1,1),
4933                 v2s16(0,1),
4934         */
4935         /*corners[0] = m_heightmap->getGroundHeight
4936                         ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4937         corners[1] = m_heightmap->getGroundHeight
4938                         ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4939         corners[2] = m_heightmap->getGroundHeight
4940                         ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4941         corners[3] = m_heightmap->getGroundHeight
4942                         ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);*/
4943 }
4944
4945 void ServerMap::PrintInfo(std::ostream &out)
4946 {
4947         out<<"ServerMap: ";
4948 }
4949
4950 #ifndef SERVER
4951
4952 /*
4953         ClientMap
4954 */
4955
4956 ClientMap::ClientMap(
4957                 Client *client,
4958                 MapDrawControl &control,
4959                 scene::ISceneNode* parent,
4960                 scene::ISceneManager* mgr,
4961                 s32 id
4962 ):
4963         Map(dout_client),
4964         scene::ISceneNode(parent, mgr, id),
4965         m_client(client),
4966         m_control(control)
4967 {
4968         //mesh_mutex.Init();
4969
4970         /*m_box = core::aabbox3d<f32>(0,0,0,
4971                         map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4972         /*m_box = core::aabbox3d<f32>(0,0,0,
4973                         map->getSizeNodes().X * BS,
4974                         map->getSizeNodes().Y * BS,
4975                         map->getSizeNodes().Z * BS);*/
4976         m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4977                         BS*1000000,BS*1000000,BS*1000000);
4978         
4979         //setPosition(v3f(BS,BS,BS));
4980 }
4981
4982 ClientMap::~ClientMap()
4983 {
4984         /*JMutexAutoLock lock(mesh_mutex);
4985         
4986         if(mesh != NULL)
4987         {
4988                 mesh->drop();
4989                 mesh = NULL;
4990         }*/
4991 }
4992
4993 MapSector * ClientMap::emergeSector(v2s16 p2d)
4994 {
4995         DSTACK(__FUNCTION_NAME);
4996         // Check that it doesn't exist already
4997         try{
4998                 return getSectorNoGenerate(p2d);
4999         }
5000         catch(InvalidPositionException &e)
5001         {
5002         }
5003         
5004         // Create a sector with no heightmaps
5005         ClientMapSector *sector = new ClientMapSector(this, p2d);
5006         
5007         {
5008                 JMutexAutoLock lock(m_sector_mutex);
5009                 m_sectors.insert(p2d, sector);
5010         }
5011         
5012         return sector;
5013 }
5014
5015 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5016 {
5017         DSTACK(__FUNCTION_NAME);
5018         ClientMapSector *sector = NULL;
5019
5020         JMutexAutoLock lock(m_sector_mutex);
5021         
5022         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5023
5024         if(n != NULL)
5025         {
5026                 sector = (ClientMapSector*)n->getValue();
5027                 assert(sector->getId() == MAPSECTOR_CLIENT);
5028         }
5029         else
5030         {
5031                 sector = new ClientMapSector(this, p2d);
5032                 {
5033                         JMutexAutoLock lock(m_sector_mutex);
5034                         m_sectors.insert(p2d, sector);
5035                 }
5036         }
5037
5038         sector->deSerialize(is);
5039 }
5040
5041 void ClientMap::OnRegisterSceneNode()
5042 {
5043         if(IsVisible)
5044         {
5045                 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5046                 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5047         }
5048
5049         ISceneNode::OnRegisterSceneNode();
5050 }
5051
5052 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5053 {
5054         //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5055         DSTACK(__FUNCTION_NAME);
5056
5057         bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5058
5059         /*
5060                 Get time for measuring timeout.
5061                 
5062                 Measuring time is very useful for long delays when the
5063                 machine is swapping a lot.
5064         */
5065         int time1 = time(0);
5066
5067         u32 daynight_ratio = m_client->getDayNightRatio();
5068
5069         m_camera_mutex.Lock();
5070         v3f camera_position = m_camera_position;
5071         v3f camera_direction = m_camera_direction;
5072         m_camera_mutex.Unlock();
5073
5074         /*
5075                 Get all blocks and draw all visible ones
5076         */
5077
5078         v3s16 cam_pos_nodes(
5079                         camera_position.X / BS,
5080                         camera_position.Y / BS,
5081                         camera_position.Z / BS);
5082
5083         v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5084
5085         v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5086         v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5087
5088         // Take a fair amount as we will be dropping more out later
5089         v3s16 p_blocks_min(
5090                         p_nodes_min.X / MAP_BLOCKSIZE - 1,
5091                         p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5092                         p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5093         v3s16 p_blocks_max(
5094                         p_nodes_max.X / MAP_BLOCKSIZE + 1,
5095                         p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5096                         p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5097         
5098         u32 vertex_count = 0;
5099         
5100         // For limiting number of mesh updates per frame
5101         u32 mesh_update_count = 0;
5102         
5103         u32 blocks_would_have_drawn = 0;
5104         u32 blocks_drawn = 0;
5105
5106         //NOTE: The sectors map should be locked but we're not doing it
5107         // because it'd cause too much delays
5108
5109         int timecheck_counter = 0;
5110         core::map<v2s16, MapSector*>::Iterator si;
5111         si = m_sectors.getIterator();
5112         for(; si.atEnd() == false; si++)
5113         {
5114                 {
5115                         timecheck_counter++;
5116                         if(timecheck_counter > 50)
5117                         {
5118                                 int time2 = time(0);
5119                                 if(time2 > time1 + 4)
5120                                 {
5121                                         dstream<<"ClientMap::renderMap(): "
5122                                                 "Rendering takes ages, returning."
5123                                                 <<std::endl;
5124                                         return;
5125                                 }
5126                         }
5127                 }
5128
5129                 MapSector *sector = si.getNode()->getValue();
5130                 v2s16 sp = sector->getPos();
5131                 
5132                 if(m_control.range_all == false)
5133                 {
5134                         if(sp.X < p_blocks_min.X
5135                         || sp.X > p_blocks_max.X
5136                         || sp.Y < p_blocks_min.Z
5137                         || sp.Y > p_blocks_max.Z)
5138                                 continue;
5139                 }
5140
5141                 core::list< MapBlock * > sectorblocks;
5142                 sector->getBlocks(sectorblocks);
5143                 
5144                 /*
5145                         Draw blocks
5146                 */
5147
5148                 core::list< MapBlock * >::Iterator i;
5149                 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5150                 {
5151                         MapBlock *block = *i;
5152
5153                         /*
5154                                 Compare block position to camera position, skip
5155                                 if not seen on display
5156                         */
5157                         
5158                         float range = 100000 * BS;
5159                         if(m_control.range_all == false)
5160                                 range = m_control.wanted_range * BS;
5161                         
5162                         float d = 0.0;
5163                         if(isBlockInSight(block->getPos(), camera_position,
5164                                         camera_direction, range, &d) == false)
5165                         {
5166                                 continue;
5167                         }
5168
5169 #if 0                   
5170                         v3s16 blockpos_nodes = block->getPosRelative();
5171                         
5172                         // Block center position
5173                         v3f blockpos(
5174                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5175                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5176                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5177                         );
5178
5179                         // Block position relative to camera
5180                         v3f blockpos_relative = blockpos - camera_position;
5181
5182                         // Distance in camera direction (+=front, -=back)
5183                         f32 dforward = blockpos_relative.dotProduct(camera_direction);
5184
5185                         // Total distance
5186                         f32 d = blockpos_relative.getLength();
5187                         
5188                         if(m_control.range_all == false)
5189                         {
5190                                 // If block is far away, don't draw it
5191                                 if(d > m_control.wanted_range * BS)
5192                                         continue;
5193                         }
5194                         
5195                         // Maximum radius of a block
5196                         f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5197                         
5198                         // If block is (nearly) touching the camera, don't
5199                         // bother validating further (that is, render it anyway)
5200                         if(d > block_max_radius * 1.5)
5201                         {
5202                                 // Cosine of the angle between the camera direction
5203                                 // and the block direction (camera_direction is an unit vector)
5204                                 f32 cosangle = dforward / d;
5205                                 
5206                                 // Compensate for the size of the block
5207                                 // (as the block has to be shown even if it's a bit off FOV)
5208                                 // This is an estimate.
5209                                 cosangle += block_max_radius / dforward;
5210
5211                                 // If block is not in the field of view, skip it
5212                                 //if(cosangle < cos(FOV_ANGLE/2))
5213                                 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5214                                         continue;
5215                         }
5216 #endif                  
5217 #if 0
5218                         v3s16 blockpos_nodes = block->getPosRelative();
5219                         
5220                         // Block center position
5221                         v3f blockpos(
5222                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5223                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5224                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5225                         );
5226
5227                         // Block position relative to camera
5228                         v3f blockpos_relative = blockpos - camera_position;
5229
5230                         // Total distance
5231                         f32 d = blockpos_relative.getLength();
5232 #endif
5233                         
5234 #if 1
5235                         /*
5236                                 Update expired mesh
5237                         */
5238
5239                         bool mesh_expired = false;
5240                         
5241                         {
5242                                 JMutexAutoLock lock(block->mesh_mutex);
5243
5244                                 mesh_expired = block->getMeshExpired();
5245
5246                                 // Mesh has not been expired and there is no mesh:
5247                                 // block has no content
5248                                 if(block->mesh == NULL && mesh_expired == false)
5249                                         continue;
5250                         }
5251
5252                         f32 faraway = BS*50;
5253                         //f32 faraway = m_control.wanted_range * BS;
5254                         
5255                         /*
5256                                 This has to be done with the mesh_mutex unlocked
5257                         */
5258                         // Pretty random but this should work somewhat nicely
5259                         if(mesh_expired && (
5260                                         (mesh_update_count < 3
5261                                                 && (d < faraway || mesh_update_count < 2)
5262                                         )
5263                                         || 
5264                                         (m_control.range_all && mesh_update_count < 20)
5265                                 )
5266                         )
5267                         /*if(mesh_expired && mesh_update_count < 6
5268                                         && (d < faraway || mesh_update_count < 3))*/
5269                         {
5270                                 mesh_update_count++;
5271
5272                                 // Mesh has been expired: generate new mesh
5273                                 //block->updateMeshes(daynight_i);
5274                                 block->updateMesh(daynight_ratio);
5275
5276                                 mesh_expired = false;
5277                         }
5278                         
5279                         /*
5280                                 Don't draw an expired mesh that is far away
5281                         */
5282                         /*if(mesh_expired && d >= faraway)
5283                         //if(mesh_expired)
5284                         {
5285                                 // Instead, delete it
5286                                 JMutexAutoLock lock(block->mesh_mutex);
5287                                 if(block->mesh)
5288                                 {
5289                                         block->mesh->drop();
5290                                         block->mesh = NULL;
5291                                 }
5292                                 // And continue to next block
5293                                 continue;
5294                         }*/
5295 #endif
5296                         /*
5297                                 Draw the faces of the block
5298                         */
5299                         {
5300                                 JMutexAutoLock lock(block->mesh_mutex);
5301
5302                                 scene::SMesh *mesh = block->mesh;
5303
5304                                 if(mesh == NULL)
5305                                         continue;
5306                                 
5307                                 blocks_would_have_drawn++;
5308                                 if(blocks_drawn >= m_control.wanted_max_blocks
5309                                                 && m_control.range_all == false
5310                                                 && d > m_control.wanted_min_range * BS)
5311                                         continue;
5312                                 blocks_drawn++;
5313
5314                                 u32 c = mesh->getMeshBufferCount();
5315
5316                                 for(u32 i=0; i<c; i++)
5317                                 {
5318                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5319                                         const video::SMaterial& material = buf->getMaterial();
5320                                         video::IMaterialRenderer* rnd =
5321                                                         driver->getMaterialRenderer(material.MaterialType);
5322                                         bool transparent = (rnd && rnd->isTransparent());
5323                                         // Render transparent on transparent pass and likewise.
5324                                         if(transparent == is_transparent_pass)
5325                                         {
5326                                                 driver->setMaterial(buf->getMaterial());
5327                                                 driver->drawMeshBuffer(buf);
5328                                                 vertex_count += buf->getVertexCount();
5329                                         }
5330                                 }
5331                         }
5332                 } // foreach sectorblocks
5333         }
5334         
5335         m_control.blocks_drawn = blocks_drawn;
5336         m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5337
5338         /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5339                         <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5340 }
5341
5342 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5343                 core::map<v3s16, MapBlock*> *affected_blocks)
5344 {
5345         bool changed = false;
5346         /*
5347                 Add it to all blocks touching it
5348         */
5349         v3s16 dirs[7] = {
5350                 v3s16(0,0,0), // this
5351                 v3s16(0,0,1), // back
5352                 v3s16(0,1,0), // top
5353                 v3s16(1,0,0), // right
5354                 v3s16(0,0,-1), // front
5355                 v3s16(0,-1,0), // bottom
5356                 v3s16(-1,0,0), // left
5357         };
5358         for(u16 i=0; i<7; i++)
5359         {
5360                 v3s16 p2 = p + dirs[i];
5361                 // Block position of neighbor (or requested) node
5362                 v3s16 blockpos = getNodeBlockPos(p2);
5363                 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5364                 if(blockref == NULL)
5365                         continue;
5366                 // Relative position of requested node
5367                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5368                 if(blockref->setTempMod(relpos, mod))
5369                 {
5370                         changed = true;
5371                 }
5372         }
5373         if(changed && affected_blocks!=NULL)
5374         {
5375                 for(u16 i=0; i<7; i++)
5376                 {
5377                         v3s16 p2 = p + dirs[i];
5378                         // Block position of neighbor (or requested) node
5379                         v3s16 blockpos = getNodeBlockPos(p2);
5380                         MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5381                         if(blockref == NULL)
5382                                 continue;
5383                         affected_blocks->insert(blockpos, blockref);
5384                 }
5385         }
5386         return changed;
5387 }
5388
5389 bool ClientMap::clearTempMod(v3s16 p,
5390                 core::map<v3s16, MapBlock*> *affected_blocks)
5391 {
5392         bool changed = false;
5393         v3s16 dirs[7] = {
5394                 v3s16(0,0,0), // this
5395                 v3s16(0,0,1), // back
5396                 v3s16(0,1,0), // top
5397                 v3s16(1,0,0), // right
5398                 v3s16(0,0,-1), // front
5399                 v3s16(0,-1,0), // bottom
5400                 v3s16(-1,0,0), // left
5401         };
5402         for(u16 i=0; i<7; i++)
5403         {
5404                 v3s16 p2 = p + dirs[i];
5405                 // Block position of neighbor (or requested) node
5406                 v3s16 blockpos = getNodeBlockPos(p2);
5407                 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5408                 if(blockref == NULL)
5409                         continue;
5410                 // Relative position of requested node
5411                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5412                 if(blockref->clearTempMod(relpos))
5413                 {
5414                         changed = true;
5415                 }
5416         }
5417         if(changed && affected_blocks!=NULL)
5418         {
5419                 for(u16 i=0; i<7; i++)
5420                 {
5421                         v3s16 p2 = p + dirs[i];
5422                         // Block position of neighbor (or requested) node
5423                         v3s16 blockpos = getNodeBlockPos(p2);
5424                         MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5425                         if(blockref == NULL)
5426                                 continue;
5427                         affected_blocks->insert(blockpos, blockref);
5428                 }
5429         }
5430         return changed;
5431 }
5432
5433 void ClientMap::PrintInfo(std::ostream &out)
5434 {
5435         out<<"ClientMap: ";
5436 }
5437
5438 #endif // !SERVER
5439
5440 /*
5441         MapVoxelManipulator
5442 */
5443
5444 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5445 {
5446         m_map = map;
5447 }
5448
5449 MapVoxelManipulator::~MapVoxelManipulator()
5450 {
5451         /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5452                         <<std::endl;*/
5453 }
5454
5455 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5456 {
5457         TimeTaker timer1("emerge", &emerge_time);
5458
5459         // Units of these are MapBlocks
5460         v3s16 p_min = getNodeBlockPos(a.MinEdge);
5461         v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5462
5463         VoxelArea block_area_nodes
5464                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5465
5466         addArea(block_area_nodes);
5467
5468         for(s32 z=p_min.Z; z<=p_max.Z; z++)
5469         for(s32 y=p_min.Y; y<=p_max.Y; y++)
5470         for(s32 x=p_min.X; x<=p_max.X; x++)
5471         {
5472                 v3s16 p(x,y,z);
5473                 core::map<v3s16, bool>::Node *n;
5474                 n = m_loaded_blocks.find(p);
5475                 if(n != NULL)
5476                         continue;
5477                 
5478                 bool block_data_inexistent = false;
5479                 try
5480                 {
5481                         TimeTaker timer1("emerge load", &emerge_load_time);
5482
5483                         /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5484                                         <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5485                                         <<" wanted area: ";
5486                         a.print(dstream);
5487                         dstream<<std::endl;*/
5488                         
5489                         MapBlock *block = m_map->getBlockNoCreate(p);
5490                         if(block->isDummy())
5491                                 block_data_inexistent = true;
5492                         else
5493                                 block->copyTo(*this);
5494                 }
5495                 catch(InvalidPositionException &e)
5496                 {
5497                         block_data_inexistent = true;
5498                 }
5499
5500                 if(block_data_inexistent)
5501                 {
5502                         VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5503                         // Fill with VOXELFLAG_INEXISTENT
5504                         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5505                         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5506                         {
5507                                 s32 i = m_area.index(a.MinEdge.X,y,z);
5508                                 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5509                         }
5510                 }
5511
5512                 m_loaded_blocks.insert(p, !block_data_inexistent);
5513         }
5514
5515         //dstream<<"emerge done"<<std::endl;
5516 }
5517
5518 /*
5519         SUGG: Add an option to only update eg. water and air nodes.
5520               This will make it interfere less with important stuff if
5521                   run on background.
5522 */
5523 void MapVoxelManipulator::blitBack
5524                 (core::map<v3s16, MapBlock*> & modified_blocks)
5525 {
5526         if(m_area.getExtent() == v3s16(0,0,0))
5527                 return;
5528         
5529         //TimeTaker timer1("blitBack");
5530
5531         /*dstream<<"blitBack(): m_loaded_blocks.size()="
5532                         <<m_loaded_blocks.size()<<std::endl;*/
5533         
5534         /*
5535                 Initialize block cache
5536         */
5537         v3s16 blockpos_last;
5538         MapBlock *block = NULL;
5539         bool block_checked_in_modified = false;
5540
5541         for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5542         for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5543         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5544         {
5545                 v3s16 p(x,y,z);
5546
5547                 u8 f = m_flags[m_area.index(p)];
5548                 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5549                         continue;
5550
5551                 MapNode &n = m_data[m_area.index(p)];
5552                         
5553                 v3s16 blockpos = getNodeBlockPos(p);
5554                 
5555                 try
5556                 {
5557                         // Get block
5558                         if(block == NULL || blockpos != blockpos_last){
5559                                 block = m_map->getBlockNoCreate(blockpos);
5560                                 blockpos_last = blockpos;
5561                                 block_checked_in_modified = false;
5562                         }
5563                         
5564                         // Calculate relative position in block
5565                         v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5566
5567                         // Don't continue if nothing has changed here
5568                         if(block->getNode(relpos) == n)
5569                                 continue;
5570
5571                         //m_map->setNode(m_area.MinEdge + p, n);
5572                         block->setNode(relpos, n);
5573                         
5574                         /*
5575                                 Make sure block is in modified_blocks
5576                         */
5577                         if(block_checked_in_modified == false)
5578                         {
5579                                 modified_blocks[blockpos] = block;
5580                                 block_checked_in_modified = true;
5581                         }
5582                 }
5583                 catch(InvalidPositionException &e)
5584                 {
5585                 }
5586         }
5587 }
5588
5589 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5590                 MapVoxelManipulator(map)
5591 {
5592 }
5593
5594 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5595 {
5596 }
5597
5598 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5599 {
5600         // Just create the area so that it can be pointed to
5601         VoxelManipulator::emerge(a, caller_id);
5602 }
5603
5604 void ManualMapVoxelManipulator::initialEmerge(
5605                 v3s16 blockpos_min, v3s16 blockpos_max)
5606 {
5607         TimeTaker timer1("initialEmerge", &emerge_time);
5608
5609         // Units of these are MapBlocks
5610         v3s16 p_min = blockpos_min;
5611         v3s16 p_max = blockpos_max;
5612
5613         VoxelArea block_area_nodes
5614                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5615         
5616         u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5617         if(size_MB >= 1)
5618         {
5619                 dstream<<"initialEmerge: area: ";
5620                 block_area_nodes.print(dstream);
5621                 dstream<<" ("<<size_MB<<"MB)";
5622                 dstream<<std::endl;
5623         }
5624
5625         addArea(block_area_nodes);
5626
5627         for(s32 z=p_min.Z; z<=p_max.Z; z++)
5628         for(s32 y=p_min.Y; y<=p_max.Y; y++)
5629         for(s32 x=p_min.X; x<=p_max.X; x++)
5630         {
5631                 v3s16 p(x,y,z);
5632                 core::map<v3s16, bool>::Node *n;
5633                 n = m_loaded_blocks.find(p);
5634                 if(n != NULL)
5635                         continue;
5636                 
5637                 bool block_data_inexistent = false;
5638                 try
5639                 {
5640                         TimeTaker timer1("emerge load", &emerge_load_time);
5641
5642                         MapBlock *block = m_map->getBlockNoCreate(p);
5643                         if(block->isDummy())
5644                                 block_data_inexistent = true;
5645                         else
5646                                 block->copyTo(*this);
5647                 }
5648                 catch(InvalidPositionException &e)
5649                 {
5650                         block_data_inexistent = true;
5651                 }
5652
5653                 if(block_data_inexistent)
5654                 {
5655                         /*
5656                                 Mark area inexistent
5657                         */
5658                         VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5659                         // Fill with VOXELFLAG_INEXISTENT
5660                         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5661                         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5662                         {
5663                                 s32 i = m_area.index(a.MinEdge.X,y,z);
5664                                 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5665                         }
5666                 }
5667
5668                 m_loaded_blocks.insert(p, !block_data_inexistent);
5669         }
5670 }
5671
5672 void ManualMapVoxelManipulator::blitBackAll(
5673                 core::map<v3s16, MapBlock*> * modified_blocks)
5674 {
5675         if(m_area.getExtent() == v3s16(0,0,0))
5676                 return;
5677         
5678         /*
5679                 Copy data of all blocks
5680         */
5681         for(core::map<v3s16, bool>::Iterator
5682                         i = m_loaded_blocks.getIterator();
5683                         i.atEnd() == false; i++)
5684         {
5685                 bool existed = i.getNode()->getValue();
5686                 if(existed == false)
5687                         continue;
5688                 v3s16 p = i.getNode()->getKey();
5689                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5690                 if(block == NULL)
5691                 {
5692                         dstream<<"WARNING: "<<__FUNCTION_NAME
5693                                         <<": got NULL block "
5694                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5695                                         <<std::endl;
5696                         continue;
5697                 }
5698
5699                 block->copyFrom(*this);
5700
5701                 if(modified_blocks)
5702                         modified_blocks->insert(p, block);
5703         }
5704 }
5705
5706 //END