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