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