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