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