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