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