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