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