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