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