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