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