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