changed default minimum viewing range to a bit lower
[oweals/minetest.git] / src / map.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "map.h"
21 #include "main.h"
22 #include "jmutexautolock.h"
23 #include "client.h"
24 #include "filesys.h"
25 #include "utility.h"
26 #include "voxel.h"
27 #include "porting.h"
28 #include "mineral.h"
29
30 /*
31         Map
32 */
33
34 Map::Map(std::ostream &dout):
35         m_dout(dout),
36         m_camera_position(0,0,0),
37         m_camera_direction(0,0,1),
38         m_sector_cache(NULL),
39         m_hwrapper(this)
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         //dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
1459
1460         while(m_transforming_liquid.size() != 0)
1461         {
1462                 /*
1463                         Get a queued transforming liquid node
1464                 */
1465                 v3s16 p0 = m_transforming_liquid.pop_front();
1466
1467                 MapNode n0 = getNode(p0);
1468                 
1469                 // Don't deal with non-liquids
1470                 if(content_liquid(n0.d) == false)
1471                         continue;
1472
1473                 bool is_source = !content_flowing_liquid(n0.d);
1474                 
1475                 u8 liquid_level = 8;
1476                 if(is_source == false)
1477                         liquid_level = n0.param2 & 0x0f;
1478                 
1479                 // Turn possible source into non-source
1480                 u8 nonsource_c = make_liquid_flowing(n0.d);
1481
1482                 /*
1483                         If not source, check that some node flows into this one
1484                         and what is the level of liquid in this one
1485                 */
1486                 if(is_source == false)
1487                 {
1488                         s8 new_liquid_level_max = -1;
1489
1490                         v3s16 dirs_from[5] = {
1491                                 v3s16(0,1,0), // top
1492                                 v3s16(0,0,1), // back
1493                                 v3s16(1,0,0), // right
1494                                 v3s16(0,0,-1), // front
1495                                 v3s16(-1,0,0), // left
1496                         };
1497                         for(u16 i=0; i<5; i++)
1498                         {
1499                                 try
1500                                 {
1501
1502                                 bool from_top = (i==0);
1503
1504                                 v3s16 p2 = p0 + dirs_from[i];
1505                                 MapNode n2 = getNode(p2);
1506
1507                                 if(content_liquid(n2.d))
1508                                 {
1509                                         u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1510                                         // Check that the liquids are the same type
1511                                         if(n2_nonsource_c != nonsource_c)
1512                                         {
1513                                                 dstream<<"WARNING: Not handling: different liquids"
1514                                                                 " collide"<<std::endl;
1515                                                 continue;
1516                                         }
1517                                         bool n2_is_source = !content_flowing_liquid(n2.d);
1518                                         s8 n2_liquid_level = 8;
1519                                         if(n2_is_source == false)
1520                                                 n2_liquid_level = n2.param2 & 0x07;
1521                                         
1522                                         s8 new_liquid_level = -1;
1523                                         if(from_top)
1524                                         {
1525                                                 //new_liquid_level = 7;
1526                                                 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1527                                                         new_liquid_level = 7;
1528                                                 else
1529                                                         new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1530                                         }
1531                                         else if(n2_liquid_level > 0)
1532                                         {
1533                                                 new_liquid_level = n2_liquid_level - 1;
1534                                         }
1535
1536                                         if(new_liquid_level > new_liquid_level_max)
1537                                                 new_liquid_level_max = new_liquid_level;
1538                                 }
1539
1540                                 }catch(InvalidPositionException &e)
1541                                 {
1542                                 }
1543                         } //for
1544                         
1545                         /*
1546                                 If liquid level should be something else, update it and
1547                                 add all the neighboring water nodes to the transform queue.
1548                         */
1549                         if(new_liquid_level_max != liquid_level)
1550                         {
1551                                 if(new_liquid_level_max == -1)
1552                                 {
1553                                         // Remove water alltoghether
1554                                         n0.d = CONTENT_AIR;
1555                                         n0.param2 = 0;
1556                                         setNode(p0, n0);
1557                                 }
1558                                 else
1559                                 {
1560                                         n0.param2 = new_liquid_level_max;
1561                                         setNode(p0, n0);
1562                                 }
1563                                 
1564                                 // Block has been modified
1565                                 {
1566                                         v3s16 blockpos = getNodeBlockPos(p0);
1567                                         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1568                                         if(block != NULL)
1569                                                 modified_blocks.insert(blockpos, block);
1570                                 }
1571                                 
1572                                 /*
1573                                         Add neighboring non-source liquid nodes to transform queue.
1574                                 */
1575                                 v3s16 dirs[6] = {
1576                                         v3s16(0,0,1), // back
1577                                         v3s16(0,1,0), // top
1578                                         v3s16(1,0,0), // right
1579                                         v3s16(0,0,-1), // front
1580                                         v3s16(0,-1,0), // bottom
1581                                         v3s16(-1,0,0), // left
1582                                 };
1583                                 for(u16 i=0; i<6; i++)
1584                                 {
1585                                         try
1586                                         {
1587
1588                                         v3s16 p2 = p0 + dirs[i];
1589                                         
1590                                         MapNode n2 = getNode(p2);
1591                                         if(content_flowing_liquid(n2.d))
1592                                         {
1593                                                 m_transforming_liquid.push_back(p2);
1594                                         }
1595                                         
1596                                         }catch(InvalidPositionException &e)
1597                                         {
1598                                         }
1599                                 }
1600                         }
1601                 }
1602                 
1603                 // Get a new one from queue if the node has turned into non-water
1604                 if(content_liquid(n0.d) == false)
1605                         continue;
1606
1607                 /*
1608                         Flow water from this node
1609                 */
1610                 v3s16 dirs_to[5] = {
1611                         v3s16(0,-1,0), // bottom
1612                         v3s16(0,0,1), // back
1613                         v3s16(1,0,0), // right
1614                         v3s16(0,0,-1), // front
1615                         v3s16(-1,0,0), // left
1616                 };
1617                 for(u16 i=0; i<5; i++)
1618                 {
1619                         try
1620                         {
1621
1622                         bool to_bottom = (i == 0);
1623
1624                         // If liquid is at lowest possible height, it's not going
1625                         // anywhere except down
1626                         if(liquid_level == 0 && to_bottom == false)
1627                                 continue;
1628                         
1629                         u8 liquid_next_level = 0;
1630                         // If going to bottom
1631                         if(to_bottom)
1632                         {
1633                                 //liquid_next_level = 7;
1634                                 if(liquid_level >= 7 - WATER_DROP_BOOST)
1635                                         liquid_next_level = 7;
1636                                 else
1637                                         liquid_next_level = liquid_level + WATER_DROP_BOOST;
1638                         }
1639                         else
1640                                 liquid_next_level = liquid_level - 1;
1641
1642                         bool n2_changed = false;
1643                         bool flowed = false;
1644                         
1645                         v3s16 p2 = p0 + dirs_to[i];
1646
1647                         MapNode n2 = getNode(p2);
1648                         //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1649
1650                         if(content_liquid(n2.d))
1651                         {
1652                                 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1653                                 // Check that the liquids are the same type
1654                                 if(n2_nonsource_c != nonsource_c)
1655                                 {
1656                                         dstream<<"WARNING: Not handling: different liquids"
1657                                                         " collide"<<std::endl;
1658                                         continue;
1659                                 }
1660                                 bool n2_is_source = !content_flowing_liquid(n2.d);
1661                                 u8 n2_liquid_level = 8;
1662                                 if(n2_is_source == false)
1663                                         n2_liquid_level = n2.param2 & 0x07;
1664                                 
1665                                 if(to_bottom)
1666                                 {
1667                                         flowed = true;
1668                                 }
1669
1670                                 if(n2_is_source)
1671                                 {
1672                                         // Just flow into the source, nothing changes.
1673                                         // n2_changed is not set because destination didn't change
1674                                         flowed = true;
1675                                 }
1676                                 else
1677                                 {
1678                                         if(liquid_next_level > liquid_level)
1679                                         {
1680                                                 n2.param2 = liquid_next_level;
1681                                                 setNode(p2, n2);
1682
1683                                                 n2_changed = true;
1684                                                 flowed = true;
1685                                         }
1686                                 }
1687                         }
1688                         else if(n2.d == CONTENT_AIR)
1689                         {
1690                                 n2.d = nonsource_c;
1691                                 n2.param2 = liquid_next_level;
1692                                 setNode(p2, n2);
1693                                 
1694                                 n2_changed = true;
1695                                 flowed = true;
1696                         }
1697                         
1698                         //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1699
1700                         if(n2_changed)
1701                         {
1702                                 m_transforming_liquid.push_back(p2);
1703                                 
1704                                 v3s16 blockpos = getNodeBlockPos(p2);
1705                                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1706                                 if(block != NULL)
1707                                         modified_blocks.insert(blockpos, block);
1708                         }
1709                         
1710                         // If n2_changed to bottom, don't flow anywhere else
1711                         if(to_bottom && flowed && !is_source)
1712                                 break;
1713                                 
1714                         }catch(InvalidPositionException &e)
1715                         {
1716                         }
1717                 }
1718
1719                 loopcount++;
1720                 //if(loopcount >= 100000)
1721                 if(loopcount >= initial_size * 1)
1722                         break;
1723         }
1724         //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1725 }
1726
1727 /*
1728         ServerMap
1729 */
1730
1731 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1732         Map(dout_server),
1733         m_heightmap(NULL)
1734 {
1735         
1736         //m_chunksize = 64;
1737         //m_chunksize = 16;
1738         m_chunksize = 8;
1739         //m_chunksize = 4;
1740         //m_chunksize = 2;
1741
1742         /*
1743                 Experimental and debug stuff
1744         */
1745         
1746         {
1747                 dstream<<"Generating map point attribute lists"<<std::endl;
1748                 
1749                 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1750                 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1751                 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1752                 //PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1753                 //PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1754
1755 #if 0
1756                 /*
1757                         NOTE: BEWARE: Too big amount of these will make map generation
1758                         slow. Especially those that are read by every block emerge.
1759                         
1760                         Fetch times:
1761                         1000 points: 2-3ms
1762                         5000 points: 15ms
1763                         15000 points: 40ms
1764                 */
1765                 
1766                 for(u32 i=0; i<500; i++)
1767                 {
1768                         /*u32 lim = MAP_GENERATION_LIMIT;
1769                         if(i < 400)
1770                                 lim = 2000;*/
1771
1772                         u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1773
1774                         v3s16 p(
1775                                 -lim + myrand()%(lim*2),
1776                                 0,
1777                                 -lim + myrand()%(lim*2)
1778                         );
1779                         /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1780                         plants_amount = pow(plants_amount, 5);
1781                         list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1782                         
1783                         float plants_amount = 0;
1784                         if(myrand()%4 == 0)
1785                         {
1786                                 plants_amount = 1.5;
1787                         }
1788                         else if(myrand()%4 == 0)
1789                         {
1790                                 plants_amount = 0.5;
1791                         }
1792                         else if(myrand()%2 == 0)
1793                         {
1794                                 plants_amount = 0.03;
1795                         }
1796                         else
1797                         {
1798                                 plants_amount = 0.0;
1799                         }
1800
1801
1802                         list_plants_amount->addPoint(p, Attribute(plants_amount));
1803                 }
1804
1805                 for(u32 i=0; i<500; i++)
1806                 {
1807                         /*u32 lim = MAP_GENERATION_LIMIT;
1808                         if(i < 400)
1809                                 lim = 2000;*/
1810
1811                         u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1812
1813                         v3s16 p(
1814                                 -lim + myrand()%(lim*2),
1815                                 0,
1816                                 -lim + myrand()%(lim*2)
1817                         );
1818
1819                         float caves_amount = 0;
1820                         if(myrand()%5 == 0)
1821                         {
1822                                 caves_amount = 1.0;
1823                         }
1824                         else if(myrand()%3 == 0)
1825                         {
1826                                 caves_amount = 0.3;
1827                         }
1828                         else
1829                         {
1830                                 caves_amount = 0.05;
1831                         }
1832
1833                         list_caves_amount->addPoint(p, Attribute(caves_amount));
1834                 }
1835                 
1836                 for(u32 i=0; i<500; i++)
1837                 {
1838                         /*u32 lim = MAP_GENERATION_LIMIT;
1839                         if(i < 400)
1840                                 lim = 2000;*/
1841
1842                         u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500;
1843
1844                         v3s16 p(
1845                                 -lim + (myrand()%(lim*2)),
1846                                 0,
1847                                 -lim + (myrand()%(lim*2))
1848                         );
1849                         
1850                         /*s32 bh_i = (myrand()%200) - 50;
1851                         float baseheight = (float)bh_i;
1852                         
1853                         float m = 100.;
1854                         float e = 3.;
1855                         float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1856                         randmax = pow(randmax, e);
1857
1858                         //float randmax = (float)(myrand()%60);
1859                         float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1860
1861                         float baseheight = 0;
1862                         float randmax = 0;
1863                         float randfactor = 0;
1864
1865                         /*if(myrand()%5 == 0)
1866                         {
1867                                 baseheight = 100;
1868                                 randmax = 50;
1869                                 randfactor = 0.63;
1870                         }
1871                         else if(myrand()%6 == 0)
1872                         {
1873                                 baseheight = 200;
1874                                 randmax = 100;
1875                                 randfactor = 0.66;
1876                         }
1877                         else if(myrand()%4 == 0)
1878                         {
1879                                 baseheight = -3;
1880                                 randmax = 30;
1881                                 randfactor = 0.7;
1882                         }
1883                         else if(myrand()%3 == 0)
1884                         {
1885                                 baseheight = 0;
1886                                 randmax = 30;
1887                                 randfactor = 0.63;
1888                         }
1889                         else
1890                         {
1891                                 baseheight = -3;
1892                                 randmax = 20;
1893                                 randfactor = 0.5;
1894                         }*/
1895                         
1896                         if(myrand()%3 < 2)
1897                         {
1898                                 baseheight = 10;
1899                                 randmax = 30;
1900                                 randfactor = 0.7;
1901                         }
1902                         else
1903                         {
1904                                 baseheight = 0;
1905                                 randmax = 15;
1906                                 randfactor = 0.63;
1907                         }
1908
1909                         list_baseheight->addPoint(p, Attribute(baseheight));
1910                         list_randmax->addPoint(p, Attribute(randmax));
1911                         list_randfactor->addPoint(p, Attribute(randfactor));
1912                 }
1913 #endif
1914                 
1915                 // Add only one entry
1916                 list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1917                 list_randmax->addPoint(v3s16(0,0,0), Attribute(30));
1918                 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45));
1919
1920                 // Easy spawn point
1921                 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1922                 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1923                 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1924         }
1925         
1926         /*
1927                 Try to load map; if not found, create a new one.
1928         */
1929
1930         m_savedir = savedir;
1931         m_map_saving_enabled = false;
1932         
1933         try
1934         {
1935                 // If directory exists, check contents and load if possible
1936                 if(fs::PathExists(m_savedir))
1937                 {
1938                         // If directory is empty, it is safe to save into it.
1939                         if(fs::GetDirListing(m_savedir).size() == 0)
1940                         {
1941                                 dstream<<DTIME<<"Server: Empty save directory is valid."
1942                                                 <<std::endl;
1943                                 m_map_saving_enabled = true;
1944                         }
1945                         else
1946                         {
1947                                 // Load master heightmap
1948                                 loadMasterHeightmap();
1949                                 
1950                                 // Load sector (0,0) and throw and exception on fail
1951                                 if(loadSectorFull(v2s16(0,0)) == false)
1952                                         throw LoadError("Failed to load sector (0,0)");
1953
1954                                 dstream<<DTIME<<"Server: Successfully loaded master "
1955                                                 "heightmap and sector (0,0) from "<<savedir<<
1956                                                 ", assuming valid save directory."
1957                                                 <<std::endl;
1958
1959                                 m_map_saving_enabled = true;
1960                                 // Map loaded, not creating new one
1961                                 return;
1962                         }
1963                 }
1964                 // If directory doesn't exist, it is safe to save to it
1965                 else{
1966                         m_map_saving_enabled = true;
1967                 }
1968         }
1969         catch(std::exception &e)
1970         {
1971                 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1972                                 <<", exception: "<<e.what()<<std::endl;
1973                 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1974                 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1975         }
1976
1977         dstream<<DTIME<<"Initializing new map."<<std::endl;
1978         
1979         // Create master heightmap
1980         m_heightmap = new UnlimitedHeightmap
1981                         (32, &m_padb);
1982         
1983         // Set map parameters
1984         m_params = mp;
1985         
1986         // Create zero sector
1987         emergeSector(v2s16(0,0));
1988
1989         // Initially write whole map
1990         save(false);
1991 }
1992
1993 ServerMap::~ServerMap()
1994 {
1995         try
1996         {
1997                 if(m_map_saving_enabled)
1998                 {
1999                         //save(false);
2000                         // Save only changed parts
2001                         save(true);
2002                         dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2003                 }
2004                 else
2005                 {
2006                         dstream<<DTIME<<"Server: map not saved"<<std::endl;
2007                 }
2008         }
2009         catch(std::exception &e)
2010         {
2011                 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2012                                 <<", exception: "<<e.what()<<std::endl;
2013         }
2014         
2015         if(m_heightmap != NULL)
2016                 delete m_heightmap;
2017         
2018         /*
2019                 Free all MapChunks
2020         */
2021         core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2022         for(; i.atEnd() == false; i++)
2023         {
2024                 MapChunk *chunk = i.getNode()->getValue();
2025                 delete chunk;
2026         }
2027 }
2028
2029 /*
2030         Some helper functions
2031 */
2032
2033 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
2034 {
2035         v3s16 em = vmanip.m_area.getExtent();
2036         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2037         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2038         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2039         s16 y;
2040         for(y=y_nodes_max; y>=y_nodes_min; y--)
2041         {
2042                 MapNode &n = vmanip.m_data[i];
2043                 if(content_walkable(n.d))
2044                         break;
2045                         
2046                 vmanip.m_area.add_y(em, i, -1);
2047         }
2048         if(y >= y_nodes_min)
2049                 return y;
2050         else
2051                 return y_nodes_min;
2052 }
2053
2054 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2055 {
2056         MapNode treenode(CONTENT_TREE);
2057         MapNode leavesnode(CONTENT_LEAVES);
2058
2059         s16 trunk_h = myrand_range(2, 6);
2060         v3s16 p1 = p0;
2061         for(s16 ii=0; ii<trunk_h; ii++)
2062         {
2063                 if(vmanip.m_area.contains(p1))
2064                         vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2065                 p1.Y++;
2066         }
2067         
2068         // p1 is now the last piece of the trunk
2069         p1.Y -= 1;
2070
2071         VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2072         SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2073         for(s32 i=0; i<leaves_a.getVolume(); i++)
2074                 leaves_d[i] = 0;
2075         
2076         // Force leaves at near the end of the trunk
2077         {
2078                 s16 d = 1;
2079                 for(s16 z=-d; z<=d; z++)
2080                 for(s16 y=-d; y<=d; y++)
2081                 for(s16 x=-d; x<=d; x++)
2082                 {
2083                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2084                 }
2085         }
2086         
2087         // Add leaves randomly
2088         for(u32 iii=0; iii<7; iii++)
2089         {
2090                 s16 d = 1;
2091
2092                 v3s16 p(
2093                         myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2094                         myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2095                         myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2096                 );
2097                 
2098                 for(s16 z=0; z<=d; z++)
2099                 for(s16 y=0; y<=d; y++)
2100                 for(s16 x=0; x<=d; x++)
2101                 {
2102                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2103                 }
2104         }
2105         
2106         // Blit leaves to vmanip
2107         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2108         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2109         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2110         {
2111                 v3s16 p(x,y,z);
2112                 p += p1;
2113                 if(vmanip.m_area.contains(p) == false)
2114                         continue;
2115                 u32 vi = vmanip.m_area.index(p);
2116                 if(vmanip.m_data[vi].d != CONTENT_AIR)
2117                         continue;
2118                 u32 i = leaves_a.index(x,y,z);
2119                 if(leaves_d[i] == 1)
2120                         vmanip.m_data[vi] = leavesnode;
2121         }
2122 }
2123
2124 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
2125                 core::map<v3s16, MapBlock*> &changed_blocks)
2126 {
2127         /*
2128                 Don't generate if already fully generated
2129         */
2130         {
2131                 MapChunk *chunk = getChunk(chunkpos);
2132                 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
2133                 {
2134                         dstream<<"generateChunkRaw(): Chunk "
2135                                         <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2136                                         <<" already generated"<<std::endl;
2137                         return chunk;
2138                 }
2139         }
2140
2141         dstream<<"generateChunkRaw(): Generating chunk "
2142                         <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
2143                         <<std::endl;
2144         
2145         TimeTaker timer("generateChunkRaw()");
2146         
2147         // The distance how far into the neighbors the generator is allowed to go
2148         s16 max_spread_amount_sectors = 2;
2149         assert(max_spread_amount_sectors <= m_chunksize);
2150         s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
2151         // Minimum amount of space left on sides for mud to fall in
2152         s16 min_mud_fall_space = 2;
2153         // Maximum diameter of stone obstacles in X and Z
2154         s16 stone_obstacle_max_size = (max_spread_amount-min_mud_fall_space)*2;
2155         assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space);
2156         
2157         s16 y_blocks_min = -4;
2158         s16 y_blocks_max = 3;
2159         s16 h_blocks = y_blocks_max - y_blocks_min + 1;
2160         s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE;
2161         s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2162
2163         v2s16 sectorpos_base = chunk_to_sector(chunkpos);
2164         s16 sectorpos_base_size = m_chunksize;
2165
2166         /*v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1));
2167         s16 sectorpos_bigbase_size = m_chunksize * 3;*/
2168         v2s16 sectorpos_bigbase =
2169                         sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
2170         s16 sectorpos_bigbase_size =
2171                         sectorpos_base_size + 2 * max_spread_amount_sectors;
2172
2173         v3s16 bigarea_blocks_min(
2174                 sectorpos_bigbase.X,
2175                 y_blocks_min,
2176                 sectorpos_bigbase.Y
2177         );
2178
2179         v3s16 bigarea_blocks_max(
2180                 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
2181                 y_blocks_max,
2182                 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
2183         );
2184         
2185         // Relative values to control amount of stuff in one chunk
2186         u32 relative_area = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2187                         *(u32)sectorpos_base_size*MAP_BLOCKSIZE;
2188         u32 relative_volume = (u32)sectorpos_base_size*MAP_BLOCKSIZE
2189                         *(u32)sectorpos_base_size*MAP_BLOCKSIZE
2190                         *(u32)h_blocks*MAP_BLOCKSIZE;
2191                 
2192         /*
2193                 Create the whole area of this and the neighboring chunks
2194         */
2195         {
2196                 TimeTaker timer("generateChunkRaw() create area");
2197                 
2198                 for(s16 x=0; x<sectorpos_bigbase_size; x++)
2199                 for(s16 z=0; z<sectorpos_bigbase_size; z++)
2200                 {
2201                         v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
2202                         ServerMapSector *sector = createSector(sectorpos);
2203                         assert(sector);
2204
2205                         for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
2206                         {
2207                                 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
2208                                 MapBlock *block = createBlock(blockpos);
2209
2210                                 // Lighting won't be calculated
2211                                 //block->setLightingExpired(true);
2212                                 // Lighting will be calculated
2213                                 block->setLightingExpired(false);
2214
2215                                 /*
2216                                         Block gets sunlight if this is true.
2217
2218                                         This should be set to true when the top side of a block
2219                                         is completely exposed to the sky.
2220
2221                                         Actually this doesn't matter now because the
2222                                         initial lighting is done here.
2223                                 */
2224                                 block->setIsUnderground(y != y_blocks_max);
2225                         }
2226                 }
2227         }
2228
2229         /*
2230                 Now we have a big empty area.
2231
2232                 Make a ManualMapVoxelManipulator that contains this and the
2233                 neighboring chunks
2234         */
2235
2236         ManualMapVoxelManipulator vmanip(this);
2237         // Add the area we just generated
2238         {
2239                 TimeTaker timer("generateChunkRaw() initialEmerge");
2240                 vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2241         }
2242
2243         TimeTaker timer_generate("generateChunkRaw() generate");
2244
2245         /*
2246                 Generate general ground level to full area
2247         */
2248         
2249         {
2250         // 22ms @cs=8
2251         //TimeTaker timer1("ground level");
2252
2253         for(s16 x=0; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2254         for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2255         {
2256                 // Node position
2257                 v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2258                 
2259                 /*
2260                         Skip of already generated
2261                 */
2262                 {
2263                         v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2264                         if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
2265                                 continue;
2266                 }
2267
2268                 // Ground height at this point
2269                 float surface_y_f = 0.0;
2270                 /*
2271                         A hack to get the ground height from the sector.
2272                         Do this better.
2273                 */
2274                 {
2275                         v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE);
2276                         v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE;
2277                         MapSector *sector = getSectorNoGenerate(sectorpos);
2278                         assert(sector);
2279                         float h = sector->getGroundHeight(sector_relpos);
2280                         if(h > GROUNDHEIGHT_VALID_MINVALUE)
2281                                 surface_y_f = h;
2282                         else
2283                                 dstream<<"WARNING: "<<__FUNCTION_NAME
2284                                 <<": sector->getGroundHeight returned bad height"<<std::endl;
2285                 }
2286                 // Convert to integer
2287                 s16 surface_y = (s16)surface_y_f;
2288
2289                 /*
2290                         Fill ground with stone
2291                 */
2292                 {
2293                         // Use fast index incrementing
2294                         v3s16 em = vmanip.m_area.getExtent();
2295                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2296                         for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2297                         {
2298                                 vmanip.m_data[i].d = CONTENT_STONE;
2299
2300                                 vmanip.m_area.add_y(em, i, 1);
2301                         }
2302                 }
2303         }
2304         
2305         }//timer1
2306
2307         // This is set during the next operation.
2308         // Maximum height of the stone surface and obstacles.
2309         // This is used to disable dungeon generation from going too high.
2310         s16 stone_surface_max_y = 0;
2311
2312         {
2313         // 8ms @cs=8
2314         //TimeTaker timer1("stone obstacles");
2315
2316         /*
2317                 Add some random stone obstacles
2318         */
2319
2320         for(u32 ri=0; ri<10; ri++)
2321         {
2322                 // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2
2323                 v3s16 ob_size(
2324                         myrand_range(5, stone_obstacle_max_size),
2325                         myrand_range(0, 23),
2326                         myrand_range(5, stone_obstacle_max_size)
2327                 );
2328                 v2s16 ob_place(
2329                         myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1),
2330                         myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1)
2331                 );
2332                 
2333                 // Minimum space left on top of the obstacle
2334                 s16 min_head_space = 10;
2335                 
2336                 for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++)
2337                 for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++)
2338                 {
2339                         // Node position in 2d
2340                         v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z);
2341                         
2342                         // Find stone ground level
2343                         // (ignore everything else than mud in already generated chunks)
2344                         // and mud amount over the stone level
2345                         s16 surface_y = 0;
2346                         s16 mud_amount = 0;
2347                         {
2348                                 v3s16 em = vmanip.m_area.getExtent();
2349                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2350                                 s16 y;
2351                                 // Go to ground level
2352                                 for(y=y_nodes_max; y>=y_nodes_min; y--)
2353                                 {
2354                                         MapNode &n = vmanip.m_data[i];
2355                                         /*if(content_walkable(n.d)
2356                                                         && n.d != CONTENT_MUD
2357                                                         && n.d != CONTENT_GRASS)
2358                                                 break;*/
2359                                         if(n.d == CONTENT_STONE)
2360                                                 break;
2361
2362                                         if(n.d == CONTENT_MUD || n.d == CONTENT_GRASS)
2363                                                 mud_amount++;
2364                                                 
2365                                         vmanip.m_area.add_y(em, i, -1);
2366                                 }
2367                                 if(y >= y_nodes_min)
2368                                         surface_y = y;
2369                                 else
2370                                         surface_y = y_nodes_min;
2371                         }
2372
2373
2374                         /*
2375                                 Add stone on ground
2376                         */
2377                         {
2378                                 v3s16 em = vmanip.m_area.getExtent();
2379                                 s16 y_start = surface_y+1;
2380                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2381                                 s16 y;
2382                                 // Add stone
2383                                 s16 count = 0;
2384                                 for(y=y_start; y<=y_nodes_max - min_head_space; y++)
2385                                 {
2386                                         MapNode &n = vmanip.m_data[i];
2387                                         n.d = CONTENT_STONE;
2388
2389                                         if(y > stone_surface_max_y)
2390                                                 stone_surface_max_y = y;
2391
2392                                         count++;
2393                                         if(count >= ob_size.Y)
2394                                                 break;
2395                                                 
2396                                         vmanip.m_area.add_y(em, i, 1);
2397                                 }
2398                                 // Add mud
2399                                 count = 0;
2400                                 for(; y<=y_nodes_max; y++)
2401                                 {
2402                                         MapNode &n = vmanip.m_data[i];
2403                                         n.d = CONTENT_MUD;
2404                                         count++;
2405                                         if(count >= mud_amount)
2406                                                 break;
2407                                                 
2408                                         vmanip.m_area.add_y(em, i, 1);
2409                                 }
2410                         }
2411
2412                 }
2413         }
2414
2415         }//timer1
2416         {
2417         // 24ms @cs=8
2418         //TimeTaker timer1("dungeons");
2419
2420         /*
2421                 Make dungeons
2422         */
2423         u32 dungeons_count = relative_volume/200000;
2424         for(u32 jj=0; jj<dungeons_count; jj++)
2425         {
2426                 s16 min_tunnel_diameter = 1;
2427                 s16 max_tunnel_diameter = 5;
2428                 u16 tunnel_routepoints = 15;
2429                 
2430                 bool bruise_surface = (jj < dungeons_count / 3);
2431
2432                 if(bruise_surface)
2433                 {
2434                         min_tunnel_diameter = 5;
2435                         max_tunnel_diameter = 10;
2436                         tunnel_routepoints = 10;
2437                 }
2438
2439                 // Allowed route area size in nodes
2440                 v3s16 ar(
2441                         sectorpos_base_size*MAP_BLOCKSIZE,
2442                         h_blocks*MAP_BLOCKSIZE,
2443                         sectorpos_base_size*MAP_BLOCKSIZE
2444                 );
2445
2446                 // Area starting point in nodes
2447                 v3s16 of(
2448                         sectorpos_base.X*MAP_BLOCKSIZE,
2449                         y_blocks_min*MAP_BLOCKSIZE,
2450                         sectorpos_base.Y*MAP_BLOCKSIZE
2451                 );
2452
2453                 // Allow a bit more
2454                 //(this should be more than the maximum radius of the tunnel)
2455                 s16 insure = 5;
2456                 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
2457                 ar += v3s16(1,0,1) * more * 2;
2458                 of -= v3s16(1,0,1) * more;
2459                 
2460                 s16 route_y_min = 0;
2461                 //s16 route_y_max = ar.Y-1;
2462                 s16 route_y_max = stone_surface_max_y - of.Y;
2463
2464                 if(bruise_surface)
2465                 {
2466                         // Minimum is at y=0
2467                         route_y_min = -of.Y - 0;
2468                         route_y_min = rangelim(route_y_min, 0, route_y_max);
2469                 }
2470
2471                 /*dstream<<"route_y_min = "<<route_y_min
2472                                 <<", route_y_max = "<<route_y_max<<std::endl;*/
2473
2474                 // Randomize starting position
2475                 v3f orp(
2476                         (float)(myrand()%ar.X)+0.5,
2477                         (float)(myrand_range(route_y_min, route_y_max))+0.5,
2478                         (float)(myrand()%ar.Z)+0.5
2479                 );
2480
2481                 MapNode airnode(CONTENT_AIR);
2482                 
2483                 /*
2484                         Generate some tunnel starting from orp
2485                 */
2486                 
2487                 for(u16 j=0; j<tunnel_routepoints; j++)
2488                 {
2489                         v3s16 maxlen(20, 10, 20);
2490
2491                         if(bruise_surface)
2492                         {
2493                                 maxlen = v3s16(60,60,60);
2494                         }
2495
2496                         v3f vec(
2497                                 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2498                                 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2499                                 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2500                         );
2501                         v3f rp = orp + vec;
2502                         if(rp.X < 0)
2503                                 rp.X = 0;
2504                         else if(rp.X >= ar.X)
2505                                 rp.X = ar.X-1;
2506                         if(rp.Y < route_y_min)
2507                                 rp.Y = route_y_min;
2508                         else if(rp.Y >= route_y_max)
2509                                 rp.Y = route_y_max-1;
2510                         if(rp.Z < 0)
2511                                 rp.Z = 0;
2512                         else if(rp.Z >= ar.Z)
2513                                 rp.Z = ar.Z-1;
2514                         vec = rp - orp;
2515
2516                         // Randomize size
2517                         s16 min_d = min_tunnel_diameter;
2518                         s16 max_d = max_tunnel_diameter;
2519                         s16 rs = myrand_range(min_d, max_d);
2520                         
2521                         for(float f=0; f<1.0; f+=1.0/vec.getLength())
2522                         {
2523                                 v3f fp = orp + vec * f;
2524                                 v3s16 cp(fp.X, fp.Y, fp.Z);
2525
2526                                 s16 d0 = -rs/2;
2527                                 s16 d1 = d0 + rs - 1;
2528                                 for(s16 z0=d0; z0<=d1; z0++)
2529                                 {
2530                                         s16 si = rs - abs(z0);
2531                                         for(s16 x0=-si; x0<=si-1; x0++)
2532                                         {
2533                                                 s16 si2 = rs - abs(x0);
2534                                                 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2535                                                 {
2536                                                         s16 z = cp.Z + z0;
2537                                                         s16 y = cp.Y + y0;
2538                                                         s16 x = cp.X + x0;
2539                                                         v3s16 p(x,y,z);
2540                                                         /*if(isInArea(p, ar) == false)
2541                                                                 continue;*/
2542                                                         // Check only height
2543                                                         if(y < 0 || y >= ar.Y)
2544                                                                 continue;
2545                                                         p += of;
2546                                                         
2547                                                         //assert(vmanip.m_area.contains(p));
2548                                                         if(vmanip.m_area.contains(p) == false)
2549                                                         {
2550                                                                 dstream<<"WARNING: "<<__FUNCTION_NAME
2551                                                                                 <<":"<<__LINE__<<": "
2552                                                                                 <<"point not in area"
2553                                                                                 <<std::endl;
2554                                                                 continue;
2555                                                         }
2556                                                         
2557                                                         // Just set it to air, it will be changed to
2558                                                         // water afterwards
2559                                                         u32 i = vmanip.m_area.index(p);
2560                                                         vmanip.m_data[i] = airnode;
2561                                                 }
2562                                         }
2563                                 }
2564                         }
2565
2566                         orp = rp;
2567                 }
2568         
2569         }
2570
2571         }//timer1
2572         {
2573         // 46ms @cs=8
2574         //TimeTaker timer1("ore veins");
2575
2576         /*
2577                 Make ore veins
2578         */
2579         for(u32 jj=0; jj<relative_volume/524; jj++)
2580         {
2581                 s16 max_vein_diameter = 3;
2582
2583                 // Allowed route area size in nodes
2584                 v3s16 ar(
2585                         sectorpos_base_size*MAP_BLOCKSIZE,
2586                         h_blocks*MAP_BLOCKSIZE,
2587                         sectorpos_base_size*MAP_BLOCKSIZE
2588                 );
2589
2590                 // Area starting point in nodes
2591                 v3s16 of(
2592                         sectorpos_base.X*MAP_BLOCKSIZE,
2593                         y_blocks_min*MAP_BLOCKSIZE,
2594                         sectorpos_base.Y*MAP_BLOCKSIZE
2595                 );
2596
2597                 // Allow a bit more
2598                 //(this should be more than the maximum radius of the tunnel)
2599                 s16 insure = 3;
2600                 s16 more = max_spread_amount - max_vein_diameter/2 - insure;
2601                 ar += v3s16(1,0,1) * more * 2;
2602                 of -= v3s16(1,0,1) * more;
2603                 
2604                 // Randomize starting position
2605                 v3f orp(
2606                         (float)(myrand()%ar.X)+0.5,
2607                         (float)(myrand()%ar.Y)+0.5,
2608                         (float)(myrand()%ar.Z)+0.5
2609                 );
2610
2611                 // Randomize mineral
2612                 u8 mineral = myrand_range(1, MINERAL_COUNT-1);
2613
2614                 /*
2615                         Generate some vein starting from orp
2616                 */
2617
2618                 for(u16 j=0; j<2; j++)
2619                 {
2620                         /*v3f rp(
2621                                 (float)(myrand()%ar.X)+0.5,
2622                                 (float)(myrand()%ar.Y)+0.5,
2623                                 (float)(myrand()%ar.Z)+0.5
2624                         );
2625                         v3f vec = rp - orp;*/
2626                         
2627                         v3s16 maxlen(10, 10, 10);
2628                         v3f vec(
2629                                 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2630                                 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2631                                 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2632                         );
2633                         v3f rp = orp + vec;
2634                         if(rp.X < 0)
2635                                 rp.X = 0;
2636                         else if(rp.X >= ar.X)
2637                                 rp.X = ar.X;
2638                         if(rp.Y < 0)
2639                                 rp.Y = 0;
2640                         else if(rp.Y >= ar.Y)
2641                                 rp.Y = ar.Y;
2642                         if(rp.Z < 0)
2643                                 rp.Z = 0;
2644                         else if(rp.Z >= ar.Z)
2645                                 rp.Z = ar.Z;
2646                         vec = rp - orp;
2647
2648                         // Randomize size
2649                         s16 min_d = 0;
2650                         s16 max_d = max_vein_diameter;
2651                         s16 rs = myrand_range(min_d, max_d);
2652                         
2653                         for(float f=0; f<1.0; f+=1.0/vec.getLength())
2654                         {
2655                                 v3f fp = orp + vec * f;
2656                                 v3s16 cp(fp.X, fp.Y, fp.Z);
2657                                 s16 d0 = -rs/2;
2658                                 s16 d1 = d0 + rs - 1;
2659                                 for(s16 z0=d0; z0<=d1; z0++)
2660                                 {
2661                                         s16 si = rs - abs(z0);
2662                                         for(s16 x0=-si; x0<=si-1; x0++)
2663                                         {
2664                                                 s16 si2 = rs - abs(x0);
2665                                                 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2666                                                 {
2667                                                         // Don't put mineral to every place
2668                                                         if(myrand()%5 != 0)
2669                                                                 continue;
2670
2671                                                         s16 z = cp.Z + z0;
2672                                                         s16 y = cp.Y + y0;
2673                                                         s16 x = cp.X + x0;
2674                                                         v3s16 p(x,y,z);
2675                                                         /*if(isInArea(p, ar) == false)
2676                                                                 continue;*/
2677                                                         // Check only height
2678                                                         if(y < 0 || y >= ar.Y)
2679                                                                 continue;
2680                                                         p += of;
2681                                                         
2682                                                         assert(vmanip.m_area.contains(p));
2683                                                         
2684                                                         // Just set it to air, it will be changed to
2685                                                         // water afterwards
2686                                                         u32 i = vmanip.m_area.index(p);
2687                                                         MapNode *n = &vmanip.m_data[i];
2688                                                         if(n->d == CONTENT_STONE)
2689                                                                 n->param = mineral;
2690                                                 }
2691                                         }
2692                                 }
2693                         }
2694
2695                         orp = rp;
2696                 }
2697         
2698         }
2699
2700         }//timer1
2701         {
2702         // 15ms @cs=8
2703         //TimeTaker timer1("add mud");
2704
2705         /*
2706                 Add mud to the central chunk
2707         */
2708         
2709         for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
2710         for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)
2711         {
2712                 // Node position in 2d
2713                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2714                 
2715                 // Find ground level
2716                 s16 surface_y = find_ground_level(vmanip, p2d);
2717
2718                 /*
2719                         Add mud on ground
2720                 */
2721                 {
2722                         s16 mudcount = 0;
2723                         v3s16 em = vmanip.m_area.getExtent();
2724                         s16 y_start = surface_y+1;
2725                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2726                         for(s16 y=y_start; y<=y_nodes_max; y++)
2727                         {
2728                                 MapNode &n = vmanip.m_data[i];
2729                                 n.d = CONTENT_MUD;
2730                                 mudcount++;
2731                                 if(mudcount >= 3)
2732                                         break;
2733                                         
2734                                 vmanip.m_area.add_y(em, i, 1);
2735                         }
2736                 }
2737
2738         }
2739
2740         }//timer1
2741         {
2742         // 179ms @cs=8
2743         //TimeTaker timer1("flow mud");
2744
2745         /*
2746                 Flow mud away from steep edges
2747         */
2748
2749         // Iterate a few times
2750         for(s16 k=0; k<4; k++)
2751         {
2752
2753         for(s16 x=0-max_spread_amount+1;
2754                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2755                         x++)
2756         for(s16 z=0-max_spread_amount+1;
2757                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
2758                         z++)
2759         {
2760                 // Node position in 2d
2761                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2762                 
2763                 v3s16 em = vmanip.m_area.getExtent();
2764                 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2765                 s16 y;
2766                 // Go to ground level
2767                 for(y=y_nodes_max; y>=y_nodes_min; y--)
2768                 {
2769                         MapNode &n = vmanip.m_data[i];
2770                         //if(n.d != CONTENT_AIR)
2771                         if(content_walkable(n.d))
2772                                 break;
2773                                 
2774                         vmanip.m_area.add_y(em, i, -1);
2775                 }
2776
2777                 // If not mud, do nothing to it
2778                 MapNode *n = &vmanip.m_data[i];
2779                 if(n->d != CONTENT_MUD)
2780                         continue;
2781
2782                 v3s16 dirs4[4] = {
2783                         v3s16(0,0,1), // back
2784                         v3s16(1,0,0), // right
2785                         v3s16(0,0,-1), // front
2786                         v3s16(-1,0,0), // left
2787                 };
2788
2789                 // Drop mud on side
2790                 
2791                 for(u32 di=0; di<4; di++)
2792                 {
2793                         v3s16 dirp = dirs4[di];
2794                         u32 i2 = i;
2795                         // Check that side is air
2796                         vmanip.m_area.add_p(em, i2, dirp);
2797                         MapNode *n2 = &vmanip.m_data[i2];
2798                         if(content_walkable(n2->d))
2799                                 continue;
2800                         // Check that under side is air
2801                         vmanip.m_area.add_y(em, i2, -1);
2802                         n2 = &vmanip.m_data[i2];
2803                         if(content_walkable(n2->d))
2804                                 continue;
2805                         // Loop further down until not air
2806                         do{
2807                                 vmanip.m_area.add_y(em, i2, -1);
2808                                 n2 = &vmanip.m_data[i2];
2809                         }while(content_walkable(n2->d) == false);
2810                         // Loop one up so that we're in air
2811                         vmanip.m_area.add_y(em, i2, 1);
2812                         n2 = &vmanip.m_data[i2];
2813
2814                         // Move mud to new place
2815                         *n2 = *n;
2816                         // Set old place to be air
2817                         *n = MapNode(CONTENT_AIR);
2818
2819                         #if 0
2820                         // Switch mud and other and change mud source to air
2821                         //MapNode tempnode = *n2;
2822                         *n2 = *n;
2823                         //*n = tempnode;
2824                         // Force old mud position to be air
2825                         n->d = CONTENT_AIR;
2826                         #endif
2827
2828                         // Done
2829                         break;
2830                 }
2831         }
2832         
2833         }
2834
2835         }//timer1
2836         {
2837         // 50ms @cs=8
2838         //TimeTaker timer1("add water");
2839
2840         /*
2841                 Add water to the central chunk (and a bit more)
2842         */
2843         
2844         for(s16 x=0-max_spread_amount;
2845                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2846                         x++)
2847         for(s16 z=0-max_spread_amount;
2848                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2849                         z++)
2850         {
2851                 // Node position in 2d
2852                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2853                 
2854                 // Find ground level
2855                 //s16 surface_y = find_ground_level(vmanip, p2d);
2856
2857                 /*
2858                         If ground level is over water level, skip.
2859                         NOTE: This leaves caves near water without water,
2860                         which looks especially crappy when the nearby water
2861                         won't start flowing either for some reason
2862                 */
2863                 /*if(surface_y > WATER_LEVEL)
2864                         continue;*/
2865
2866                 /*
2867                         Add water on ground
2868                 */
2869                 {
2870                         v3s16 em = vmanip.m_area.getExtent();
2871                         s16 y_start = WATER_LEVEL;
2872                         u8 light = LIGHT_MAX;
2873                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2874                         MapNode *n = &vmanip.m_data[i];
2875                         /*
2876                                 Add first one to transforming liquid queue
2877                         */
2878                         if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2879                         {
2880                                 v3s16 p = v3s16(p2d.X, y_start, p2d.Y);
2881                                 m_transforming_liquid.push_back(p);
2882                         }
2883                         for(s16 y=y_start; y>=y_nodes_min; y--)
2884                         {
2885                                 n = &vmanip.m_data[i];
2886                                 
2887                                 // Stop when there is no water and no air
2888                                 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2889                                                 && n->d != CONTENT_WATER)
2890                                 {
2891                                         /*
2892                                                 Add bottom one to transforming liquid queue
2893                                         */
2894                                         vmanip.m_area.add_y(em, i, 1);
2895                                         n = &vmanip.m_data[i];
2896                                         if(n->d == CONTENT_WATER || n->d == CONTENT_WATERSOURCE)
2897                                         {
2898                                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2899                                                 m_transforming_liquid.push_back(p);
2900                                         }
2901
2902                                         break;
2903                                 }
2904                                 
2905                                 n->d = CONTENT_WATERSOURCE;
2906                                 n->setLight(LIGHTBANK_DAY, light);
2907
2908                                 /*// Add to transforming liquid queue (in case it'd
2909                                 // start flowing)
2910                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2911                                 m_transforming_liquid.push_back(p);*/
2912                                 
2913                                 // Next one
2914                                 vmanip.m_area.add_y(em, i, -1);
2915                                 if(light > 0)
2916                                         light--;
2917                         }
2918                 }
2919
2920         }
2921
2922         }//timer1
2923         {
2924         // 1ms @cs=8
2925         //TimeTaker timer1("plant trees");
2926
2927         /*
2928                 Plant some trees
2929         */
2930         {
2931                 u32 tree_max = relative_area / 100;
2932                 
2933                 u32 count = myrand_range(0, tree_max);
2934                 for(u32 i=0; i<count; i++)
2935                 {
2936                         s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2937                         s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1);
2938                         x += sectorpos_base.X*MAP_BLOCKSIZE;
2939                         z += sectorpos_base.Y*MAP_BLOCKSIZE;
2940                         s16 y = find_ground_level(vmanip, v2s16(x,z));
2941                         // Don't make a tree under water level
2942                         if(y < WATER_LEVEL)
2943                                 continue;
2944                         v3s16 p(x,y+1,z);
2945                         // Make a tree
2946                         make_tree(vmanip, p);
2947                 }
2948         }
2949
2950         }//timer1
2951         {
2952         // 19ms @cs=8
2953         //TimeTaker timer1("grow grass");
2954
2955         /*
2956                 Grow grass
2957         */
2958
2959         /*for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
2960         for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
2961         for(s16 x=0-max_spread_amount;
2962                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2963                         x++)
2964         for(s16 z=0-max_spread_amount;
2965                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount;
2966                         z++)
2967         {
2968                 // Node position in 2d
2969                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2970                 
2971                 /*
2972                         Find the lowest surface to which enough light ends up
2973                         to make grass grow.
2974
2975                         Basically just wait until not air and not leaves.
2976                 */
2977                 s16 surface_y = 0;
2978                 {
2979                         v3s16 em = vmanip.m_area.getExtent();
2980                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2981                         s16 y;
2982                         // Go to ground level
2983                         for(y=y_nodes_max; y>=y_nodes_min; y--)
2984                         {
2985                                 MapNode &n = vmanip.m_data[i];
2986                                 if(n.d != CONTENT_AIR
2987                                                 && n.d != CONTENT_LEAVES)
2988                                         break;
2989                                 vmanip.m_area.add_y(em, i, -1);
2990                         }
2991                         if(y >= y_nodes_min)
2992                                 surface_y = y;
2993                         else
2994                                 surface_y = y_nodes_min;
2995                 }
2996                 
2997                 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
2998                 MapNode *n = &vmanip.m_data[i];
2999                 if(n->d == CONTENT_MUD)
3000                         n->d = CONTENT_GRASS;
3001         }
3002
3003         }//timer1
3004
3005         /*
3006                 Handle lighting
3007         */
3008
3009         core::map<v3s16, bool> light_sources;
3010
3011         {
3012         // 750ms @cs=8, can't optimize more
3013         //TimeTaker timer1("initial lighting");
3014
3015         /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++)
3016         for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3017         for(s16 x=0-max_spread_amount+1;
3018                         x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3019                         x++)
3020         for(s16 z=0-max_spread_amount+1;
3021                         z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1;
3022                         z++)
3023         {
3024                 // Node position in 2d
3025                 v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3026                 
3027                 /*
3028                         Apply initial sunlight
3029                 */
3030                 {
3031                         u8 light = LIGHT_SUN;
3032                         bool add_to_sources = false;
3033                         v3s16 em = vmanip.m_area.getExtent();
3034                         s16 y_start = y_nodes_max;
3035                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3036                         for(s16 y=y_start; y>=y_nodes_min; y--)
3037                         {
3038                                 MapNode *n = &vmanip.m_data[i];
3039
3040                                 if(light_propagates_content(n->d) == false)
3041                                 {
3042                                         light = 0;
3043                                 }
3044                                 else if(light != LIGHT_SUN
3045                                         || sunlight_propagates_content(n->d) == false)
3046                                 {
3047                                         if(light > 0)
3048                                                 light--;
3049                                 }
3050                                 
3051                                 // This doesn't take much time
3052                                 if(add_to_sources == false)
3053                                 {
3054                                         /*
3055                                                 Check sides. If side is not air or water, start
3056                                                 adding to light_sources.
3057                                         */
3058                                         v3s16 dirs4[4] = {
3059                                                 v3s16(0,0,1), // back
3060                                                 v3s16(1,0,0), // right
3061                                                 v3s16(0,0,-1), // front
3062                                                 v3s16(-1,0,0), // left
3063                                         };
3064                                         for(u32 di=0; di<4; di++)
3065                                         {
3066                                                 v3s16 dirp = dirs4[di];
3067                                                 u32 i2 = i;
3068                                                 vmanip.m_area.add_p(em, i2, dirp);
3069                                                 MapNode *n2 = &vmanip.m_data[i2];
3070                                                 if(
3071                                                         n2->d != CONTENT_AIR
3072                                                         && n2->d != CONTENT_WATERSOURCE
3073                                                         && n2->d != CONTENT_WATER
3074                                                 ){
3075                                                         add_to_sources = true;
3076                                                         break;
3077                                                 }
3078                                         }
3079                                 }
3080
3081                                 n->setLight(LIGHTBANK_DAY, light);
3082                                 n->setLight(LIGHTBANK_NIGHT, 0);
3083                                 
3084                                 // This doesn't take much time
3085                                 if(light != 0 && add_to_sources)
3086                                 {
3087                                         // Insert light source
3088                                         light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3089                                 }
3090                                 
3091                                 // Increment index by y
3092                                 vmanip.m_area.add_y(em, i, -1);
3093                         }
3094                 }
3095         }
3096
3097         }//timer1
3098
3099         // Spread light around
3100         {
3101                 TimeTaker timer("generateChunkRaw() spreadLight");
3102                 vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3103         }
3104         
3105         /*
3106                 Generation ended
3107         */
3108
3109         timer_generate.stop();
3110
3111         /*
3112                 Blit generated stuff to map
3113         */
3114         {
3115                 // 70ms @cs=8
3116                 //TimeTaker timer("generateChunkRaw() blitBackAll");
3117                 vmanip.blitBackAll(&changed_blocks);
3118         }
3119         /*
3120                 Update day/night difference cache of the MapBlocks
3121         */
3122         {
3123                 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3124                                 i.atEnd() == false; i++)
3125                 {
3126                         MapBlock *block = i.getNode()->getValue();
3127                         block->updateDayNightDiff();
3128                 }
3129         }
3130
3131         
3132         /*
3133                 Create chunk metadata
3134         */
3135
3136         for(s16 x=-1; x<=1; x++)
3137         for(s16 y=-1; y<=1; y++)
3138         {
3139                 v2s16 chunkpos0 = chunkpos + v2s16(x,y);
3140                 // Add chunk meta information
3141                 MapChunk *chunk = getChunk(chunkpos0);
3142                 if(chunk == NULL)
3143                 {
3144                         chunk = new MapChunk();
3145                         m_chunks.insert(chunkpos0, chunk);
3146                 }
3147                 //chunk->setIsVolatile(true);
3148                 if(chunk->getGenLevel() > GENERATED_PARTLY)
3149                         chunk->setGenLevel(GENERATED_PARTLY);
3150         }
3151
3152         /*
3153                 Set central chunk non-volatile and return it
3154         */
3155         MapChunk *chunk = getChunk(chunkpos);
3156         assert(chunk);
3157         // Set non-volatile
3158         //chunk->setIsVolatile(false);
3159         chunk->setGenLevel(GENERATED_FULLY);
3160         // Return it
3161         return chunk;
3162 }
3163
3164 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3165                 core::map<v3s16, MapBlock*> &changed_blocks)
3166 {
3167         dstream<<"generateChunk(): Generating chunk "
3168                         <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3169                         <<std::endl;
3170         
3171         /*for(s16 x=-1; x<=1; x++)
3172         for(s16 y=-1; y<=1; y++)*/
3173         for(s16 x=-0; x<=0; x++)
3174         for(s16 y=-0; y<=0; y++)
3175         {
3176                 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3177                 MapChunk *chunk = getChunk(chunkpos0);
3178                 // Skip if already generated
3179                 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3180                         continue;
3181                 generateChunkRaw(chunkpos0, changed_blocks);
3182         }
3183         
3184         assert(chunkNonVolatile(chunkpos1));
3185
3186         MapChunk *chunk = getChunk(chunkpos1);
3187         return chunk;
3188 }
3189
3190 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3191 {
3192         DSTACK("%s: p2d=(%d,%d)",
3193                         __FUNCTION_NAME,
3194                         p2d.X, p2d.Y);
3195         
3196         /*
3197                 Check if it exists already in memory
3198         */
3199         ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3200         if(sector != NULL)
3201                 return sector;
3202         
3203         /*
3204                 Try to load it from disk (with blocks)
3205         */
3206         if(loadSectorFull(p2d) == true)
3207         {
3208                 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3209                 if(sector == NULL)
3210                 {
3211                         dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3212                         throw InvalidPositionException("");
3213                 }
3214                 return sector;
3215         }
3216
3217         /*
3218                 If there is no master heightmap, throw.
3219         */
3220         if(m_heightmap == NULL)
3221         {
3222                 throw InvalidPositionException("createSector(): no heightmap");
3223         }
3224
3225         /*
3226                 Do not create over-limit
3227         */
3228         if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3229         || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3230         || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3231         || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3232                 throw InvalidPositionException("createSector(): pos. over limit");
3233
3234         /*
3235                 Generate blank sector
3236         */
3237         
3238         // Number of heightmaps in sector in each direction
3239         u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
3240
3241         // Heightmap side width
3242         s16 hm_d = MAP_BLOCKSIZE / hm_split;
3243
3244         sector = new ServerMapSector(this, p2d, hm_split);
3245         
3246         // Sector position on map in nodes
3247         v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3248
3249         /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
3250                         " heightmaps and objects"<<std::endl;*/
3251         
3252         /*
3253                 Generate sector heightmap
3254         */
3255
3256         v2s16 mhm_p = p2d * hm_split;
3257         /*f32 corners[4] = {
3258                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
3259                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
3260                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
3261                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
3262         };*/
3263         
3264         // Loop through sub-heightmaps
3265         for(s16 y=0; y<hm_split; y++)
3266         for(s16 x=0; x<hm_split; x++)
3267         {
3268                 v2s16 p_in_sector = v2s16(x,y);
3269                 v2s16 mhm_p = p2d * hm_split + p_in_sector;
3270                 f32 corners[4] = {
3271                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
3272                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
3273                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
3274                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
3275                 };
3276
3277                 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
3278                                 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
3279                                 <<std::endl;*/
3280
3281                 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
3282                                 mhm_p, hm_d);
3283                 sector->setHeightmap(p_in_sector, hm);
3284
3285                 //hm->generateContinued(1.0, 0.5, corners);
3286                 hm->generateContinued(0.5, 0.5, corners);
3287
3288                 //hm->print();
3289         }
3290         
3291         // Add dummy objects
3292         core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
3293         sector->setObjects(objects);
3294         
3295         /*
3296                 Insert to container
3297         */
3298         m_sectors.insert(p2d, sector);
3299         
3300         return sector;
3301 }
3302
3303 MapSector * ServerMap::emergeSector(v2s16 p2d,
3304                 core::map<v3s16, MapBlock*> &changed_blocks)
3305 {
3306         DSTACK("%s: p2d=(%d,%d)",
3307                         __FUNCTION_NAME,
3308                         p2d.X, p2d.Y);
3309         
3310         /*
3311                 Check chunk status
3312         */
3313         v2s16 chunkpos = sector_to_chunk(p2d);
3314         /*bool chunk_nonvolatile = false;
3315         MapChunk *chunk = getChunk(chunkpos);
3316         if(chunk && chunk->getIsVolatile() == false)
3317                 chunk_nonvolatile = true;*/
3318         bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3319
3320         /*
3321                 If chunk is not fully generated, generate chunk
3322         */
3323         if(chunk_nonvolatile == false)
3324         {
3325                 // Generate chunk and neighbors
3326                 generateChunk(chunkpos, changed_blocks);
3327         }
3328         
3329         /*
3330                 Return sector if it exists now
3331         */
3332         MapSector *sector = getSectorNoGenerateNoEx(p2d);
3333         if(sector != NULL)
3334                 return sector;
3335         
3336         /*
3337                 Try to load it from disk
3338         */
3339         if(loadSectorFull(p2d) == true)
3340         {
3341                 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3342                 if(sector == NULL)
3343                 {
3344                         dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3345                         throw InvalidPositionException("");
3346                 }
3347                 return sector;
3348         }
3349
3350         /*
3351                 generateChunk should have generated the sector
3352         */
3353         assert(0);
3354
3355         /*
3356                 Generate directly
3357         */
3358         //return generateSector();
3359 }
3360
3361 /*
3362         NOTE: This is not used for main map generation, only for blocks
3363         that are very high or low
3364 */
3365 MapBlock * ServerMap::generateBlock(
3366                 v3s16 p,
3367                 MapBlock *original_dummy,
3368                 ServerMapSector *sector,
3369                 core::map<v3s16, MapBlock*> &changed_blocks,
3370                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3371 )
3372 {
3373         DSTACK("%s: p=(%d,%d,%d)",
3374                         __FUNCTION_NAME,
3375                         p.X, p.Y, p.Z);
3376         
3377         /*dstream<<"generateBlock(): "
3378                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3379                         <<std::endl;*/
3380         
3381         MapBlock *block = original_dummy;
3382                         
3383         v2s16 p2d(p.X, p.Z);
3384         s16 block_y = p.Y;
3385         
3386         /*
3387                 Do not generate over-limit
3388         */
3389         if(blockpos_over_limit(p))
3390         {
3391                 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3392                 throw InvalidPositionException("generateBlock(): pos. over limit");
3393         }
3394
3395         /*
3396                 If block doesn't exist, create one.
3397                 If it exists, it is a dummy. In that case unDummify() it.
3398
3399                 NOTE: This already sets the map as the parent of the block
3400         */
3401         if(block == NULL)
3402         {
3403                 block = sector->createBlankBlockNoInsert(block_y);
3404         }
3405         else
3406         {
3407                 // Remove the block so that nobody can get a half-generated one.
3408                 sector->removeBlock(block);
3409                 // Allocate the block to contain the generated data
3410                 block->unDummify();
3411         }
3412         
3413         /*u8 water_material = CONTENT_WATER;
3414         if(g_settings.getBool("endless_water"))
3415                 water_material = CONTENT_WATERSOURCE;*/
3416         u8 water_material = CONTENT_WATERSOURCE;
3417         
3418         s32 lowest_ground_y = 32767;
3419         s32 highest_ground_y = -32768;
3420         
3421         // DEBUG
3422         //sector->printHeightmaps();
3423
3424         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3425         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3426         {
3427                 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3428
3429                 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
3430                 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
3431                 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
3432                 {
3433                         dstream<<"WARNING: Surface height not found in sector "
3434                                         "for block that is being emerged"<<std::endl;
3435                         surface_y_f = 0.0;
3436                 }
3437
3438                 s16 surface_y = surface_y_f;
3439                 //avg_ground_y += surface_y;
3440                 if(surface_y < lowest_ground_y)
3441                         lowest_ground_y = surface_y;
3442                 if(surface_y > highest_ground_y)
3443                         highest_ground_y = surface_y;
3444
3445                 s32 surface_depth = 0;
3446                 
3447                 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
3448                 
3449                 //float min_slope = 0.45;
3450                 //float max_slope = 0.85;
3451                 float min_slope = 0.60;
3452                 float max_slope = 1.20;
3453                 float min_slope_depth = 5.0;
3454                 float max_slope_depth = 0;
3455
3456                 if(slope < min_slope)
3457                         surface_depth = min_slope_depth;
3458                 else if(slope > max_slope)
3459                         surface_depth = max_slope_depth;
3460                 else
3461                         surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
3462
3463                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3464                 {
3465                         s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3466                         MapNode n;
3467                         /*
3468                                 Calculate lighting
3469                                 
3470                                 NOTE: If there are some man-made structures above the
3471                                 newly created block, they won't be taken into account.
3472                         */
3473                         if(real_y > surface_y)
3474                                 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3475
3476                         /*
3477                                 Calculate material
3478                         */
3479
3480                         // If node is over heightmap y, it's air or water
3481                         if(real_y > surface_y)
3482                         {
3483                                 // If under water level, it's water
3484                                 if(real_y < WATER_LEVEL)
3485                                 {
3486                                         n.d = water_material;
3487                                         n.setLight(LIGHTBANK_DAY,
3488                                                         diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3489                                         /*
3490                                                 Add to transforming liquid queue (in case it'd
3491                                                 start flowing)
3492                                         */
3493                                         v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3494                                         m_transforming_liquid.push_back(real_pos);
3495                                 }
3496                                 // else air
3497                                 else
3498                                         n.d = CONTENT_AIR;
3499                         }
3500                         // Else it's ground or dungeons (air)
3501                         else
3502                         {
3503                                 // If it's surface_depth under ground, it's stone
3504                                 if(real_y <= surface_y - surface_depth)
3505                                 {
3506                                         n.d = CONTENT_STONE;
3507                                 }
3508                                 else
3509                                 {
3510                                         // It is mud if it is under the first ground
3511                                         // level or under water
3512                                         if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3513                                         {
3514                                                 n.d = CONTENT_MUD;
3515                                         }
3516                                         else
3517                                         {
3518                                                 n.d = CONTENT_GRASS;
3519                                         }
3520
3521                                         //n.d = CONTENT_MUD;
3522                                         
3523                                         /*// If under water level, it's mud
3524                                         if(real_y < WATER_LEVEL)
3525                                                 n.d = CONTENT_MUD;
3526                                         // Only the topmost node is grass
3527                                         else if(real_y <= surface_y - 1)
3528                                                 n.d = CONTENT_MUD;
3529                                         else
3530                                                 n.d = CONTENT_GRASS;*/
3531                                 }
3532                         }
3533
3534                         block->setNode(v3s16(x0,y0,z0), n);
3535                 }
3536         }
3537         
3538         /*
3539                 Calculate some helper variables
3540         */
3541         
3542         // Completely underground if the highest part of block is under lowest
3543         // ground height.
3544         // This has to be very sure; it's probably one too strict now but
3545         // that's just better.
3546         bool completely_underground =
3547                         block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
3548
3549         bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
3550
3551         bool mostly_underwater_surface = false;
3552         if(highest_ground_y < WATER_LEVEL
3553                         && some_part_underground && !completely_underground)
3554                 mostly_underwater_surface = true;
3555
3556         /*
3557                 Get local attributes
3558         */
3559
3560         //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
3561
3562         float caves_amount = 0.5;
3563
3564 #if 0
3565         {
3566                 /*
3567                         NOTE: BEWARE: Too big amount of attribute points slows verything
3568                         down by a lot.
3569                         1 interpolation from 5000 points takes 2-3ms.
3570                 */
3571                 //TimeTaker timer("generateBlock() local attribute retrieval");
3572                 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3573                 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
3574                 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
3575         }
3576 #endif
3577
3578         //dstream<<"generateBlock(): Done"<<std::endl;
3579
3580         /*
3581                 Generate dungeons
3582         */
3583
3584         // Initialize temporary table
3585         const s32 ued = MAP_BLOCKSIZE;
3586         bool underground_emptiness[ued*ued*ued];
3587         for(s32 i=0; i<ued*ued*ued; i++)
3588         {
3589                 underground_emptiness[i] = 0;
3590         }
3591         
3592         // Fill table
3593 #if 1
3594         {
3595                 /*
3596                         Initialize orp and ors. Try to find if some neighboring
3597                         MapBlock has a tunnel ended in its side
3598                 */
3599
3600                 v3f orp(
3601                         (float)(myrand()%ued)+0.5,
3602                         (float)(myrand()%ued)+0.5,
3603                         (float)(myrand()%ued)+0.5
3604                 );
3605                 
3606                 bool found_existing = false;
3607
3608                 // Check z-
3609                 try
3610                 {
3611                         s16 z = -1;
3612                         for(s16 y=0; y<ued; y++)
3613                         for(s16 x=0; x<ued; x++)
3614                         {
3615                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3616                                 if(getNode(ap).d == CONTENT_AIR)
3617                                 {
3618                                         orp = v3f(x+1,y+1,0);
3619                                         found_existing = true;
3620                                         goto continue_generating;
3621                                 }
3622                         }
3623                 }
3624                 catch(InvalidPositionException &e){}
3625                 
3626                 // Check z+
3627                 try
3628                 {
3629                         s16 z = ued;
3630                         for(s16 y=0; y<ued; y++)
3631                         for(s16 x=0; x<ued; x++)
3632                         {
3633                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3634                                 if(getNode(ap).d == CONTENT_AIR)
3635                                 {
3636                                         orp = v3f(x+1,y+1,ued-1);
3637                                         found_existing = true;
3638                                         goto continue_generating;
3639                                 }
3640                         }
3641                 }
3642                 catch(InvalidPositionException &e){}
3643                 
3644                 // Check x-
3645                 try
3646                 {
3647                         s16 x = -1;
3648                         for(s16 y=0; y<ued; y++)
3649                         for(s16 z=0; z<ued; z++)
3650                         {
3651                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3652                                 if(getNode(ap).d == CONTENT_AIR)
3653                                 {
3654                                         orp = v3f(0,y+1,z+1);
3655                                         found_existing = true;
3656                                         goto continue_generating;
3657                                 }
3658                         }
3659                 }
3660                 catch(InvalidPositionException &e){}
3661                 
3662                 // Check x+
3663                 try
3664                 {
3665                         s16 x = ued;
3666                         for(s16 y=0; y<ued; y++)
3667                         for(s16 z=0; z<ued; z++)
3668                         {
3669                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3670                                 if(getNode(ap).d == CONTENT_AIR)
3671                                 {
3672                                         orp = v3f(ued-1,y+1,z+1);
3673                                         found_existing = true;
3674                                         goto continue_generating;
3675                                 }
3676                         }
3677                 }
3678                 catch(InvalidPositionException &e){}
3679
3680                 // Check y-
3681                 try
3682                 {
3683                         s16 y = -1;
3684                         for(s16 x=0; x<ued; x++)
3685                         for(s16 z=0; z<ued; z++)
3686                         {
3687                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3688                                 if(getNode(ap).d == CONTENT_AIR)
3689                                 {
3690                                         orp = v3f(x+1,0,z+1);
3691                                         found_existing = true;
3692                                         goto continue_generating;
3693                                 }
3694                         }
3695                 }
3696                 catch(InvalidPositionException &e){}
3697                 
3698                 // Check y+
3699                 try
3700                 {
3701                         s16 y = ued;
3702                         for(s16 x=0; x<ued; x++)
3703                         for(s16 z=0; z<ued; z++)
3704                         {
3705                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
3706                                 if(getNode(ap).d == CONTENT_AIR)
3707                                 {
3708                                         orp = v3f(x+1,ued-1,z+1);
3709                                         found_existing = true;
3710                                         goto continue_generating;
3711                                 }
3712                         }
3713                 }
3714                 catch(InvalidPositionException &e){}
3715
3716 continue_generating:
3717                 
3718                 /*
3719                         Choose whether to actually generate dungeon
3720                 */
3721                 bool do_generate_dungeons = true;
3722                 // Don't generate if no part is underground
3723                 if(!some_part_underground)
3724                 {
3725                         do_generate_dungeons = false;
3726                 }
3727                 // Don't generate if mostly underwater surface
3728                 /*else if(mostly_underwater_surface)
3729                 {
3730                         do_generate_dungeons = false;
3731                 }*/
3732                 // Partly underground = cave
3733                 else if(!completely_underground)
3734                 {
3735                         do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3736                 }
3737                 // Found existing dungeon underground
3738                 else if(found_existing && completely_underground)
3739                 {
3740                         do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
3741                 }
3742                 // Underground and no dungeons found
3743                 else
3744                 {
3745                         do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
3746                 }
3747
3748                 if(do_generate_dungeons)
3749                 {
3750                         /*
3751                                 Generate some tunnel starting from orp and ors
3752                         */
3753                         for(u16 i=0; i<3; i++)
3754                         {
3755                                 v3f rp(
3756                                         (float)(myrand()%ued)+0.5,
3757                                         (float)(myrand()%ued)+0.5,
3758                                         (float)(myrand()%ued)+0.5
3759                                 );
3760                                 s16 min_d = 0;
3761                                 s16 max_d = 4;
3762                                 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
3763                                 
3764                                 v3f vec = rp - orp;
3765
3766                                 for(float f=0; f<1.0; f+=0.04)
3767                                 {
3768                                         v3f fp = orp + vec * f;
3769                                         v3s16 cp(fp.X, fp.Y, fp.Z);
3770                                         s16 d0 = -rs/2;
3771                                         s16 d1 = d0 + rs - 1;
3772                                         for(s16 z0=d0; z0<=d1; z0++)
3773                                         {
3774                                                 s16 si = rs - abs(z0);
3775                                                 for(s16 x0=-si; x0<=si-1; x0++)
3776                                                 {
3777                                                         s16 si2 = rs - abs(x0);
3778                                                         for(s16 y0=-si2+1; y0<=si2-1; y0++)
3779                                                         {
3780                                                                 s16 z = cp.Z + z0;
3781                                                                 s16 y = cp.Y + y0;
3782                                                                 s16 x = cp.X + x0;
3783                                                                 v3s16 p(x,y,z);
3784                                                                 if(isInArea(p, ued) == false)
3785                                                                         continue;
3786                                                                 underground_emptiness[ued*ued*z + ued*y + x] = 1;
3787                                                         }
3788                                                 }
3789                                         }
3790                                 }
3791
3792                                 orp = rp;
3793                         }
3794                 }
3795         }
3796 #endif
3797
3798         // Set to true if has caves.
3799         // Set when some non-air is changed to air when making caves.
3800         bool has_dungeons = false;
3801
3802         /*
3803                 Apply temporary cave data to block
3804         */
3805
3806         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3807         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3808         {
3809                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3810                 {
3811                         MapNode n = block->getNode(v3s16(x0,y0,z0));
3812
3813                         // Create dungeons
3814                         if(underground_emptiness[
3815                                         ued*ued*(z0*ued/MAP_BLOCKSIZE)
3816                                         +ued*(y0*ued/MAP_BLOCKSIZE)
3817                                         +(x0*ued/MAP_BLOCKSIZE)])
3818                         {
3819                                 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
3820                                 {
3821                                         // Has now caves
3822                                         has_dungeons = true;
3823                                         // Set air to node
3824                                         n.d = CONTENT_AIR;
3825                                 }
3826                         }
3827
3828                         block->setNode(v3s16(x0,y0,z0), n);
3829                 }
3830         }
3831         
3832         /*
3833                 This is used for guessing whether or not the block should
3834                 receive sunlight from the top if the block above doesn't exist
3835         */
3836         block->setIsUnderground(completely_underground);
3837
3838         /*
3839                 Force lighting update if some part of block is partly
3840                 underground and has caves.
3841         */
3842         /*if(some_part_underground && !completely_underground && has_dungeons)
3843         {
3844                 //dstream<<"Half-ground caves"<<std::endl;
3845                 lighting_invalidated_blocks[block->getPos()] = block;
3846         }*/
3847         
3848         // DEBUG: Always update lighting
3849         //lighting_invalidated_blocks[block->getPos()] = block;
3850
3851         /*
3852                 Add some minerals
3853         */
3854
3855         if(some_part_underground)
3856         {
3857                 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
3858
3859                 /*
3860                         Add meseblocks
3861                 */
3862                 for(s16 i=0; i<underground_level/4 + 1; i++)
3863                 {
3864                         if(myrand()%50 == 0)
3865                         {
3866                                 v3s16 cp(
3867                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3868                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3869                                         (myrand()%(MAP_BLOCKSIZE-2))+1
3870                                 );
3871
3872                                 MapNode n;
3873                                 n.d = CONTENT_MESE;
3874                                 
3875                                 for(u16 i=0; i<27; i++)
3876                                 {
3877                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3878                                                 if(myrand()%8 == 0)
3879                                                         block->setNode(cp+g_27dirs[i], n);
3880                                 }
3881                         }
3882                 }
3883
3884                 /*
3885                         Add coal
3886                 */
3887                 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
3888                 u16 coal_rareness = 60 / coal_amount;
3889                 if(coal_rareness == 0)
3890                         coal_rareness = 1;
3891                 if(myrand()%coal_rareness == 0)
3892                 {
3893                         u16 a = myrand() % 16;
3894                         u16 amount = coal_amount * a*a*a / 1000;
3895                         for(s16 i=0; i<amount; i++)
3896                         {
3897                                 v3s16 cp(
3898                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3899                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3900                                         (myrand()%(MAP_BLOCKSIZE-2))+1
3901                                 );
3902
3903                                 MapNode n;
3904                                 n.d = CONTENT_STONE;
3905                                 n.param = MINERAL_COAL;
3906
3907                                 for(u16 i=0; i<27; i++)
3908                                 {
3909                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3910                                                 if(myrand()%8 == 0)
3911                                                         block->setNode(cp+g_27dirs[i], n);
3912                                 }
3913                         }
3914                 }
3915
3916                 /*
3917                         Add iron
3918                 */
3919                 //TODO: change to iron_amount or whatever
3920                 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
3921                 u16 iron_rareness = 60 / iron_amount;
3922                 if(iron_rareness == 0)
3923                         iron_rareness = 1;
3924                 if(myrand()%iron_rareness == 0)
3925                 {
3926                         u16 a = myrand() % 16;
3927                         u16 amount = iron_amount * a*a*a / 1000;
3928                         for(s16 i=0; i<amount; i++)
3929                         {
3930                                 v3s16 cp(
3931                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3932                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
3933                                         (myrand()%(MAP_BLOCKSIZE-2))+1
3934                                 );
3935
3936                                 MapNode n;
3937                                 n.d = CONTENT_STONE;
3938                                 n.param = MINERAL_IRON;
3939
3940                                 for(u16 i=0; i<27; i++)
3941                                 {
3942                                         if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
3943                                                 if(myrand()%8 == 0)
3944                                                         block->setNode(cp+g_27dirs[i], n);
3945                                 }
3946                         }
3947                 }
3948         }
3949         
3950         /*
3951                 Create a few rats in empty blocks underground
3952         */
3953         if(completely_underground)
3954         {
3955                 //for(u16 i=0; i<2; i++)
3956                 {
3957                         v3s16 cp(
3958                                 (myrand()%(MAP_BLOCKSIZE-2))+1,
3959                                 (myrand()%(MAP_BLOCKSIZE-2))+1,
3960                                 (myrand()%(MAP_BLOCKSIZE-2))+1
3961                         );
3962
3963                         // Check that the place is empty
3964                         //if(!is_ground_content(block->getNode(cp).d))
3965                         if(1)
3966                         {
3967                                 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
3968                                 block->addObject(obj);
3969                         }
3970                 }
3971         }
3972         
3973         /*
3974                 Add block to sector.
3975         */
3976         sector->insertBlock(block);
3977         
3978         /*
3979                 Sector object stuff
3980         */
3981                 
3982         // An y-wise container of changed blocks
3983         core::map<s16, MapBlock*> changed_blocks_sector;
3984
3985         /*
3986                 Check if any sector's objects can be placed now.
3987                 If so, place them.
3988         */
3989         core::map<v3s16, u8> *objects = sector->getObjects();
3990         core::list<v3s16> objects_to_remove;
3991         for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
3992                         i.atEnd() == false; i++)
3993         {
3994                 v3s16 p = i.getNode()->getKey();
3995                 v2s16 p2d(p.X,p.Z);
3996                 u8 d = i.getNode()->getValue();
3997
3998                 // Ground level point (user for stuff that is on ground)
3999                 v3s16 gp = p;
4000                 bool ground_found = true;
4001                 
4002                 // Search real ground level
4003                 try{
4004                         for(;;)
4005                         {
4006                                 MapNode n = sector->getNode(gp);
4007
4008                                 // If not air, go one up and continue to placing the tree
4009                                 if(n.d != CONTENT_AIR)
4010                                 {
4011                                         gp += v3s16(0,1,0);
4012                                         break;
4013                                 }
4014
4015                                 // If air, go one down
4016                                 gp += v3s16(0,-1,0);
4017                         }
4018                 }catch(InvalidPositionException &e)
4019                 {
4020                         // Ground not found.
4021                         ground_found = false;
4022                         // This is most close to ground
4023                         gp += v3s16(0,1,0);
4024                 }
4025
4026                 try
4027                 {
4028
4029                 if(d == SECTOR_OBJECT_TEST)
4030                 {
4031                         if(sector->isValidArea(p + v3s16(0,0,0),
4032                                         p + v3s16(0,0,0), &changed_blocks_sector))
4033                         {
4034                                 MapNode n;
4035                                 n.d = CONTENT_TORCH;
4036                                 sector->setNode(p, n);
4037                                 objects_to_remove.push_back(p);
4038                         }
4039                 }
4040                 else if(d == SECTOR_OBJECT_TREE_1)
4041                 {
4042                         if(ground_found == false)
4043                                 continue;
4044
4045                         v3s16 p_min = gp + v3s16(-1,0,-1);
4046                         v3s16 p_max = gp + v3s16(1,5,1);
4047                         if(sector->isValidArea(p_min, p_max,
4048                                         &changed_blocks_sector))
4049                         {
4050                                 MapNode n;
4051                                 n.d = CONTENT_TREE;
4052                                 sector->setNode(gp+v3s16(0,0,0), n);
4053                                 sector->setNode(gp+v3s16(0,1,0), n);
4054                                 sector->setNode(gp+v3s16(0,2,0), n);
4055                                 sector->setNode(gp+v3s16(0,3,0), n);
4056
4057                                 n.d = CONTENT_LEAVES;
4058
4059                                 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
4060
4061                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
4062                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
4063                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
4064                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
4065                                 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
4066                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
4067                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
4068                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
4069
4070                                 sector->setNode(gp+v3s16(0,4,0), n);
4071                                 
4072                                 sector->setNode(gp+v3s16(-1,4,0), n);
4073                                 sector->setNode(gp+v3s16(1,4,0), n);
4074                                 sector->setNode(gp+v3s16(0,4,-1), n);
4075                                 sector->setNode(gp+v3s16(0,4,1), n);
4076                                 sector->setNode(gp+v3s16(1,4,1), n);
4077                                 sector->setNode(gp+v3s16(-1,4,1), n);
4078                                 sector->setNode(gp+v3s16(-1,4,-1), n);
4079                                 sector->setNode(gp+v3s16(1,4,-1), n);
4080
4081                                 sector->setNode(gp+v3s16(-1,3,0), n);
4082                                 sector->setNode(gp+v3s16(1,3,0), n);
4083                                 sector->setNode(gp+v3s16(0,3,-1), n);
4084                                 sector->setNode(gp+v3s16(0,3,1), n);
4085                                 sector->setNode(gp+v3s16(1,3,1), n);
4086                                 sector->setNode(gp+v3s16(-1,3,1), n);
4087                                 sector->setNode(gp+v3s16(-1,3,-1), n);
4088                                 sector->setNode(gp+v3s16(1,3,-1), n);
4089                                 
4090                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
4091                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
4092                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
4093                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
4094                                 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
4095                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
4096                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
4097                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
4098                                 
4099                                 // Objects are identified by wanted position
4100                                 objects_to_remove.push_back(p);
4101                                 
4102                                 // Lighting has to be recalculated for this one.
4103                                 sector->getBlocksInArea(p_min, p_max, 
4104                                                 lighting_invalidated_blocks);
4105                         }
4106                 }
4107                 else if(d == SECTOR_OBJECT_BUSH_1)
4108                 {
4109                         if(ground_found == false)
4110                                 continue;
4111                         
4112                         if(sector->isValidArea(gp + v3s16(0,0,0),
4113                                         gp + v3s16(0,0,0), &changed_blocks_sector))
4114                         {
4115                                 MapNode n;
4116                                 n.d = CONTENT_LEAVES;
4117                                 sector->setNode(gp+v3s16(0,0,0), n);
4118                                 
4119                                 // Objects are identified by wanted position
4120                                 objects_to_remove.push_back(p);
4121                         }
4122                 }
4123                 else if(d == SECTOR_OBJECT_RAVINE)
4124                 {
4125                         s16 maxdepth = -20;
4126                         v3s16 p_min = p + v3s16(-6,maxdepth,-6);
4127                         v3s16 p_max = p + v3s16(6,6,6);
4128                         if(sector->isValidArea(p_min, p_max,
4129                                         &changed_blocks_sector))
4130                         {
4131                                 MapNode n;
4132                                 n.d = CONTENT_STONE;
4133                                 MapNode n2;
4134                                 n2.d = CONTENT_AIR;
4135                                 s16 depth = maxdepth + (myrand()%10);
4136                                 s16 z = 0;
4137                                 s16 minz = -6 - (-2);
4138                                 s16 maxz = 6 -1;
4139                                 for(s16 x=-6; x<=6; x++)
4140                                 {
4141                                         z += -1 + (myrand()%3);
4142                                         if(z < minz)
4143                                                 z = minz;
4144                                         if(z > maxz)
4145                                                 z = maxz;
4146                                         for(s16 y=depth+(myrand()%2); y<=6; y++)
4147                                         {
4148                                                 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
4149                                                                 <<std::endl;*/
4150                                                 {
4151                                                         v3s16 p2 = p + v3s16(x,y,z-2);
4152                                                         //if(is_ground_content(sector->getNode(p2).d))
4153                                                         if(content_features(sector->getNode(p2).d).walkable)
4154                                                                 sector->setNode(p2, n);
4155                                                 }
4156                                                 {
4157                                                         v3s16 p2 = p + v3s16(x,y,z-1);
4158                                                         if(content_features(sector->getNode(p2).d).walkable)
4159                                                                 sector->setNode(p2, n2);
4160                                                 }
4161                                                 {
4162                                                         v3s16 p2 = p + v3s16(x,y,z+0);
4163                                                         if(content_features(sector->getNode(p2).d).walkable)
4164                                                                 sector->setNode(p2, n2);
4165                                                 }
4166                                                 {
4167                                                         v3s16 p2 = p + v3s16(x,y,z+1);
4168                                                         if(content_features(sector->getNode(p2).d).walkable)
4169                                                                 sector->setNode(p2, n);
4170                                                 }
4171
4172                                                 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
4173                                                 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
4174                                         }
4175                                 }
4176                                 
4177                                 objects_to_remove.push_back(p);
4178                                 
4179                                 // Lighting has to be recalculated for this one.
4180                                 sector->getBlocksInArea(p_min, p_max, 
4181                                                 lighting_invalidated_blocks);
4182                         }
4183                 }
4184                 else
4185                 {
4186                         dstream<<"ServerMap::generateBlock(): "
4187                                         "Invalid heightmap object"
4188                                         <<std::endl;
4189                 }
4190
4191                 }//try
4192                 catch(InvalidPositionException &e)
4193                 {
4194                         dstream<<"WARNING: "<<__FUNCTION_NAME
4195                                         <<": while inserting object "<<(int)d
4196                                         <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
4197                                         <<" InvalidPositionException.what()="
4198                                         <<e.what()<<std::endl;
4199                         // This is not too fatal and seems to happen sometimes.
4200                         assert(0);
4201                 }
4202         }
4203
4204         for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
4205                         i != objects_to_remove.end(); i++)
4206         {
4207                 objects->remove(*i);
4208         }
4209         
4210         /*
4211                 Translate sector's changed blocks to global changed blocks
4212         */
4213         
4214         for(core::map<s16, MapBlock*>::Iterator
4215                         i = changed_blocks_sector.getIterator();
4216                         i.atEnd() == false; i++)
4217         {
4218                 MapBlock *block = i.getNode()->getValue();
4219
4220                 changed_blocks.insert(block->getPos(), block);
4221         }
4222
4223         block->setLightingExpired(true);
4224         
4225 #if 0
4226         /*
4227                 Debug information
4228         */
4229         dstream
4230         <<"lighting_invalidated_blocks.size()"
4231         <<", has_dungeons"
4232         <<", completely_ug"
4233         <<", some_part_ug"
4234         <<"  "<<lighting_invalidated_blocks.size()
4235         <<", "<<has_dungeons
4236         <<", "<<completely_underground
4237         <<", "<<some_part_underground
4238         <<std::endl;
4239 #endif
4240
4241         return block;
4242 }
4243
4244 MapBlock * ServerMap::createBlock(v3s16 p)
4245 {
4246         DSTACK("%s: p=(%d,%d,%d)",
4247                         __FUNCTION_NAME, p.X, p.Y, p.Z);
4248         
4249         v2s16 p2d(p.X, p.Z);
4250         s16 block_y = p.Y;
4251         /*
4252                 This will create or load a sector if not found in memory.
4253                 If block exists on disk, it will be loaded.
4254
4255                 NOTE: On old save formats, this will be slow, as it generates
4256                       lighting on blocks for them.
4257         */
4258         ServerMapSector *sector;
4259         try{
4260                 sector = (ServerMapSector*)createSector(p2d);
4261                 assert(sector->getId() == MAPSECTOR_SERVER);
4262         }
4263         /*catch(InvalidPositionException &e)
4264         {
4265                 dstream<<"createBlock: createSector() failed"<<std::endl;
4266                 throw e;
4267         }*/
4268         catch(std::exception &e)
4269         {
4270                 dstream<<"createBlock: createSector() failed: "
4271                                 <<e.what()<<std::endl;
4272                 throw e;
4273         }
4274
4275         /*
4276                 Try to get a block from the sector
4277         */
4278
4279         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4280         if(block)
4281                 return block;
4282         // Create blank
4283         block = sector->createBlankBlock(block_y);
4284         return block;
4285 }
4286
4287 MapBlock * ServerMap::emergeBlock(
4288                 v3s16 p,
4289                 bool only_from_disk,
4290                 core::map<v3s16, MapBlock*> &changed_blocks,
4291                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4292 )
4293 {
4294         DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4295                         __FUNCTION_NAME,
4296                         p.X, p.Y, p.Z, only_from_disk);
4297         
4298         v2s16 p2d(p.X, p.Z);
4299         s16 block_y = p.Y;
4300         /*
4301                 This will create or load a sector if not found in memory.
4302                 If block exists on disk, it will be loaded.
4303
4304                 NOTE: On old save formats, this will be slow, as it generates
4305                       lighting on blocks for them.
4306         */
4307         ServerMapSector *sector;
4308         try{
4309                 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4310                 assert(sector->getId() == MAPSECTOR_SERVER);
4311         }
4312         catch(std::exception &e)
4313         {
4314                 dstream<<"emergeBlock: emergeSector() failed: "
4315                                 <<e.what()<<std::endl;
4316                 throw e;
4317         }
4318
4319         /*
4320                 Try to get a block from the sector
4321         */
4322
4323         bool does_not_exist = false;
4324         bool lighting_expired = false;
4325         MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4326
4327         if(block == NULL)
4328         {
4329                 does_not_exist = true;
4330         }
4331         else if(block->isDummy() == true)
4332         {
4333                 does_not_exist = true;
4334         }
4335         else if(block->getLightingExpired())
4336         {
4337                 lighting_expired = true;
4338         }
4339         else
4340         {
4341                 // Valid block
4342                 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4343                 return block;
4344         }
4345         
4346         /*
4347                 If block was not found on disk and not going to generate a
4348                 new one, make sure there is a dummy block in place.
4349         */
4350         if(only_from_disk && (does_not_exist || lighting_expired))
4351         {
4352                 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4353
4354                 if(block == NULL)
4355                 {
4356                         // Create dummy block
4357                         block = new MapBlock(this, p, true);
4358
4359                         // Add block to sector
4360                         sector->insertBlock(block);
4361                 }
4362                 // Done.
4363                 return block;
4364         }
4365
4366         //dstream<<"Not found on disk, generating."<<std::endl;
4367         // 0ms
4368         //TimeTaker("emergeBlock() generate");
4369
4370         //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4371
4372         /*
4373                 If the block doesn't exist, generate the block.
4374         */
4375         if(does_not_exist)
4376         {
4377                 block = generateBlock(p, block, sector, changed_blocks,
4378                                 lighting_invalidated_blocks); 
4379         }
4380
4381         if(lighting_expired)
4382         {
4383                 lighting_invalidated_blocks.insert(p, block);
4384         }
4385
4386         /*
4387                 Initially update sunlight
4388         */
4389         
4390         {
4391                 core::map<v3s16, bool> light_sources;
4392                 bool black_air_left = false;
4393                 bool bottom_invalid =
4394                                 block->propagateSunlight(light_sources, true,
4395                                 &black_air_left, true);
4396
4397                 // If sunlight didn't reach everywhere and part of block is
4398                 // above ground, lighting has to be properly updated
4399                 //if(black_air_left && some_part_underground)
4400                 if(black_air_left)
4401                 {
4402                         lighting_invalidated_blocks[block->getPos()] = block;
4403                 }
4404
4405                 if(bottom_invalid)
4406                 {
4407                         lighting_invalidated_blocks[block->getPos()] = block;
4408                 }
4409         }
4410         
4411         /*
4412                 Debug mode operation
4413         */
4414         bool haxmode = g_settings.getBool("haxmode");
4415         if(haxmode)
4416         {
4417                 // Don't calculate lighting at all
4418                 //lighting_invalidated_blocks.clear();
4419         }
4420
4421         return block;
4422 }
4423
4424 void ServerMap::createDir(std::string path)
4425 {
4426         if(fs::CreateDir(path) == false)
4427         {
4428                 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4429                                 <<"\""<<path<<"\""<<std::endl;
4430                 throw BaseException("ServerMap failed to create directory");
4431         }
4432 }
4433
4434 std::string ServerMap::getSectorSubDir(v2s16 pos)
4435 {
4436         char cc[9];
4437         snprintf(cc, 9, "%.4x%.4x",
4438                         (unsigned int)pos.X&0xffff,
4439                         (unsigned int)pos.Y&0xffff);
4440
4441         return std::string(cc);
4442 }
4443
4444 std::string ServerMap::getSectorDir(v2s16 pos)
4445 {
4446         return m_savedir + "/sectors/" + getSectorSubDir(pos);
4447 }
4448
4449 v2s16 ServerMap::getSectorPos(std::string dirname)
4450 {
4451         if(dirname.size() != 8)
4452                 throw InvalidFilenameException("Invalid sector directory name");
4453         unsigned int x, y;
4454         int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4455         if(r != 2)
4456                 throw InvalidFilenameException("Invalid sector directory name");
4457         v2s16 pos((s16)x, (s16)y);
4458         return pos;
4459 }
4460
4461 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4462 {
4463         v2s16 p2d = getSectorPos(sectordir);
4464
4465         if(blockfile.size() != 4){
4466                 throw InvalidFilenameException("Invalid block filename");
4467         }
4468         unsigned int y;
4469         int r = sscanf(blockfile.c_str(), "%4x", &y);
4470         if(r != 1)
4471                 throw InvalidFilenameException("Invalid block filename");
4472         return v3s16(p2d.X, y, p2d.Y);
4473 }
4474
4475 // Debug helpers
4476 #define ENABLE_SECTOR_SAVING 1
4477 #define ENABLE_SECTOR_LOADING 1
4478 #define ENABLE_BLOCK_SAVING 1
4479 #define ENABLE_BLOCK_LOADING 1
4480
4481 void ServerMap::save(bool only_changed)
4482 {
4483         DSTACK(__FUNCTION_NAME);
4484         if(m_map_saving_enabled == false)
4485         {
4486                 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4487                 return;
4488         }
4489         
4490         if(only_changed == false)
4491                 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4492                                 <<std::endl;
4493         
4494         saveMasterHeightmap();
4495         
4496         u32 sector_meta_count = 0;
4497         u32 block_count = 0;
4498         
4499         { //sectorlock
4500         JMutexAutoLock lock(m_sector_mutex);
4501         
4502         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4503         for(; i.atEnd() == false; i++)
4504         {
4505                 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4506                 assert(sector->getId() == MAPSECTOR_SERVER);
4507                 
4508                 if(ENABLE_SECTOR_SAVING)
4509                 {
4510                         if(sector->differs_from_disk || only_changed == false)
4511                         {
4512                                 saveSectorMeta(sector);
4513                                 sector_meta_count++;
4514                         }
4515                 }
4516                 if(ENABLE_BLOCK_SAVING)
4517                 {
4518                         core::list<MapBlock*> blocks;
4519                         sector->getBlocks(blocks);
4520                         core::list<MapBlock*>::Iterator j;
4521                         for(j=blocks.begin(); j!=blocks.end(); j++)
4522                         {
4523                                 MapBlock *block = *j;
4524                                 if(block->getChangedFlag() || only_changed == false)
4525                                 {
4526                                         saveBlock(block);
4527                                         block_count++;
4528                                 }
4529                         }
4530                 }
4531         }
4532
4533         }//sectorlock
4534         
4535         /*
4536                 Only print if something happened or saved whole map
4537         */
4538         if(only_changed == false || sector_meta_count != 0
4539                         || block_count != 0)
4540         {
4541                 dstream<<DTIME<<"ServerMap: Written: "
4542                                 <<sector_meta_count<<" sector metadata files, "
4543                                 <<block_count<<" block files"
4544                                 <<std::endl;
4545         }
4546 }
4547
4548 void ServerMap::loadAll()
4549 {
4550         DSTACK(__FUNCTION_NAME);
4551         dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4552
4553         loadMasterHeightmap();
4554
4555         std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4556
4557         dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4558         
4559         JMutexAutoLock lock(m_sector_mutex);
4560         
4561         s32 counter = 0;
4562         s32 printed_counter = -100000;
4563         s32 count = list.size();
4564
4565         std::vector<fs::DirListNode>::iterator i;
4566         for(i=list.begin(); i!=list.end(); i++)
4567         {
4568                 if(counter > printed_counter + 10)
4569                 {
4570                         dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4571                         printed_counter = counter;
4572                 }
4573                 counter++;
4574
4575                 MapSector *sector = NULL;
4576
4577                 // We want directories
4578                 if(i->dir == false)
4579                         continue;
4580                 try{
4581                         sector = loadSectorMeta(i->name);
4582                 }
4583                 catch(InvalidFilenameException &e)
4584                 {
4585                         // This catches unknown crap in directory
4586                 }
4587                 
4588                 if(ENABLE_BLOCK_LOADING)
4589                 {
4590                         std::vector<fs::DirListNode> list2 = fs::GetDirListing
4591                                         (m_savedir+"/sectors/"+i->name);
4592                         std::vector<fs::DirListNode>::iterator i2;
4593                         for(i2=list2.begin(); i2!=list2.end(); i2++)
4594                         {
4595                                 // We want files
4596                                 if(i2->dir)
4597                                         continue;
4598                                 try{
4599                                         loadBlock(i->name, i2->name, sector);
4600                                 }
4601                                 catch(InvalidFilenameException &e)
4602                                 {
4603                                         // This catches unknown crap in directory
4604                                 }
4605                         }
4606                 }
4607         }
4608         dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4609 }
4610
4611 void ServerMap::saveMasterHeightmap()
4612 {
4613         DSTACK(__FUNCTION_NAME);
4614         createDir(m_savedir);
4615         
4616         std::string fullpath = m_savedir + "/master_heightmap";
4617         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4618         if(o.good() == false)
4619                 throw FileNotGoodException("Cannot open master heightmap");
4620         
4621         // Format used for writing
4622         u8 version = SER_FMT_VER_HIGHEST;
4623
4624 #if 0
4625         SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
4626         /*
4627                 [0] u8 serialization version
4628                 [1] X master heightmap
4629         */
4630         u32 fullsize = 1 + hmdata.getSize();
4631         SharedBuffer<u8> data(fullsize);
4632
4633         data[0] = version;
4634         memcpy(&data[1], *hmdata, hmdata.getSize());
4635
4636         o.write((const char*)*data, fullsize);
4637 #endif
4638         
4639         m_heightmap->serialize(o, version);
4640 }
4641
4642 void ServerMap::loadMasterHeightmap()
4643 {
4644         DSTACK(__FUNCTION_NAME);
4645         std::string fullpath = m_savedir + "/master_heightmap";
4646         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4647         if(is.good() == false)
4648                 throw FileNotGoodException("Cannot open master heightmap");
4649         
4650         if(m_heightmap != NULL)
4651                 delete m_heightmap;
4652                 
4653         m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
4654 }
4655
4656 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4657 {
4658         DSTACK(__FUNCTION_NAME);
4659         // Format used for writing
4660         u8 version = SER_FMT_VER_HIGHEST;
4661         // Get destination
4662         v2s16 pos = sector->getPos();
4663         createDir(m_savedir);
4664         createDir(m_savedir+"/sectors");
4665         std::string dir = getSectorDir(pos);
4666         createDir(dir);
4667         
4668         std::string fullpath = dir + "/heightmap";
4669         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4670         if(o.good() == false)
4671                 throw FileNotGoodException("Cannot open master heightmap");
4672
4673         sector->serialize(o, version);
4674         
4675         sector->differs_from_disk = false;
4676 }
4677
4678 MapSector* ServerMap::loadSectorMeta(std::string dirname)
4679 {
4680         DSTACK(__FUNCTION_NAME);
4681         // Get destination
4682         v2s16 p2d = getSectorPos(dirname);
4683         std::string dir = m_savedir + "/sectors/" + dirname;
4684         
4685         std::string fullpath = dir + "/heightmap";
4686         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4687         if(is.good() == false)
4688                 throw FileNotGoodException("Cannot open sector heightmap");
4689
4690         ServerMapSector *sector = ServerMapSector::deSerialize
4691                         (is, this, p2d, &m_hwrapper, m_sectors);
4692         
4693         sector->differs_from_disk = false;
4694
4695         return sector;
4696 }
4697
4698 bool ServerMap::loadSectorFull(v2s16 p2d)
4699 {
4700         DSTACK(__FUNCTION_NAME);
4701         std::string sectorsubdir = getSectorSubDir(p2d);
4702
4703         MapSector *sector = NULL;
4704
4705         JMutexAutoLock lock(m_sector_mutex);
4706
4707         try{
4708                 sector = loadSectorMeta(sectorsubdir);
4709         }
4710         catch(InvalidFilenameException &e)
4711         {
4712                 return false;
4713         }
4714         catch(FileNotGoodException &e)
4715         {
4716                 return false;
4717         }
4718         catch(std::exception &e)
4719         {
4720                 return false;
4721         }
4722
4723         if(ENABLE_BLOCK_LOADING)
4724         {
4725                 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4726                                 (m_savedir+"/sectors/"+sectorsubdir);
4727                 std::vector<fs::DirListNode>::iterator i2;
4728                 for(i2=list2.begin(); i2!=list2.end(); i2++)
4729                 {
4730                         // We want files
4731                         if(i2->dir)
4732                                 continue;
4733                         try{
4734                                 loadBlock(sectorsubdir, i2->name, sector);
4735                         }
4736                         catch(InvalidFilenameException &e)
4737                         {
4738                                 // This catches unknown crap in directory
4739                         }
4740                 }
4741         }
4742         return true;
4743 }
4744
4745 #if 0
4746 bool ServerMap::deFlushSector(v2s16 p2d)
4747 {
4748         DSTACK(__FUNCTION_NAME);
4749         // See if it already exists in memory
4750         try{
4751                 MapSector *sector = getSectorNoGenerate(p2d);
4752                 return true;
4753         }
4754         catch(InvalidPositionException &e)
4755         {
4756                 /*
4757                         Try to load the sector from disk.
4758                 */
4759                 if(loadSectorFull(p2d) == true)
4760                 {
4761                         return true;
4762                 }
4763         }
4764         return false;
4765 }
4766 #endif
4767
4768 void ServerMap::saveBlock(MapBlock *block)
4769 {
4770         DSTACK(__FUNCTION_NAME);
4771         /*
4772                 Dummy blocks are not written
4773         */
4774         if(block->isDummy())
4775         {
4776                 /*v3s16 p = block->getPos();
4777                 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4778                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4779                 return;
4780         }
4781
4782         // Format used for writing
4783         u8 version = SER_FMT_VER_HIGHEST;
4784         // Get destination
4785         v3s16 p3d = block->getPos();
4786         v2s16 p2d(p3d.X, p3d.Z);
4787         createDir(m_savedir);
4788         createDir(m_savedir+"/sectors");
4789         std::string dir = getSectorDir(p2d);
4790         createDir(dir);
4791         
4792         // Block file is map/sectors/xxxxxxxx/xxxx
4793         char cc[5];
4794         snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
4795         std::string fullpath = dir + "/" + cc;
4796         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4797         if(o.good() == false)
4798                 throw FileNotGoodException("Cannot open block data");
4799
4800         /*
4801                 [0] u8 serialization version
4802                 [1] data
4803         */
4804         o.write((char*)&version, 1);
4805         
4806         block->serialize(o, version);
4807
4808         /*
4809                 Versions up from 9 have block objects.
4810         */
4811         if(version >= 9)
4812         {
4813                 block->serializeObjects(o, version);
4814         }
4815         
4816         // We just wrote it to the disk
4817         block->resetChangedFlag();
4818 }
4819
4820 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
4821 {
4822         DSTACK(__FUNCTION_NAME);
4823
4824         try{
4825
4826         // Block file is map/sectors/xxxxxxxx/xxxx
4827         std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
4828         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4829         if(is.good() == false)
4830                 throw FileNotGoodException("Cannot open block file");
4831
4832         v3s16 p3d = getBlockPos(sectordir, blockfile);
4833         v2s16 p2d(p3d.X, p3d.Z);
4834         
4835         assert(sector->getPos() == p2d);
4836         
4837         u8 version = SER_FMT_VER_INVALID;
4838         is.read((char*)&version, 1);
4839
4840         /*u32 block_size = MapBlock::serializedLength(version);
4841         SharedBuffer<u8> data(block_size);
4842         is.read((char*)*data, block_size);*/
4843
4844         // This will always return a sector because we're the server
4845         //MapSector *sector = emergeSector(p2d);
4846
4847         MapBlock *block = NULL;
4848         bool created_new = false;
4849         try{
4850                 block = sector->getBlockNoCreate(p3d.Y);
4851         }
4852         catch(InvalidPositionException &e)
4853         {
4854                 block = sector->createBlankBlockNoInsert(p3d.Y);
4855                 created_new = true;
4856         }
4857         
4858         // deserialize block data
4859         block->deSerialize(is, version);
4860         
4861         /*
4862                 Versions up from 9 have block objects.
4863         */
4864         if(version >= 9)
4865         {
4866                 block->updateObjects(is, version, NULL, 0);
4867         }
4868
4869         if(created_new)
4870                 sector->insertBlock(block);
4871         
4872         /*
4873                 Convert old formats to new and save
4874         */
4875
4876         // Save old format blocks in new format
4877         if(version < SER_FMT_VER_HIGHEST)
4878         {
4879                 saveBlock(block);
4880         }
4881         
4882         // We just loaded it from the disk, so it's up-to-date.
4883         block->resetChangedFlag();
4884
4885         }
4886         catch(SerializationError &e)
4887         {
4888                 dstream<<"WARNING: Invalid block data on disk "
4889                                 "(SerializationError). Ignoring."
4890                                 <<std::endl;
4891         }
4892 }
4893
4894 // Gets from master heightmap
4895 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
4896 {
4897         assert(m_heightmap != NULL);
4898         /*
4899                 Corner definition:
4900                 v2s16(0,0),
4901                 v2s16(1,0),
4902                 v2s16(1,1),
4903                 v2s16(0,1),
4904         */
4905         corners[0] = m_heightmap->getGroundHeight
4906                         ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
4907         corners[1] = m_heightmap->getGroundHeight
4908                         ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
4909         corners[2] = m_heightmap->getGroundHeight
4910                         ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
4911         corners[3] = m_heightmap->getGroundHeight
4912                         ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
4913 }
4914
4915 void ServerMap::PrintInfo(std::ostream &out)
4916 {
4917         out<<"ServerMap: ";
4918 }
4919
4920 #ifndef SERVER
4921
4922 /*
4923         ClientMap
4924 */
4925
4926 ClientMap::ClientMap(
4927                 Client *client,
4928                 MapDrawControl &control,
4929                 scene::ISceneNode* parent,
4930                 scene::ISceneManager* mgr,
4931                 s32 id
4932 ):
4933         Map(dout_client),
4934         scene::ISceneNode(parent, mgr, id),
4935         m_client(client),
4936         mesh(NULL),
4937         m_control(control)
4938 {
4939         mesh_mutex.Init();
4940
4941         /*m_box = core::aabbox3d<f32>(0,0,0,
4942                         map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
4943         /*m_box = core::aabbox3d<f32>(0,0,0,
4944                         map->getSizeNodes().X * BS,
4945                         map->getSizeNodes().Y * BS,
4946                         map->getSizeNodes().Z * BS);*/
4947         m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
4948                         BS*1000000,BS*1000000,BS*1000000);
4949         
4950         //setPosition(v3f(BS,BS,BS));
4951 }
4952
4953 ClientMap::~ClientMap()
4954 {
4955         JMutexAutoLock lock(mesh_mutex);
4956         
4957         if(mesh != NULL)
4958         {
4959                 mesh->drop();
4960                 mesh = NULL;
4961         }
4962 }
4963
4964 MapSector * ClientMap::emergeSector(v2s16 p2d)
4965 {
4966         DSTACK(__FUNCTION_NAME);
4967         // Check that it doesn't exist already
4968         try{
4969                 return getSectorNoGenerate(p2d);
4970         }
4971         catch(InvalidPositionException &e)
4972         {
4973         }
4974         
4975         // Create a sector with no heightmaps
4976         ClientMapSector *sector = new ClientMapSector(this, p2d);
4977         
4978         {
4979                 JMutexAutoLock lock(m_sector_mutex);
4980                 m_sectors.insert(p2d, sector);
4981         }
4982         
4983         return sector;
4984 }
4985
4986 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
4987 {
4988         DSTACK(__FUNCTION_NAME);
4989         ClientMapSector *sector = NULL;
4990
4991         JMutexAutoLock lock(m_sector_mutex);
4992         
4993         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
4994
4995         if(n != NULL)
4996         {
4997                 sector = (ClientMapSector*)n->getValue();
4998                 assert(sector->getId() == MAPSECTOR_CLIENT);
4999         }
5000         else
5001         {
5002                 sector = new ClientMapSector(this, p2d);
5003                 {
5004                         JMutexAutoLock lock(m_sector_mutex);
5005                         m_sectors.insert(p2d, sector);
5006                 }
5007         }
5008
5009         sector->deSerialize(is);
5010 }
5011
5012 void ClientMap::OnRegisterSceneNode()
5013 {
5014         if(IsVisible)
5015         {
5016                 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5017                 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5018         }
5019
5020         ISceneNode::OnRegisterSceneNode();
5021 }
5022
5023 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5024 {
5025         //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5026         DSTACK(__FUNCTION_NAME);
5027
5028         bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5029
5030         /*
5031                 Get time for measuring timeout.
5032                 
5033                 Measuring time is very useful for long delays when the
5034                 machine is swapping a lot.
5035         */
5036         int time1 = time(0);
5037
5038         u32 daynight_ratio = m_client->getDayNightRatio();
5039
5040         m_camera_mutex.Lock();
5041         v3f camera_position = m_camera_position;
5042         v3f camera_direction = m_camera_direction;
5043         m_camera_mutex.Unlock();
5044
5045         /*
5046                 Get all blocks and draw all visible ones
5047         */
5048
5049         v3s16 cam_pos_nodes(
5050                         camera_position.X / BS,
5051                         camera_position.Y / BS,
5052                         camera_position.Z / BS);
5053
5054         v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5055
5056         v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5057         v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5058
5059         // Take a fair amount as we will be dropping more out later
5060         v3s16 p_blocks_min(
5061                         p_nodes_min.X / MAP_BLOCKSIZE - 1,
5062                         p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5063                         p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5064         v3s16 p_blocks_max(
5065                         p_nodes_max.X / MAP_BLOCKSIZE + 1,
5066                         p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5067                         p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5068         
5069         u32 vertex_count = 0;
5070         
5071         // For limiting number of mesh updates per frame
5072         u32 mesh_update_count = 0;
5073         
5074         u32 blocks_would_have_drawn = 0;
5075         u32 blocks_drawn = 0;
5076
5077         //NOTE: The sectors map should be locked but we're not doing it
5078         // because it'd cause too much delays
5079
5080         int timecheck_counter = 0;
5081         core::map<v2s16, MapSector*>::Iterator si;
5082         si = m_sectors.getIterator();
5083         for(; si.atEnd() == false; si++)
5084         {
5085                 {
5086                         timecheck_counter++;
5087                         if(timecheck_counter > 50)
5088                         {
5089                                 int time2 = time(0);
5090                                 if(time2 > time1 + 4)
5091                                 {
5092                                         dstream<<"ClientMap::renderMap(): "
5093                                                 "Rendering takes ages, returning."
5094                                                 <<std::endl;
5095                                         return;
5096                                 }
5097                         }
5098                 }
5099
5100                 MapSector *sector = si.getNode()->getValue();
5101                 v2s16 sp = sector->getPos();
5102                 
5103                 if(m_control.range_all == false)
5104                 {
5105                         if(sp.X < p_blocks_min.X
5106                         || sp.X > p_blocks_max.X
5107                         || sp.Y < p_blocks_min.Z
5108                         || sp.Y > p_blocks_max.Z)
5109                                 continue;
5110                 }
5111
5112                 core::list< MapBlock * > sectorblocks;
5113                 sector->getBlocks(sectorblocks);
5114                 
5115                 /*
5116                         Draw blocks
5117                 */
5118
5119                 core::list< MapBlock * >::Iterator i;
5120                 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5121                 {
5122                         MapBlock *block = *i;
5123
5124                         /*
5125                                 Compare block position to camera position, skip
5126                                 if not seen on display
5127                         */
5128                         
5129                         float range = 100000 * BS;
5130                         if(m_control.range_all == false)
5131                                 range = m_control.wanted_range * BS;
5132
5133                         if(isBlockInSight(block->getPos(), camera_position,
5134                                         camera_direction, range) == false)
5135                         {
5136                                 continue;
5137                         }
5138
5139 #if 0                   
5140                         v3s16 blockpos_nodes = block->getPosRelative();
5141                         
5142                         // Block center position
5143                         v3f blockpos(
5144                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5145                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5146                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5147                         );
5148
5149                         // Block position relative to camera
5150                         v3f blockpos_relative = blockpos - camera_position;
5151
5152                         // Distance in camera direction (+=front, -=back)
5153                         f32 dforward = blockpos_relative.dotProduct(camera_direction);
5154
5155                         // Total distance
5156                         f32 d = blockpos_relative.getLength();
5157                         
5158                         if(m_control.range_all == false)
5159                         {
5160                                 // If block is far away, don't draw it
5161                                 if(d > m_control.wanted_range * BS)
5162                                         continue;
5163                         }
5164                         
5165                         // Maximum radius of a block
5166                         f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
5167                         
5168                         // If block is (nearly) touching the camera, don't
5169                         // bother validating further (that is, render it anyway)
5170                         if(d > block_max_radius * 1.5)
5171                         {
5172                                 // Cosine of the angle between the camera direction
5173                                 // and the block direction (camera_direction is an unit vector)
5174                                 f32 cosangle = dforward / d;
5175                                 
5176                                 // Compensate for the size of the block
5177                                 // (as the block has to be shown even if it's a bit off FOV)
5178                                 // This is an estimate.
5179                                 cosangle += block_max_radius / dforward;
5180
5181                                 // If block is not in the field of view, skip it
5182                                 //if(cosangle < cos(FOV_ANGLE/2))
5183                                 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
5184                                         continue;
5185                         }
5186 #endif                  
5187
5188                         v3s16 blockpos_nodes = block->getPosRelative();
5189                         
5190                         // Block center position
5191                         v3f blockpos(
5192                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
5193                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
5194                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
5195                         );
5196
5197                         // Block position relative to camera
5198                         v3f blockpos_relative = blockpos - camera_position;
5199
5200                         // Total distance
5201                         f32 d = blockpos_relative.getLength();
5202                         
5203 #if 1
5204                         /*
5205                                 Update expired mesh
5206                         */
5207
5208                         bool mesh_expired = false;
5209                         
5210                         {
5211                                 JMutexAutoLock lock(block->mesh_mutex);
5212
5213                                 mesh_expired = block->getMeshExpired();
5214
5215                                 // Mesh has not been expired and there is no mesh:
5216                                 // block has no content
5217                                 if(block->mesh == NULL && mesh_expired == false)
5218                                         continue;
5219                         }
5220
5221                         f32 faraway = BS*50;
5222                         //f32 faraway = m_control.wanted_range * BS;
5223                         
5224                         /*
5225                                 This has to be done with the mesh_mutex unlocked
5226                         */
5227                         // Pretty random but this should work somewhat nicely
5228                         if(mesh_expired && (
5229                                         (mesh_update_count < 3
5230                                                 && (d < faraway || mesh_update_count < 2)
5231                                         )
5232                                         || 
5233                                         (m_control.range_all && mesh_update_count < 20)
5234                                 )
5235                         )
5236                         /*if(mesh_expired && mesh_update_count < 6
5237                                         && (d < faraway || mesh_update_count < 3))*/
5238                         {
5239                                 mesh_update_count++;
5240
5241                                 // Mesh has been expired: generate new mesh
5242                                 //block->updateMeshes(daynight_i);
5243                                 block->updateMesh(daynight_ratio);
5244
5245                                 mesh_expired = false;
5246                         }
5247                         
5248                         /*
5249                                 Don't draw an expired mesh that is far away
5250                         */
5251                         /*if(mesh_expired && d >= faraway)
5252                         //if(mesh_expired)
5253                         {
5254                                 // Instead, delete it
5255                                 JMutexAutoLock lock(block->mesh_mutex);
5256                                 if(block->mesh)
5257                                 {
5258                                         block->mesh->drop();
5259                                         block->mesh = NULL;
5260                                 }
5261                                 // And continue to next block
5262                                 continue;
5263                         }*/
5264 #endif
5265                         /*
5266                                 Draw the faces of the block
5267                         */
5268                         {
5269                                 JMutexAutoLock lock(block->mesh_mutex);
5270
5271                                 scene::SMesh *mesh = block->mesh;
5272
5273                                 if(mesh == NULL)
5274                                         continue;
5275                                 
5276                                 blocks_would_have_drawn++;
5277                                 if(blocks_drawn >= m_control.wanted_max_blocks
5278                                                 && m_control.range_all == false
5279                                                 && d > m_control.wanted_min_range * BS)
5280                                         continue;
5281                                 blocks_drawn++;
5282
5283                                 u32 c = mesh->getMeshBufferCount();
5284
5285                                 for(u32 i=0; i<c; i++)
5286                                 {
5287                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5288                                         const video::SMaterial& material = buf->getMaterial();
5289                                         video::IMaterialRenderer* rnd =
5290                                                         driver->getMaterialRenderer(material.MaterialType);
5291                                         bool transparent = (rnd && rnd->isTransparent());
5292                                         // Render transparent on transparent pass and likewise.
5293                                         if(transparent == is_transparent_pass)
5294                                         {
5295                                                 driver->setMaterial(buf->getMaterial());
5296                                                 driver->drawMeshBuffer(buf);
5297                                                 vertex_count += buf->getVertexCount();
5298                                         }
5299                                 }
5300                         }
5301                 } // foreach sectorblocks
5302         }
5303         
5304         m_control.blocks_drawn = blocks_drawn;
5305         m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5306
5307         /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5308                         <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5309 }
5310
5311 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5312                 core::map<v3s16, MapBlock*> *affected_blocks)
5313 {
5314         bool changed = false;
5315         /*
5316                 Add it to all blocks touching it
5317         */
5318         v3s16 dirs[7] = {
5319                 v3s16(0,0,0), // this
5320                 v3s16(0,0,1), // back
5321                 v3s16(0,1,0), // top
5322                 v3s16(1,0,0), // right
5323                 v3s16(0,0,-1), // front
5324                 v3s16(0,-1,0), // bottom
5325                 v3s16(-1,0,0), // left
5326         };
5327         for(u16 i=0; i<7; i++)
5328         {
5329                 v3s16 p2 = p + dirs[i];
5330                 // Block position of neighbor (or requested) node
5331                 v3s16 blockpos = getNodeBlockPos(p2);
5332                 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5333                 if(blockref == NULL)
5334                         continue;
5335                 // Relative position of requested node
5336                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5337                 if(blockref->setTempMod(relpos, mod))
5338                 {
5339                         changed = true;
5340                 }
5341         }
5342         if(changed && affected_blocks!=NULL)
5343         {
5344                 for(u16 i=0; i<7; i++)
5345                 {
5346                         v3s16 p2 = p + dirs[i];
5347                         // Block position of neighbor (or requested) node
5348                         v3s16 blockpos = getNodeBlockPos(p2);
5349                         MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5350                         if(blockref == NULL)
5351                                 continue;
5352                         affected_blocks->insert(blockpos, blockref);
5353                 }
5354         }
5355         return changed;
5356 }
5357
5358 bool ClientMap::clearTempMod(v3s16 p,
5359                 core::map<v3s16, MapBlock*> *affected_blocks)
5360 {
5361         bool changed = false;
5362         v3s16 dirs[7] = {
5363                 v3s16(0,0,0), // this
5364                 v3s16(0,0,1), // back
5365                 v3s16(0,1,0), // top
5366                 v3s16(1,0,0), // right
5367                 v3s16(0,0,-1), // front
5368                 v3s16(0,-1,0), // bottom
5369                 v3s16(-1,0,0), // left
5370         };
5371         for(u16 i=0; i<7; i++)
5372         {
5373                 v3s16 p2 = p + dirs[i];
5374                 // Block position of neighbor (or requested) node
5375                 v3s16 blockpos = getNodeBlockPos(p2);
5376                 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5377                 if(blockref == NULL)
5378                         continue;
5379                 // Relative position of requested node
5380                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5381                 if(blockref->clearTempMod(relpos))
5382                 {
5383                         changed = true;
5384                 }
5385         }
5386         if(changed && affected_blocks!=NULL)
5387         {
5388                 for(u16 i=0; i<7; i++)
5389                 {
5390                         v3s16 p2 = p + dirs[i];
5391                         // Block position of neighbor (or requested) node
5392                         v3s16 blockpos = getNodeBlockPos(p2);
5393                         MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5394                         if(blockref == NULL)
5395                                 continue;
5396                         affected_blocks->insert(blockpos, blockref);
5397                 }
5398         }
5399         return changed;
5400 }
5401
5402 void ClientMap::PrintInfo(std::ostream &out)
5403 {
5404         out<<"ClientMap: ";
5405 }
5406
5407 #endif // !SERVER
5408
5409 /*
5410         MapVoxelManipulator
5411 */
5412
5413 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5414 {
5415         m_map = map;
5416 }
5417
5418 MapVoxelManipulator::~MapVoxelManipulator()
5419 {
5420         /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5421                         <<std::endl;*/
5422 }
5423
5424 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5425 {
5426         TimeTaker timer1("emerge", &emerge_time);
5427
5428         // Units of these are MapBlocks
5429         v3s16 p_min = getNodeBlockPos(a.MinEdge);
5430         v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5431
5432         VoxelArea block_area_nodes
5433                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5434
5435         addArea(block_area_nodes);
5436
5437         for(s32 z=p_min.Z; z<=p_max.Z; z++)
5438         for(s32 y=p_min.Y; y<=p_max.Y; y++)
5439         for(s32 x=p_min.X; x<=p_max.X; x++)
5440         {
5441                 v3s16 p(x,y,z);
5442                 core::map<v3s16, bool>::Node *n;
5443                 n = m_loaded_blocks.find(p);
5444                 if(n != NULL)
5445                         continue;
5446                 
5447                 bool block_data_inexistent = false;
5448                 try
5449                 {
5450                         TimeTaker timer1("emerge load", &emerge_load_time);
5451
5452                         /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5453                                         <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5454                                         <<" wanted area: ";
5455                         a.print(dstream);
5456                         dstream<<std::endl;*/
5457                         
5458                         MapBlock *block = m_map->getBlockNoCreate(p);
5459                         if(block->isDummy())
5460                                 block_data_inexistent = true;
5461                         else
5462                                 block->copyTo(*this);
5463                 }
5464                 catch(InvalidPositionException &e)
5465                 {
5466                         block_data_inexistent = true;
5467                 }
5468
5469                 if(block_data_inexistent)
5470                 {
5471                         VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5472                         // Fill with VOXELFLAG_INEXISTENT
5473                         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5474                         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5475                         {
5476                                 s32 i = m_area.index(a.MinEdge.X,y,z);
5477                                 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5478                         }
5479                 }
5480
5481                 m_loaded_blocks.insert(p, !block_data_inexistent);
5482         }
5483
5484         //dstream<<"emerge done"<<std::endl;
5485 }
5486
5487 /*
5488         SUGG: Add an option to only update eg. water and air nodes.
5489               This will make it interfere less with important stuff if
5490                   run on background.
5491 */
5492 void MapVoxelManipulator::blitBack
5493                 (core::map<v3s16, MapBlock*> & modified_blocks)
5494 {
5495         if(m_area.getExtent() == v3s16(0,0,0))
5496                 return;
5497         
5498         //TimeTaker timer1("blitBack");
5499
5500         /*dstream<<"blitBack(): m_loaded_blocks.size()="
5501                         <<m_loaded_blocks.size()<<std::endl;*/
5502         
5503         /*
5504                 Initialize block cache
5505         */
5506         v3s16 blockpos_last;
5507         MapBlock *block = NULL;
5508         bool block_checked_in_modified = false;
5509
5510         for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5511         for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5512         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5513         {
5514                 v3s16 p(x,y,z);
5515
5516                 u8 f = m_flags[m_area.index(p)];
5517                 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5518                         continue;
5519
5520                 MapNode &n = m_data[m_area.index(p)];
5521                         
5522                 v3s16 blockpos = getNodeBlockPos(p);
5523                 
5524                 try
5525                 {
5526                         // Get block
5527                         if(block == NULL || blockpos != blockpos_last){
5528                                 block = m_map->getBlockNoCreate(blockpos);
5529                                 blockpos_last = blockpos;
5530                                 block_checked_in_modified = false;
5531                         }
5532                         
5533                         // Calculate relative position in block
5534                         v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5535
5536                         // Don't continue if nothing has changed here
5537                         if(block->getNode(relpos) == n)
5538                                 continue;
5539
5540                         //m_map->setNode(m_area.MinEdge + p, n);
5541                         block->setNode(relpos, n);
5542                         
5543                         /*
5544                                 Make sure block is in modified_blocks
5545                         */
5546                         if(block_checked_in_modified == false)
5547                         {
5548                                 modified_blocks[blockpos] = block;
5549                                 block_checked_in_modified = true;
5550                         }
5551                 }
5552                 catch(InvalidPositionException &e)
5553                 {
5554                 }
5555         }
5556 }
5557
5558 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5559                 MapVoxelManipulator(map)
5560 {
5561 }
5562
5563 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5564 {
5565 }
5566
5567 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5568 {
5569         // Just create the area so that it can be pointed to
5570         VoxelManipulator::emerge(a, caller_id);
5571 }
5572
5573 void ManualMapVoxelManipulator::initialEmerge(
5574                 v3s16 blockpos_min, v3s16 blockpos_max)
5575 {
5576         TimeTaker timer1("initialEmerge", &emerge_time);
5577
5578         // Units of these are MapBlocks
5579         v3s16 p_min = blockpos_min;
5580         v3s16 p_max = blockpos_max;
5581
5582         VoxelArea block_area_nodes
5583                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5584         
5585         u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5586         if(size_MB >= 1)
5587         {
5588                 dstream<<"initialEmerge: area: ";
5589                 block_area_nodes.print(dstream);
5590                 dstream<<" ("<<size_MB<<"MB)";
5591                 dstream<<std::endl;
5592         }
5593
5594         addArea(block_area_nodes);
5595
5596         for(s32 z=p_min.Z; z<=p_max.Z; z++)
5597         for(s32 y=p_min.Y; y<=p_max.Y; y++)
5598         for(s32 x=p_min.X; x<=p_max.X; x++)
5599         {
5600                 v3s16 p(x,y,z);
5601                 core::map<v3s16, bool>::Node *n;
5602                 n = m_loaded_blocks.find(p);
5603                 if(n != NULL)
5604                         continue;
5605                 
5606                 bool block_data_inexistent = false;
5607                 try
5608                 {
5609                         TimeTaker timer1("emerge load", &emerge_load_time);
5610
5611                         MapBlock *block = m_map->getBlockNoCreate(p);
5612                         if(block->isDummy())
5613                                 block_data_inexistent = true;
5614                         else
5615                                 block->copyTo(*this);
5616                 }
5617                 catch(InvalidPositionException &e)
5618                 {
5619                         block_data_inexistent = true;
5620                 }
5621
5622                 if(block_data_inexistent)
5623                 {
5624                         /*
5625                                 Mark area inexistent
5626                         */
5627                         VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5628                         // Fill with VOXELFLAG_INEXISTENT
5629                         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5630                         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5631                         {
5632                                 s32 i = m_area.index(a.MinEdge.X,y,z);
5633                                 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5634                         }
5635                 }
5636
5637                 m_loaded_blocks.insert(p, !block_data_inexistent);
5638         }
5639 }
5640
5641 void ManualMapVoxelManipulator::blitBackAll(
5642                 core::map<v3s16, MapBlock*> * modified_blocks)
5643 {
5644         if(m_area.getExtent() == v3s16(0,0,0))
5645                 return;
5646         
5647         /*
5648                 Copy data of all blocks
5649         */
5650         for(core::map<v3s16, bool>::Iterator
5651                         i = m_loaded_blocks.getIterator();
5652                         i.atEnd() == false; i++)
5653         {
5654                 bool existed = i.getNode()->getValue();
5655                 if(existed == false)
5656                         continue;
5657                 v3s16 p = i.getNode()->getKey();
5658                 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5659                 if(block == NULL)
5660                 {
5661                         dstream<<"WARNING: "<<__FUNCTION_NAME
5662                                         <<": got NULL block "
5663                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5664                                         <<std::endl;
5665                         continue;
5666                 }
5667
5668                 block->copyFrom(*this);
5669
5670                 if(modified_blocks)
5671                         modified_blocks->insert(p, block);
5672         }
5673 }
5674
5675 //END