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