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