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