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