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