]> git.lizzy.rs Git - dragonfireclient.git/blob - src/map.cpp
0b5872e05e3c53cae49f28ba74fde447333fd15f
[dragonfireclient.git] / src / map.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "map.h"
21 #include "main.h"
22 #include "jmutexautolock.h"
23 #include "client.h"
24 #include "filesys.h"
25 #include "utility.h"
26 #include "voxel.h"
27 #include "porting.h"
28
29 /*
30         Map
31 */
32
33 Map::Map(std::ostream &dout):
34         m_dout(dout),
35         m_camera_position(0,0,0),
36         m_camera_direction(0,0,1),
37         m_sector_cache(NULL),
38         m_hwrapper(this)
39 {
40         m_sector_mutex.Init();
41         m_camera_mutex.Init();
42         assert(m_sector_mutex.IsInitialized());
43         assert(m_camera_mutex.IsInitialized());
44         
45         // Get this so that the player can stay on it at first
46         //getSector(v2s16(0,0));
47 }
48
49 Map::~Map()
50 {
51         /*
52                 Stop updater thread
53         */
54         /*updater.setRun(false);
55         while(updater.IsRunning())
56                 sleep_s(1);*/
57
58         /*
59                 Free all MapSectors.
60         */
61         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
62         for(; i.atEnd() == false; i++)
63         {
64                 MapSector *sector = i.getNode()->getValue();
65                 delete sector;
66         }
67 }
68
69 MapSector * Map::getSectorNoGenerate(v2s16 p)
70 {
71         JMutexAutoLock lock(m_sector_mutex);
72
73         if(m_sector_cache != NULL && p == m_sector_cache_p){
74                 MapSector * sector = m_sector_cache;
75                 // Reset inactivity timer
76                 sector->usage_timer = 0.0;
77                 return sector;
78         }
79         
80         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
81         // If sector doesn't exist, throw an exception
82         if(n == NULL)
83         {
84                 throw InvalidPositionException();
85         }
86         
87         MapSector *sector = n->getValue();
88         
89         // Cache the last result
90         m_sector_cache_p = p;
91         m_sector_cache = sector;
92
93         //MapSector * ref(sector);
94         
95         // Reset inactivity timer
96         sector->usage_timer = 0.0;
97         return sector;
98 }
99
100 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
101 {       
102         v2s16 p2d(p3d.X, p3d.Z);
103         MapSector * sector = getSectorNoGenerate(p2d);
104
105         MapBlock *block = sector->getBlockNoCreate(p3d.Y);
106
107         return block;
108 }
109
110 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
111 {
112         try
113         {
114                 v2s16 p2d(p3d.X, p3d.Z);
115                 MapSector * sector = getSectorNoGenerate(p2d);
116                 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
117                 return block;
118         }
119         catch(InvalidPositionException &e)
120         {
121                 return NULL;
122         }
123 }
124
125 f32 Map::getGroundHeight(v2s16 p, bool generate)
126 {
127         try{
128                 v2s16 sectorpos = getNodeSectorPos(p);
129                 MapSector * sref = getSectorNoGenerate(sectorpos);
130                 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
131                 f32 y = sref->getGroundHeight(relpos);
132                 return y;
133         }
134         catch(InvalidPositionException &e)
135         {
136                 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
137         }
138 }
139
140 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
141 {
142         /*m_dout<<DTIME<<"Map::setGroundHeight(("
143                         <<p.X<<","<<p.Y
144                         <<"), "<<y<<")"<<std::endl;*/
145         v2s16 sectorpos = getNodeSectorPos(p);
146         MapSector * sref = getSectorNoGenerate(sectorpos);
147         v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
148         //sref->mutex.Lock();
149         sref->setGroundHeight(relpos, y);
150         //sref->mutex.Unlock();
151 }
152
153 bool Map::isNodeUnderground(v3s16 p)
154 {
155         v3s16 blockpos = getNodeBlockPos(p);
156         try{
157                 MapBlock * block = getBlockNoCreate(blockpos);
158                 return block->getIsUnderground();
159         }
160         catch(InvalidPositionException &e)
161         {
162                 return false;
163         }
164 }
165
166 /*
167         Goes recursively through the neighbours of the node.
168
169         Alters only transparent nodes.
170
171         If the lighting of the neighbour is lower than the lighting of
172         the node was (before changing it to 0 at the step before), the
173         lighting of the neighbour is set to 0 and then the same stuff
174         repeats for the neighbour.
175
176         The ending nodes of the routine are stored in light_sources.
177         This is useful when a light is removed. In such case, this
178         routine can be called for the light node and then again for
179         light_sources to re-light the area without the removed light.
180
181         values of from_nodes are lighting values.
182 */
183 void Map::unspreadLight(enum LightBank bank,
184                 core::map<v3s16, u8> & from_nodes,
185                 core::map<v3s16, bool> & light_sources,
186                 core::map<v3s16, MapBlock*>  & modified_blocks)
187 {
188         v3s16 dirs[6] = {
189                 v3s16(0,0,1), // back
190                 v3s16(0,1,0), // top
191                 v3s16(1,0,0), // right
192                 v3s16(0,0,-1), // front
193                 v3s16(0,-1,0), // bottom
194                 v3s16(-1,0,0), // left
195         };
196         
197         if(from_nodes.size() == 0)
198                 return;
199         
200         u32 blockchangecount = 0;
201
202         core::map<v3s16, u8> unlighted_nodes;
203         core::map<v3s16, u8>::Iterator j;
204         j = from_nodes.getIterator();
205
206         /*
207                 Initialize block cache
208         */
209         v3s16 blockpos_last;
210         MapBlock *block = NULL;
211         // Cache this a bit, too
212         bool block_checked_in_modified = false;
213         
214         for(; j.atEnd() == false; j++)
215         {
216                 v3s16 pos = j.getNode()->getKey();
217                 v3s16 blockpos = getNodeBlockPos(pos);
218                 
219                 // Only fetch a new block if the block position has changed
220                 try{
221                         if(block == NULL || blockpos != blockpos_last){
222                                 block = getBlockNoCreate(blockpos);
223                                 blockpos_last = blockpos;
224
225                                 block_checked_in_modified = false;
226                                 blockchangecount++;
227                         }
228                 }
229                 catch(InvalidPositionException &e)
230                 {
231                         continue;
232                 }
233
234                 if(block->isDummy())
235                         continue;
236
237                 // Calculate relative position in block
238                 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
239
240                 // Get node straight from the block
241                 MapNode n = block->getNode(relpos);
242                 
243                 u8 oldlight = j.getNode()->getValue();
244                 
245                 // Loop through 6 neighbors
246                 for(u16 i=0; i<6; i++)
247                 {
248                         // Get the position of the neighbor node
249                         v3s16 n2pos = pos + dirs[i];
250                         
251                         // Get the block where the node is located
252                         v3s16 blockpos = getNodeBlockPos(n2pos);
253
254                         try
255                         {
256                                 // Only fetch a new block if the block position has changed
257                                 try{
258                                         if(block == NULL || blockpos != blockpos_last){
259                                                 block = getBlockNoCreate(blockpos);
260                                                 blockpos_last = blockpos;
261
262                                                 block_checked_in_modified = false;
263                                                 blockchangecount++;
264                                         }
265                                 }
266                                 catch(InvalidPositionException &e)
267                                 {
268                                         continue;
269                                 }
270                                 
271                                 // Calculate relative position in block
272                                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
273                                 // Get node straight from the block
274                                 MapNode n2 = block->getNode(relpos);
275                                 
276                                 bool changed = false;
277
278                                 //TODO: Optimize output by optimizing light_sources?
279
280                                 /*
281                                         If the neighbor is dimmer than what was specified
282                                         as oldlight (the light of the previous node)
283                                 */
284                                 if(n2.getLight(bank) < oldlight)
285                                 {
286                                         /*
287                                                 And the neighbor is transparent and it has some light
288                                         */
289                                         if(n2.light_propagates() && n2.getLight(bank) != 0)
290                                         {
291                                                 /*
292                                                         Set light to 0 and add to queue
293                                                 */
294
295                                                 u8 current_light = n2.getLight(bank);
296                                                 n2.setLight(bank, 0);
297                                                 block->setNode(relpos, n2);
298
299                                                 unlighted_nodes.insert(n2pos, current_light);
300                                                 changed = true;
301
302                                                 /*
303                                                         Remove from light_sources if it is there
304                                                         NOTE: This doesn't happen nearly at all
305                                                 */
306                                                 /*if(light_sources.find(n2pos))
307                                                 {
308                                                         std::cout<<"Removed from light_sources"<<std::endl;
309                                                         light_sources.remove(n2pos);
310                                                 }*/
311                                         }
312                                         
313                                         /*// DEBUG
314                                         if(light_sources.find(n2pos) != NULL)
315                                                 light_sources.remove(n2pos);*/
316                                 }
317                                 else{
318                                         light_sources.insert(n2pos, true);
319                                 }
320
321                                 // Add to modified_blocks
322                                 if(changed == true && block_checked_in_modified == false)
323                                 {
324                                         // If the block is not found in modified_blocks, add.
325                                         if(modified_blocks.find(blockpos) == NULL)
326                                         {
327                                                 modified_blocks.insert(blockpos, block);
328                                         }
329                                         block_checked_in_modified = true;
330                                 }
331                         }
332                         catch(InvalidPositionException &e)
333                         {
334                                 continue;
335                         }
336                 }
337         }
338
339         /*dstream<<"unspreadLight(): Changed block "
340                         <<blockchangecount<<" times"
341                         <<" for "<<from_nodes.size()<<" nodes"
342                         <<std::endl;*/
343         
344         if(unlighted_nodes.size() > 0)
345                 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
346 }
347
348 /*
349         A single-node wrapper of the above
350 */
351 void Map::unLightNeighbors(enum LightBank bank,
352                 v3s16 pos, u8 lightwas,
353                 core::map<v3s16, bool> & light_sources,
354                 core::map<v3s16, MapBlock*>  & modified_blocks)
355 {
356         core::map<v3s16, u8> from_nodes;
357         from_nodes.insert(pos, lightwas);
358
359         unspreadLight(bank, from_nodes, light_sources, modified_blocks);
360 }
361
362 /*
363         Lights neighbors of from_nodes, collects all them and then
364         goes on recursively.
365 */
366 void Map::spreadLight(enum LightBank bank,
367                 core::map<v3s16, bool> & from_nodes,
368                 core::map<v3s16, MapBlock*> & modified_blocks)
369 {
370         const v3s16 dirs[6] = {
371                 v3s16(0,0,1), // back
372                 v3s16(0,1,0), // top
373                 v3s16(1,0,0), // right
374                 v3s16(0,0,-1), // front
375                 v3s16(0,-1,0), // bottom
376                 v3s16(-1,0,0), // left
377         };
378
379         if(from_nodes.size() == 0)
380                 return;
381         
382         u32 blockchangecount = 0;
383
384         core::map<v3s16, bool> lighted_nodes;
385         core::map<v3s16, bool>::Iterator j;
386         j = from_nodes.getIterator();
387
388         /*
389                 Initialize block cache
390         */
391         v3s16 blockpos_last;
392         MapBlock *block = NULL;
393         // Cache this a bit, too
394         bool block_checked_in_modified = false;
395         
396         for(; j.atEnd() == false; j++)
397         //for(; j != from_nodes.end(); j++)
398         {
399                 v3s16 pos = j.getNode()->getKey();
400                 //v3s16 pos = *j;
401                 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
402                 v3s16 blockpos = getNodeBlockPos(pos);
403                 
404                 // Only fetch a new block if the block position has changed
405                 try{
406                         if(block == NULL || blockpos != blockpos_last){
407                                 block = getBlockNoCreate(blockpos);
408                                 blockpos_last = blockpos;
409
410                                 block_checked_in_modified = false;
411                                 blockchangecount++;
412                         }
413                 }
414                 catch(InvalidPositionException &e)
415                 {
416                         continue;
417                 }
418
419                 if(block->isDummy())
420                         continue;
421
422                 // Calculate relative position in block
423                 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
424
425                 // Get node straight from the block
426                 MapNode n = block->getNode(relpos);
427
428                 u8 oldlight = n.getLight(bank);
429                 u8 newlight = diminish_light(oldlight);
430
431                 // Loop through 6 neighbors
432                 for(u16 i=0; i<6; i++){
433                         // Get the position of the neighbor node
434                         v3s16 n2pos = pos + dirs[i];
435                         
436                         // Get the block where the node is located
437                         v3s16 blockpos = getNodeBlockPos(n2pos);
438
439                         try
440                         {
441                                 // Only fetch a new block if the block position has changed
442                                 try{
443                                         if(block == NULL || blockpos != blockpos_last){
444                                                 block = getBlockNoCreate(blockpos);
445                                                 blockpos_last = blockpos;
446
447                                                 block_checked_in_modified = false;
448                                                 blockchangecount++;
449                                         }
450                                 }
451                                 catch(InvalidPositionException &e)
452                                 {
453                                         continue;
454                                 }
455                                 
456                                 // Calculate relative position in block
457                                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
458                                 // Get node straight from the block
459                                 MapNode n2 = block->getNode(relpos);
460                                 
461                                 bool changed = false;
462                                 /*
463                                         If the neighbor is brighter than the current node,
464                                         add to list (it will light up this node on its turn)
465                                 */
466                                 if(n2.getLight(bank) > undiminish_light(oldlight))
467                                 {
468                                         lighted_nodes.insert(n2pos, true);
469                                         //lighted_nodes.push_back(n2pos);
470                                         changed = true;
471                                 }
472                                 /*
473                                         If the neighbor is dimmer than how much light this node
474                                         would spread on it, add to list
475                                 */
476                                 if(n2.getLight(bank) < newlight)
477                                 {
478                                         if(n2.light_propagates())
479                                         {
480                                                 n2.setLight(bank, newlight);
481                                                 block->setNode(relpos, n2);
482                                                 lighted_nodes.insert(n2pos, true);
483                                                 //lighted_nodes.push_back(n2pos);
484                                                 changed = true;
485                                         }
486                                 }
487
488                                 // Add to modified_blocks
489                                 if(changed == true && block_checked_in_modified == false)
490                                 {
491                                         // If the block is not found in modified_blocks, add.
492                                         if(modified_blocks.find(blockpos) == NULL)
493                                         {
494                                                 modified_blocks.insert(blockpos, block);
495                                         }
496                                         block_checked_in_modified = true;
497                                 }
498                         }
499                         catch(InvalidPositionException &e)
500                         {
501                                 continue;
502                         }
503                 }
504         }
505
506         /*dstream<<"spreadLight(): Changed block "
507                         <<blockchangecount<<" times"
508                         <<" for "<<from_nodes.size()<<" nodes"
509                         <<std::endl;*/
510         
511         if(lighted_nodes.size() > 0)
512                 spreadLight(bank, lighted_nodes, modified_blocks);
513 }
514
515 /*
516         A single-node source variation of the above.
517 */
518 void Map::lightNeighbors(enum LightBank bank,
519                 v3s16 pos,
520                 core::map<v3s16, MapBlock*> & modified_blocks)
521 {
522         core::map<v3s16, bool> from_nodes;
523         from_nodes.insert(pos, true);
524         spreadLight(bank, from_nodes, modified_blocks);
525 }
526
527 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
528 {
529         v3s16 dirs[6] = {
530                 v3s16(0,0,1), // back
531                 v3s16(0,1,0), // top
532                 v3s16(1,0,0), // right
533                 v3s16(0,0,-1), // front
534                 v3s16(0,-1,0), // bottom
535                 v3s16(-1,0,0), // left
536         };
537         
538         u8 brightest_light = 0;
539         v3s16 brightest_pos(0,0,0);
540         bool found_something = false;
541
542         // Loop through 6 neighbors
543         for(u16 i=0; i<6; i++){
544                 // Get the position of the neighbor node
545                 v3s16 n2pos = p + dirs[i];
546                 MapNode n2;
547                 try{
548                         n2 = getNode(n2pos);
549                 }
550                 catch(InvalidPositionException &e)
551                 {
552                         continue;
553                 }
554                 if(n2.getLight(bank) > brightest_light || found_something == false){
555                         brightest_light = n2.getLight(bank);
556                         brightest_pos = n2pos;
557                         found_something = true;
558                 }
559         }
560
561         if(found_something == false)
562                 throw InvalidPositionException();
563                 
564         return brightest_pos;
565 }
566
567 /*
568         Propagates sunlight down from a node.
569         Starting point gets sunlight.
570
571         Returns the lowest y value of where the sunlight went.
572
573         Mud is turned into grass in where the sunlight stops.
574 */
575 s16 Map::propagateSunlight(v3s16 start,
576                 core::map<v3s16, MapBlock*> & modified_blocks)
577 {
578         s16 y = start.Y;
579         for(; ; y--)
580         {
581                 v3s16 pos(start.X, y, start.Z);
582                 
583                 v3s16 blockpos = getNodeBlockPos(pos);
584                 MapBlock *block;
585                 try{
586                         block = getBlockNoCreate(blockpos);
587                 }
588                 catch(InvalidPositionException &e)
589                 {
590                         break;
591                 }
592
593                 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
594                 MapNode n = block->getNode(relpos);
595
596                 if(n.sunlight_propagates())
597                 {
598                         n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
599                         block->setNode(relpos, n);
600
601                         modified_blocks.insert(blockpos, block);
602                 }
603                 else
604                 {
605                         // Turn mud into grass
606                         if(n.d == CONTENT_MUD)
607                         {
608                                 n.d = CONTENT_GRASS;
609                                 block->setNode(relpos, n);
610                                 modified_blocks.insert(blockpos, block);
611                         }
612
613                         // Sunlight goes no further
614                         break;
615                 }
616         }
617         return y + 1;
618 }
619
620 void Map::updateLighting(enum LightBank bank,
621                 core::map<v3s16, MapBlock*> & a_blocks,
622                 core::map<v3s16, MapBlock*> & modified_blocks)
623 {
624         /*m_dout<<DTIME<<"Map::updateLighting(): "
625                         <<a_blocks.size()<<" blocks."<<std::endl;*/
626         
627         //TimeTaker timer("updateLighting");
628         
629         // For debugging
630         bool debug=true;
631
632         u32 count_was = modified_blocks.size();
633
634         core::map<v3s16, bool> light_sources;
635         
636         core::map<v3s16, u8> unlight_from;
637                 
638         core::map<v3s16, MapBlock*>::Iterator i;
639         i = a_blocks.getIterator();
640         for(; i.atEnd() == false; i++)
641         {
642                 MapBlock *block = i.getNode()->getValue();
643                 
644                 for(;;)
645                 {
646                         // Don't bother with dummy blocks.
647                         if(block->isDummy())
648                                 break;
649                 
650                         v3s16 pos = block->getPos();
651                         modified_blocks.insert(pos, block);
652
653                         /*
654                                 Clear all light from block
655                         */
656                         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
657                         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
658                         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
659                         {
660                                 
661                                 try{
662                                         v3s16 p(x,y,z);
663                                         MapNode n = block->getNode(v3s16(x,y,z));
664                                         u8 oldlight = n.getLight(bank);
665                                         n.setLight(bank, 0);
666                                         block->setNode(v3s16(x,y,z), n);
667                                         
668                                         // Collect borders for unlighting
669                                         if(x==0 || x == MAP_BLOCKSIZE-1
670                                         || y==0 || y == MAP_BLOCKSIZE-1
671                                         || z==0 || z == MAP_BLOCKSIZE-1)
672                                         {
673                                                 v3s16 p_map = p + v3s16(
674                                                                 MAP_BLOCKSIZE*pos.X,
675                                                                 MAP_BLOCKSIZE*pos.Y,
676                                                                 MAP_BLOCKSIZE*pos.Z);
677                                                 unlight_from.insert(p_map, oldlight);
678                                         }
679                                 }
680                                 catch(InvalidPositionException &e)
681                                 {
682                                         /*
683                                                 This would happen when dealing with a
684                                                 dummy block.
685                                         */
686                                         //assert(0);
687                                         dstream<<"updateLighting(): InvalidPositionException"
688                                                         <<std::endl;
689                                 }
690                         }
691                         
692                         if(bank == LIGHTBANK_DAY)
693                         {
694                                 bool bottom_valid = block->propagateSunlight(light_sources);
695
696                                 // If bottom is valid, we're done.
697                                 if(bottom_valid)
698                                         break;
699                         }
700                         else if(bank == LIGHTBANK_NIGHT)
701                         {
702                                 break;
703                         }
704                         else
705                         {
706                                 assert(0);
707                         }
708                                 
709                         /*dstream<<"Bottom for sunlight-propagated block ("
710                                         <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
711                                         <<std::endl;*/
712
713                         // Else get the block below and loop to it
714
715                         pos.Y--;
716                         try{
717                                 block = getBlockNoCreate(pos);
718                         }
719                         catch(InvalidPositionException &e)
720                         {
721                                 assert(0);
722                         }
723                         
724                 }
725         }
726
727 #if 0
728         {
729                 TimeTaker timer("unspreadLight");
730                 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
731         }
732         
733         if(debug)
734         {
735                 u32 diff = modified_blocks.size() - count_was;
736                 count_was = modified_blocks.size();
737                 dstream<<"unspreadLight modified "<<diff<<std::endl;
738         }
739
740         // TODO: Spread light from propagated sunlight?
741         // Yes, add it to light_sources... somehow.
742         // It has to be added at somewhere above, in the loop.
743         // TODO
744         // NOTE: This actually works fine without doing so
745         //       - Find out why it works
746
747         {
748                 TimeTaker timer("spreadLight");
749                 spreadLight(bank, light_sources, modified_blocks);
750         }
751         
752         if(debug)
753         {
754                 u32 diff = modified_blocks.size() - count_was;
755                 count_was = modified_blocks.size();
756                 dstream<<"spreadLight modified "<<diff<<std::endl;
757         }
758 #endif
759         
760         {
761                 //MapVoxelManipulator vmanip(this);
762
763                 ManualMapVoxelManipulator vmanip(this);
764                 
765                 core::map<v3s16, MapBlock*>::Iterator i;
766                 i = a_blocks.getIterator();
767                 for(; i.atEnd() == false; i++)
768                 {
769                         MapBlock *block = i.getNode()->getValue();
770                         v3s16 p = block->getPos();
771                         vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
772                 }
773                 {
774                         //TimeTaker timer("unSpreadLight");
775                         vmanip.unspreadLight(bank, unlight_from, light_sources);
776                 }
777                 {
778                         //TimeTaker timer("spreadLight");
779                         vmanip.spreadLight(bank, light_sources);
780                 }
781                 {
782                         //TimeTaker timer("blitBack");
783                         vmanip.blitBack(modified_blocks);
784                 }
785                 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
786                 emerge_time = 0;*/
787         }
788
789         //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
790 }
791
792 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
793                 core::map<v3s16, MapBlock*> & modified_blocks)
794 {
795         updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
796         updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
797         
798         /*
799                 Update information about whether day and night light differ
800         */
801         for(core::map<v3s16, MapBlock*>::Iterator
802                         i = modified_blocks.getIterator();
803                         i.atEnd() == false; i++)
804         {
805                 MapBlock *block = i.getNode()->getValue();
806                 block->updateDayNightDiff();
807         }
808 }
809
810 /*
811         This is called after changing a node from transparent to opaque.
812         The lighting value of the node should be left as-is after changing
813         other values. This sets the lighting value to 0.
814 */
815 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
816                 core::map<v3s16, MapBlock*> &modified_blocks)*/
817 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
818                 core::map<v3s16, MapBlock*> &modified_blocks)
819 {
820         /*PrintInfo(m_dout);
821         m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
822                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
823
824         /*
825                 From this node to nodes underneath:
826                 If lighting is sunlight (1.0), unlight neighbours and
827                 set lighting to 0.
828                 Else discontinue.
829         */
830
831         v3s16 toppos = p + v3s16(0,1,0);
832         v3s16 bottompos = p + v3s16(0,-1,0);
833
834         bool node_under_sunlight = true;
835         core::map<v3s16, bool> light_sources;
836
837         /*
838                 If there is a node at top and it doesn't have sunlight,
839                 there has not been any sunlight going down.
840
841                 Otherwise there probably is.
842         */
843         try{
844                 MapNode topnode = getNode(toppos);
845
846                 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
847                         node_under_sunlight = false;
848         }
849         catch(InvalidPositionException &e)
850         {
851         }
852         
853         if(n.d != CONTENT_TORCH)
854         {
855                 /*
856                         If there is grass below, change it to mud
857                 */
858                 try{
859                         MapNode bottomnode = getNode(bottompos);
860                         
861                         if(bottomnode.d == CONTENT_GRASS
862                                         || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
863                         {
864                                 bottomnode.d = CONTENT_MUD;
865                                 setNode(bottompos, bottomnode);
866                         }
867                 }
868                 catch(InvalidPositionException &e)
869                 {
870                 }
871         }
872
873         enum LightBank banks[] =
874         {
875                 LIGHTBANK_DAY,
876                 LIGHTBANK_NIGHT
877         };
878         for(s32 i=0; i<2; i++)
879         {
880                 enum LightBank bank = banks[i];
881
882                 u8 lightwas = getNode(p).getLight(bank);
883
884                 // Add the block of the added node to modified_blocks
885                 v3s16 blockpos = getNodeBlockPos(p);
886                 MapBlock * block = getBlockNoCreate(blockpos);
887                 assert(block != NULL);
888                 modified_blocks.insert(blockpos, block);
889                 
890                 if(isValidPosition(p) == false)
891                         throw;
892                         
893                 // Unlight neighbours of node.
894                 // This means setting light of all consequent dimmer nodes
895                 // to 0.
896                 // This also collects the nodes at the border which will spread
897                 // light again into this.
898                 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
899
900                 n.setLight(bank, 0);
901         }
902         
903         setNode(p, n);
904         
905         /*
906                 If node is under sunlight, take all sunlighted nodes under
907                 it and clear light from them and from where the light has
908                 been spread.
909                 TODO: This could be optimized by mass-unlighting instead
910                       of looping
911         */
912         if(node_under_sunlight)
913         {
914                 s16 y = p.Y - 1;
915                 for(;; y--){
916                         //m_dout<<DTIME<<"y="<<y<<std::endl;
917                         v3s16 n2pos(p.X, y, p.Z);
918                         
919                         MapNode n2;
920                         try{
921                                 n2 = getNode(n2pos);
922                         }
923                         catch(InvalidPositionException &e)
924                         {
925                                 break;
926                         }
927
928                         if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
929                         {
930                                 //m_dout<<DTIME<<"doing"<<std::endl;
931                                 unLightNeighbors(LIGHTBANK_DAY,
932                                                 n2pos, n2.getLight(LIGHTBANK_DAY),
933                                                 light_sources, modified_blocks);
934                                 n2.setLight(LIGHTBANK_DAY, 0);
935                                 setNode(n2pos, n2);
936                         }
937                         else
938                                 break;
939                 }
940         }
941         
942         for(s32 i=0; i<2; i++)
943         {
944                 enum LightBank bank = banks[i];
945                 
946                 /*
947                         Spread light from all nodes that might be capable of doing so
948                         TODO: Convert to spreadLight
949                 */
950                 spreadLight(bank, light_sources, modified_blocks);
951         }
952
953         /*
954                 Update information about whether day and night light differ
955         */
956         for(core::map<v3s16, MapBlock*>::Iterator
957                         i = modified_blocks.getIterator();
958                         i.atEnd() == false; i++)
959         {
960                 MapBlock *block = i.getNode()->getValue();
961                 block->updateDayNightDiff();
962         }
963
964         /*
965                 Add neighboring liquid nodes and the node itself if it is
966                 liquid (=water node was added) to transform queue.
967         */
968         v3s16 dirs[7] = {
969                 v3s16(0,0,0), // self
970                 v3s16(0,0,1), // back
971                 v3s16(0,1,0), // top
972                 v3s16(1,0,0), // right
973                 v3s16(0,0,-1), // front
974                 v3s16(0,-1,0), // bottom
975                 v3s16(-1,0,0), // left
976         };
977         for(u16 i=0; i<7; i++)
978         {
979                 try
980                 {
981
982                 v3s16 p2 = p + dirs[i];
983                 
984                 MapNode n2 = getNode(p2);
985                 if(content_liquid(n2.d))
986                 {
987                         m_transforming_liquid.push_back(p2);
988                 }
989                 
990                 }catch(InvalidPositionException &e)
991                 {
992                 }
993         }
994 }
995
996 /*
997 */
998 void Map::removeNodeAndUpdate(v3s16 p,
999                 core::map<v3s16, MapBlock*> &modified_blocks)
1000 {
1001         /*PrintInfo(m_dout);
1002         m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1003                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1004         
1005         bool node_under_sunlight = true;
1006         
1007         v3s16 toppos = p + v3s16(0,1,0);
1008
1009         // Node will be replaced with this
1010         u8 replace_material = CONTENT_AIR;
1011         
1012         /*
1013                 If there is a node at top and it doesn't have sunlight,
1014                 there will be no sunlight going down.
1015         */
1016         try{
1017                 MapNode topnode = getNode(toppos);
1018
1019                 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1020                         node_under_sunlight = false;
1021         }
1022         catch(InvalidPositionException &e)
1023         {
1024         }
1025
1026         core::map<v3s16, bool> light_sources;
1027
1028         enum LightBank banks[] =
1029         {
1030                 LIGHTBANK_DAY,
1031                 LIGHTBANK_NIGHT
1032         };
1033         for(s32 i=0; i<2; i++)
1034         {
1035                 enum LightBank bank = banks[i];
1036         
1037                 /*
1038                         Unlight neighbors (in case the node is a light source)
1039                 */
1040                 unLightNeighbors(bank, p,
1041                                 getNode(p).getLight(bank),
1042                                 light_sources, modified_blocks);
1043         }
1044
1045         /*
1046                 Remove the node.
1047                 This also clears the lighting.
1048         */
1049
1050         MapNode n;
1051         n.d = replace_material;
1052         setNode(p, n);
1053         
1054         for(s32 i=0; i<2; i++)
1055         {
1056                 enum LightBank bank = banks[i];
1057         
1058                 /*
1059                         Recalculate lighting
1060                 */
1061                 spreadLight(bank, light_sources, modified_blocks);
1062         }
1063
1064         // Add the block of the removed node to modified_blocks
1065         v3s16 blockpos = getNodeBlockPos(p);
1066         MapBlock * block = getBlockNoCreate(blockpos);
1067         assert(block != NULL);
1068         modified_blocks.insert(blockpos, block);
1069
1070         /*
1071                 If the removed node was under sunlight, propagate the
1072                 sunlight down from it and then light all neighbors
1073                 of the propagated blocks.
1074         */
1075         if(node_under_sunlight)
1076         {
1077                 s16 ybottom = propagateSunlight(p, modified_blocks);
1078                 /*m_dout<<DTIME<<"Node was under sunlight. "
1079                                 "Propagating sunlight";
1080                 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1081                 s16 y = p.Y;
1082                 for(; y >= ybottom; y--)
1083                 {
1084                         v3s16 p2(p.X, y, p.Z);
1085                         /*m_dout<<DTIME<<"lighting neighbors of node ("
1086                                         <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1087                                         <<std::endl;*/
1088                         lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1089                 }
1090         }
1091         else
1092         {
1093                 // Set the lighting of this node to 0
1094                 // TODO: Is this needed? Lighting is cleared up there already.
1095                 try{
1096                         MapNode n = getNode(p);
1097                         n.setLight(LIGHTBANK_DAY, 0);
1098                         setNode(p, n);
1099                 }
1100                 catch(InvalidPositionException &e)
1101                 {
1102                         throw;
1103                 }
1104         }
1105
1106         for(s32 i=0; i<2; i++)
1107         {
1108                 enum LightBank bank = banks[i];
1109         
1110                 // Get the brightest neighbour node and propagate light from it
1111                 v3s16 n2p = getBrightestNeighbour(bank, p);
1112                 try{
1113                         MapNode n2 = getNode(n2p);
1114                         lightNeighbors(bank, n2p, modified_blocks);
1115                 }
1116                 catch(InvalidPositionException &e)
1117                 {
1118                 }
1119         }
1120
1121         /*
1122                 Update information about whether day and night light differ
1123         */
1124         for(core::map<v3s16, MapBlock*>::Iterator
1125                         i = modified_blocks.getIterator();
1126                         i.atEnd() == false; i++)
1127         {
1128                 MapBlock *block = i.getNode()->getValue();
1129                 block->updateDayNightDiff();
1130         }
1131
1132         /*
1133                 Add neighboring liquid nodes to transform queue.
1134         */
1135         v3s16 dirs[6] = {
1136                 v3s16(0,0,1), // back
1137                 v3s16(0,1,0), // top
1138                 v3s16(1,0,0), // right
1139                 v3s16(0,0,-1), // front
1140                 v3s16(0,-1,0), // bottom
1141                 v3s16(-1,0,0), // left
1142         };
1143         for(u16 i=0; i<6; i++)
1144         {
1145                 try
1146                 {
1147
1148                 v3s16 p2 = p + dirs[i];
1149                 
1150                 MapNode n2 = getNode(p2);
1151                 if(content_liquid(n2.d))
1152                 {
1153                         m_transforming_liquid.push_back(p2);
1154                 }
1155                 
1156                 }catch(InvalidPositionException &e)
1157                 {
1158                 }
1159         }
1160 }
1161
1162 #ifndef SERVER
1163 void Map::expireMeshes(bool only_daynight_diffed)
1164 {
1165         TimeTaker timer("expireMeshes()");
1166
1167         core::map<v2s16, MapSector*>::Iterator si;
1168         si = m_sectors.getIterator();
1169         for(; si.atEnd() == false; si++)
1170         {
1171                 MapSector *sector = si.getNode()->getValue();
1172
1173                 core::list< MapBlock * > sectorblocks;
1174                 sector->getBlocks(sectorblocks);
1175                 
1176                 core::list< MapBlock * >::Iterator i;
1177                 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1178                 {
1179                         MapBlock *block = *i;
1180
1181                         if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1182                         {
1183                                 continue;
1184                         }
1185                         
1186                         {
1187                                 JMutexAutoLock lock(block->mesh_mutex);
1188                                 if(block->mesh != NULL)
1189                                 {
1190                                         /*block->mesh->drop();
1191                                         block->mesh = NULL;*/
1192                                         block->setMeshExpired(true);
1193                                 }
1194                         }
1195                 }
1196         }
1197 }
1198
1199 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1200 {
1201         assert(mapType() == MAPTYPE_CLIENT);
1202
1203         try{
1204                 v3s16 p = blockpos + v3s16(0,0,0);
1205                 MapBlock *b = getBlockNoCreate(p);
1206                 b->updateMesh(daynight_ratio);
1207         }
1208         catch(InvalidPositionException &e){}
1209         // Leading edge
1210         try{
1211                 v3s16 p = blockpos + v3s16(-1,0,0);
1212                 MapBlock *b = getBlockNoCreate(p);
1213                 b->updateMesh(daynight_ratio);
1214         }
1215         catch(InvalidPositionException &e){}
1216         try{
1217                 v3s16 p = blockpos + v3s16(0,-1,0);
1218                 MapBlock *b = getBlockNoCreate(p);
1219                 b->updateMesh(daynight_ratio);
1220         }
1221         catch(InvalidPositionException &e){}
1222         try{
1223                 v3s16 p = blockpos + v3s16(0,0,-1);
1224                 MapBlock *b = getBlockNoCreate(p);
1225                 b->updateMesh(daynight_ratio);
1226         }
1227         catch(InvalidPositionException &e){}
1228         /*// Trailing edge
1229         try{
1230                 v3s16 p = blockpos + v3s16(1,0,0);
1231                 MapBlock *b = getBlockNoCreate(p);
1232                 b->updateMesh(daynight_ratio);
1233         }
1234         catch(InvalidPositionException &e){}
1235         try{
1236                 v3s16 p = blockpos + v3s16(0,1,0);
1237                 MapBlock *b = getBlockNoCreate(p);
1238                 b->updateMesh(daynight_ratio);
1239         }
1240         catch(InvalidPositionException &e){}
1241         try{
1242                 v3s16 p = blockpos + v3s16(0,0,1);
1243                 MapBlock *b = getBlockNoCreate(p);
1244                 b->updateMesh(daynight_ratio);
1245         }
1246         catch(InvalidPositionException &e){}*/
1247 }
1248
1249 #endif
1250
1251 bool Map::dayNightDiffed(v3s16 blockpos)
1252 {
1253         try{
1254                 v3s16 p = blockpos + v3s16(0,0,0);
1255                 MapBlock *b = getBlockNoCreate(p);
1256                 if(b->dayNightDiffed())
1257                         return true;
1258         }
1259         catch(InvalidPositionException &e){}
1260         // Leading edges
1261         try{
1262                 v3s16 p = blockpos + v3s16(-1,0,0);
1263                 MapBlock *b = getBlockNoCreate(p);
1264                 if(b->dayNightDiffed())
1265                         return true;
1266         }
1267         catch(InvalidPositionException &e){}
1268         try{
1269                 v3s16 p = blockpos + v3s16(0,-1,0);
1270                 MapBlock *b = getBlockNoCreate(p);
1271                 if(b->dayNightDiffed())
1272                         return true;
1273         }
1274         catch(InvalidPositionException &e){}
1275         try{
1276                 v3s16 p = blockpos + v3s16(0,0,-1);
1277                 MapBlock *b = getBlockNoCreate(p);
1278                 if(b->dayNightDiffed())
1279                         return true;
1280         }
1281         catch(InvalidPositionException &e){}
1282         // Trailing edges
1283         try{
1284                 v3s16 p = blockpos + v3s16(1,0,0);
1285                 MapBlock *b = getBlockNoCreate(p);
1286                 if(b->dayNightDiffed())
1287                         return true;
1288         }
1289         catch(InvalidPositionException &e){}
1290         try{
1291                 v3s16 p = blockpos + v3s16(0,1,0);
1292                 MapBlock *b = getBlockNoCreate(p);
1293                 if(b->dayNightDiffed())
1294                         return true;
1295         }
1296         catch(InvalidPositionException &e){}
1297         try{
1298                 v3s16 p = blockpos + v3s16(0,0,1);
1299                 MapBlock *b = getBlockNoCreate(p);
1300                 if(b->dayNightDiffed())
1301                         return true;
1302         }
1303         catch(InvalidPositionException &e){}
1304
1305         return false;
1306 }
1307
1308 /*
1309         Updates usage timers
1310 */
1311 void Map::timerUpdate(float dtime)
1312 {
1313         JMutexAutoLock lock(m_sector_mutex);
1314
1315         core::map<v2s16, MapSector*>::Iterator si;
1316
1317         si = m_sectors.getIterator();
1318         for(; si.atEnd() == false; si++)
1319         {
1320                 MapSector *sector = si.getNode()->getValue();
1321                 sector->usage_timer += dtime;
1322         }
1323 }
1324
1325 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1326 {
1327         /*
1328                 Wait for caches to be removed before continuing.
1329                 
1330                 This disables the existence of caches while locked
1331         */
1332         //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1333
1334         core::list<v2s16>::Iterator j;
1335         for(j=list.begin(); j!=list.end(); j++)
1336         {
1337                 MapSector *sector = m_sectors[*j];
1338                 if(only_blocks)
1339                 {
1340                         sector->deleteBlocks();
1341                 }
1342                 else
1343                 {
1344                         /*
1345                                 If sector is in sector cache, remove it from there
1346                         */
1347                         if(m_sector_cache == sector)
1348                         {
1349                                 m_sector_cache = NULL;
1350                         }
1351                         /*
1352                                 Remove from map and delete
1353                         */
1354                         m_sectors.remove(*j);
1355                         delete sector;
1356                 }
1357         }
1358 }
1359
1360 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1361                 core::list<v3s16> *deleted_blocks)
1362 {
1363         JMutexAutoLock lock(m_sector_mutex);
1364
1365         core::list<v2s16> sector_deletion_queue;
1366         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1367         for(; i.atEnd() == false; i++)
1368         {
1369                 MapSector *sector = i.getNode()->getValue();
1370                 /*
1371                         Delete sector from memory if it hasn't been used in a long time
1372                 */
1373                 if(sector->usage_timer > timeout)
1374                 {
1375                         sector_deletion_queue.push_back(i.getNode()->getKey());
1376                         
1377                         if(deleted_blocks != NULL)
1378                         {
1379                                 // Collect positions of blocks of sector
1380                                 MapSector *sector = i.getNode()->getValue();
1381                                 core::list<MapBlock*> blocks;
1382                                 sector->getBlocks(blocks);
1383                                 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1384                                                 i != blocks.end(); i++)
1385                                 {
1386                                         deleted_blocks->push_back((*i)->getPos());
1387                                 }
1388                         }
1389                 }
1390         }
1391         deleteSectors(sector_deletion_queue, only_blocks);
1392         return sector_deletion_queue.getSize();
1393 }
1394
1395 void Map::PrintInfo(std::ostream &out)
1396 {
1397         out<<"Map: ";
1398 }
1399
1400 #define WATER_DROP_BOOST 4
1401
1402 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1403 {
1404         DSTACK(__FUNCTION_NAME);
1405         //TimeTaker timer("transformLiquids()");
1406
1407         u32 loopcount = 0;
1408         u32 initial_size = m_transforming_liquid.size();
1409
1410         while(m_transforming_liquid.size() != 0)
1411         {
1412                 /*
1413                         Get a queued transforming liquid node
1414                 */
1415                 v3s16 p0 = m_transforming_liquid.pop_front();
1416
1417                 MapNode n0 = getNode(p0);
1418                 
1419                 // Don't deal with non-liquids
1420                 if(content_liquid(n0.d) == false)
1421                         continue;
1422
1423                 bool is_source = !content_flowing_liquid(n0.d);
1424                 
1425                 u8 liquid_level = 8;
1426                 if(is_source == false)
1427                         liquid_level = n0.param2 & 0x0f;
1428                 
1429                 // Turn possible source into non-source
1430                 u8 nonsource_c = make_liquid_flowing(n0.d);
1431
1432                 /*
1433                         If not source, check that some node flows into this one
1434                         and what is the level of liquid in this one
1435                 */
1436                 if(is_source == false)
1437                 {
1438                         s8 new_liquid_level_max = -1;
1439
1440                         v3s16 dirs_from[5] = {
1441                                 v3s16(0,1,0), // top
1442                                 v3s16(0,0,1), // back
1443                                 v3s16(1,0,0), // right
1444                                 v3s16(0,0,-1), // front
1445                                 v3s16(-1,0,0), // left
1446                         };
1447                         for(u16 i=0; i<5; i++)
1448                         {
1449                                 try
1450                                 {
1451
1452                                 bool from_top = (i==0);
1453
1454                                 v3s16 p2 = p0 + dirs_from[i];
1455                                 MapNode n2 = getNode(p2);
1456
1457                                 if(content_liquid(n2.d))
1458                                 {
1459                                         u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1460                                         // Check that the liquids are the same type
1461                                         if(n2_nonsource_c != nonsource_c)
1462                                         {
1463                                                 dstream<<"WARNING: Not handling: different liquids"
1464                                                                 " collide"<<std::endl;
1465                                                 continue;
1466                                         }
1467                                         bool n2_is_source = !content_flowing_liquid(n2.d);
1468                                         s8 n2_liquid_level = 8;
1469                                         if(n2_is_source == false)
1470                                                 n2_liquid_level = n2.param2 & 0x07;
1471                                         
1472                                         s8 new_liquid_level = -1;
1473                                         if(from_top)
1474                                         {
1475                                                 //new_liquid_level = 7;
1476                                                 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1477                                                         new_liquid_level = 7;
1478                                                 else
1479                                                         new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1480                                         }
1481                                         else if(n2_liquid_level > 0)
1482                                         {
1483                                                 new_liquid_level = n2_liquid_level - 1;
1484                                         }
1485
1486                                         if(new_liquid_level > new_liquid_level_max)
1487                                                 new_liquid_level_max = new_liquid_level;
1488                                 }
1489
1490                                 }catch(InvalidPositionException &e)
1491                                 {
1492                                 }
1493                         } //for
1494                         
1495                         /*
1496                                 If liquid level should be something else, update it and
1497                                 add all the neighboring water nodes to the transform queue.
1498                         */
1499                         if(new_liquid_level_max != liquid_level)
1500                         {
1501                                 if(new_liquid_level_max == -1)
1502                                 {
1503                                         // Remove water alltoghether
1504                                         n0.d = CONTENT_AIR;
1505                                         n0.param2 = 0;
1506                                         setNode(p0, n0);
1507                                 }
1508                                 else
1509                                 {
1510                                         n0.param2 = new_liquid_level_max;
1511                                         setNode(p0, n0);
1512                                 }
1513                                 
1514                                 // Block has been modified
1515                                 {
1516                                         v3s16 blockpos = getNodeBlockPos(p0);
1517                                         MapBlock *block = getBlockNoCreateNoEx(blockpos);
1518                                         if(block != NULL)
1519                                                 modified_blocks.insert(blockpos, block);
1520                                 }
1521                                 
1522                                 /*
1523                                         Add neighboring non-source liquid nodes to transform queue.
1524                                 */
1525                                 v3s16 dirs[6] = {
1526                                         v3s16(0,0,1), // back
1527                                         v3s16(0,1,0), // top
1528                                         v3s16(1,0,0), // right
1529                                         v3s16(0,0,-1), // front
1530                                         v3s16(0,-1,0), // bottom
1531                                         v3s16(-1,0,0), // left
1532                                 };
1533                                 for(u16 i=0; i<6; i++)
1534                                 {
1535                                         try
1536                                         {
1537
1538                                         v3s16 p2 = p0 + dirs[i];
1539                                         
1540                                         MapNode n2 = getNode(p2);
1541                                         if(content_flowing_liquid(n2.d))
1542                                         {
1543                                                 m_transforming_liquid.push_back(p2);
1544                                         }
1545                                         
1546                                         }catch(InvalidPositionException &e)
1547                                         {
1548                                         }
1549                                 }
1550                         }
1551                 }
1552                 
1553                 // Get a new one from queue if the node has turned into non-water
1554                 if(content_liquid(n0.d) == false)
1555                         continue;
1556
1557                 /*
1558                         Flow water from this node
1559                 */
1560                 v3s16 dirs_to[5] = {
1561                         v3s16(0,-1,0), // bottom
1562                         v3s16(0,0,1), // back
1563                         v3s16(1,0,0), // right
1564                         v3s16(0,0,-1), // front
1565                         v3s16(-1,0,0), // left
1566                 };
1567                 for(u16 i=0; i<5; i++)
1568                 {
1569                         try
1570                         {
1571
1572                         bool to_bottom = (i == 0);
1573
1574                         // If liquid is at lowest possible height, it's not going
1575                         // anywhere except down
1576                         if(liquid_level == 0 && to_bottom == false)
1577                                 continue;
1578                         
1579                         u8 liquid_next_level = 0;
1580                         // If going to bottom
1581                         if(to_bottom)
1582                         {
1583                                 //liquid_next_level = 7;
1584                                 if(liquid_level >= 7 - WATER_DROP_BOOST)
1585                                         liquid_next_level = 7;
1586                                 else
1587                                         liquid_next_level = liquid_level + WATER_DROP_BOOST;
1588                         }
1589                         else
1590                                 liquid_next_level = liquid_level - 1;
1591
1592                         bool n2_changed = false;
1593                         bool flowed = false;
1594                         
1595                         v3s16 p2 = p0 + dirs_to[i];
1596
1597                         MapNode n2 = getNode(p2);
1598                         //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1599
1600                         if(content_liquid(n2.d))
1601                         {
1602                                 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1603                                 // Check that the liquids are the same type
1604                                 if(n2_nonsource_c != nonsource_c)
1605                                 {
1606                                         dstream<<"WARNING: Not handling: different liquids"
1607                                                         " collide"<<std::endl;
1608                                         continue;
1609                                 }
1610                                 bool n2_is_source = !content_flowing_liquid(n2.d);
1611                                 u8 n2_liquid_level = 8;
1612                                 if(n2_is_source == false)
1613                                         n2_liquid_level = n2.param2 & 0x07;
1614                                 
1615                                 if(to_bottom)
1616                                 {
1617                                         flowed = true;
1618                                 }
1619
1620                                 if(n2_is_source)
1621                                 {
1622                                         // Just flow into the source, nothing changes.
1623                                         // n2_changed is not set because destination didn't change
1624                                         flowed = true;
1625                                 }
1626                                 else
1627                                 {
1628                                         if(liquid_next_level > liquid_level)
1629                                         {
1630                                                 n2.param2 = liquid_next_level;
1631                                                 setNode(p2, n2);
1632
1633                                                 n2_changed = true;
1634                                                 flowed = true;
1635                                         }
1636                                 }
1637                         }
1638                         else if(n2.d == CONTENT_AIR)
1639                         {
1640                                 n2.d = nonsource_c;
1641                                 n2.param2 = liquid_next_level;
1642                                 setNode(p2, n2);
1643                                 
1644                                 n2_changed = true;
1645                                 flowed = true;
1646                         }
1647                         
1648                         //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1649
1650                         if(n2_changed)
1651                         {
1652                                 m_transforming_liquid.push_back(p2);
1653                                 
1654                                 v3s16 blockpos = getNodeBlockPos(p2);
1655                                 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1656                                 if(block != NULL)
1657                                         modified_blocks.insert(blockpos, block);
1658                         }
1659                         
1660                         // If n2_changed to bottom, don't flow anywhere else
1661                         if(to_bottom && flowed && !is_source)
1662                                 break;
1663                                 
1664                         }catch(InvalidPositionException &e)
1665                         {
1666                         }
1667                 }
1668
1669                 loopcount++;
1670                 //if(loopcount >= 100000)
1671                 if(loopcount >= initial_size * 1)
1672                         break;
1673         }
1674         //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1675 }
1676
1677 /*
1678         ServerMap
1679 */
1680
1681 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1682         Map(dout_server),
1683         m_heightmap(NULL)
1684 {
1685         /*
1686                 Experimental and debug stuff
1687         */
1688         
1689         {
1690                 dstream<<"Generating map point attribute lists"<<std::endl;
1691                 
1692                 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1693                 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1694                 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1695                 PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1696                 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1697
1698                 /*
1699                         NOTE: BEWARE: Too big amount of these will make map generation
1700                         slow. Especially those that are read by every block emerge.
1701                         
1702                         Fetch times:
1703                         1000 points: 2-3ms
1704                         5000 points: 15ms
1705                         15000 points: 40ms
1706                 */
1707                 
1708                 for(u32 i=0; i<5000; i++)
1709                 {
1710                         /*u32 lim = MAP_GENERATION_LIMIT;
1711                         if(i < 400)
1712                                 lim = 2000;*/
1713
1714                         u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1715
1716                         v3s16 p(
1717                                 -lim + myrand()%(lim*2),
1718                                 0,
1719                                 -lim + myrand()%(lim*2)
1720                         );
1721                         /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1722                         plants_amount = pow(plants_amount, 5);
1723                         list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1724                         
1725                         float plants_amount = 0;
1726                         if(myrand()%4 == 0)
1727                         {
1728                                 plants_amount = 1.5;
1729                         }
1730                         else if(myrand()%4 == 0)
1731                         {
1732                                 plants_amount = 0.5;
1733                         }
1734                         else if(myrand()%2 == 0)
1735                         {
1736                                 plants_amount = 0.03;
1737                         }
1738                         else
1739                         {
1740                                 plants_amount = 0.0;
1741                         }
1742
1743
1744                         list_plants_amount->addPoint(p, Attribute(plants_amount));
1745                 }
1746
1747                 for(u32 i=0; i<1000; i++)
1748                 {
1749                         /*u32 lim = MAP_GENERATION_LIMIT;
1750                         if(i < 400)
1751                                 lim = 2000;*/
1752
1753                         u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000;
1754
1755                         v3s16 p(
1756                                 -lim + myrand()%(lim*2),
1757                                 0,
1758                                 -lim + myrand()%(lim*2)
1759                         );
1760
1761                         float caves_amount = 0;
1762                         if(myrand()%5 == 0)
1763                         {
1764                                 caves_amount = 1.0;
1765                         }
1766                         else if(myrand()%3 == 0)
1767                         {
1768                                 caves_amount = 0.3;
1769                         }
1770                         else
1771                         {
1772                                 caves_amount = 0.05;
1773                         }
1774
1775                         list_caves_amount->addPoint(p, Attribute(caves_amount));
1776                 }
1777                 
1778                 for(u32 i=0; i<5000; i++)
1779                 {
1780                         /*u32 lim = MAP_GENERATION_LIMIT;
1781                         if(i < 400)
1782                                 lim = 2000;*/
1783
1784                         u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1785
1786                         v3s16 p(
1787                                 -lim + (myrand()%(lim*2)),
1788                                 0,
1789                                 -lim + (myrand()%(lim*2))
1790                         );
1791                         
1792                         /*s32 bh_i = (myrand()%200) - 50;
1793                         float baseheight = (float)bh_i;
1794                         
1795                         float m = 100.;
1796                         float e = 3.;
1797                         float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1798                         randmax = pow(randmax, e);
1799
1800                         //float randmax = (float)(myrand()%60);
1801                         float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1802
1803                         float baseheight = 0;
1804                         float randmax = 0;
1805                         float randfactor = 0;
1806
1807                         /*if(myrand()%5 == 0)
1808                         {
1809                                 baseheight = 100;
1810                                 randmax = 50;
1811                                 randfactor = 0.63;
1812                         }
1813                         else if(myrand()%6 == 0)
1814                         {
1815                                 baseheight = 200;
1816                                 randmax = 100;
1817                                 randfactor = 0.66;
1818                         }
1819                         else if(myrand()%4 == 0)
1820                         {
1821                                 baseheight = -3;
1822                                 randmax = 30;
1823                                 randfactor = 0.7;
1824                         }
1825                         else if(myrand()%3 == 0)
1826                         {
1827                                 baseheight = 0;
1828                                 randmax = 30;
1829                                 randfactor = 0.63;
1830                         }
1831                         else
1832                         {
1833                                 baseheight = -3;
1834                                 randmax = 20;
1835                                 randfactor = 0.5;
1836                         }*/
1837                         
1838                         baseheight = 0;
1839                         randmax = 15;
1840                         randfactor = 0.63;
1841
1842                         list_baseheight->addPoint(p, Attribute(baseheight));
1843                         list_randmax->addPoint(p, Attribute(randmax));
1844                         list_randfactor->addPoint(p, Attribute(randfactor));
1845                 }
1846
1847                 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
1848                 list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
1849                 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
1850
1851                 // Easy spawn point
1852                 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1853                 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1854                 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1855         }
1856
1857         /*
1858                 Try to load map; if not found, create a new one.
1859         */
1860
1861         m_savedir = savedir;
1862         m_map_saving_enabled = false;
1863         
1864         try
1865         {
1866                 // If directory exists, check contents and load if possible
1867                 if(fs::PathExists(m_savedir))
1868                 {
1869                         // If directory is empty, it is safe to save into it.
1870                         if(fs::GetDirListing(m_savedir).size() == 0)
1871                         {
1872                                 dstream<<DTIME<<"Server: Empty save directory is valid."
1873                                                 <<std::endl;
1874                                 m_map_saving_enabled = true;
1875                         }
1876                         else
1877                         {
1878                                 // Load master heightmap
1879                                 loadMasterHeightmap();
1880                                 
1881                                 // Load sector (0,0) and throw and exception on fail
1882                                 if(loadSectorFull(v2s16(0,0)) == false)
1883                                         throw LoadError("Failed to load sector (0,0)");
1884
1885                                 dstream<<DTIME<<"Server: Successfully loaded master "
1886                                                 "heightmap and sector (0,0) from "<<savedir<<
1887                                                 ", assuming valid save directory."
1888                                                 <<std::endl;
1889
1890                                 m_map_saving_enabled = true;
1891                                 // Map loaded, not creating new one
1892                                 return;
1893                         }
1894                 }
1895                 // If directory doesn't exist, it is safe to save to it
1896                 else{
1897                         m_map_saving_enabled = true;
1898                 }
1899         }
1900         catch(std::exception &e)
1901         {
1902                 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1903                                 <<", exception: "<<e.what()<<std::endl;
1904                 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1905                 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1906         }
1907
1908         dstream<<DTIME<<"Initializing new map."<<std::endl;
1909         
1910         // Create master heightmap
1911         /*ValueGenerator *maxgen =
1912                         ValueGenerator::deSerialize(hmp.randmax);
1913         ValueGenerator *factorgen =
1914                         ValueGenerator::deSerialize(hmp.randfactor);
1915         ValueGenerator *basegen =
1916                         ValueGenerator::deSerialize(hmp.base);
1917         m_heightmap = new UnlimitedHeightmap
1918                         (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
1919
1920         /*m_heightmap = new UnlimitedHeightmap
1921                         (hmp.blocksize, &m_padb);*/
1922
1923         m_heightmap = new UnlimitedHeightmap
1924                         (32, &m_padb);
1925         
1926         // Set map parameters
1927         m_params = mp;
1928         
1929         // Create zero sector
1930         emergeSector(v2s16(0,0));
1931
1932         // Initially write whole map
1933         save(false);
1934 }
1935
1936 ServerMap::~ServerMap()
1937 {
1938         try
1939         {
1940                 if(m_map_saving_enabled)
1941                 {
1942                         //save(false);
1943                         // Save only changed parts
1944                         save(true);
1945                         dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1946                 }
1947                 else
1948                 {
1949                         dstream<<DTIME<<"Server: map not saved"<<std::endl;
1950                 }
1951         }
1952         catch(std::exception &e)
1953         {
1954                 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1955                                 <<", exception: "<<e.what()<<std::endl;
1956         }
1957         
1958         if(m_heightmap != NULL)
1959                 delete m_heightmap;
1960 }
1961
1962 MapSector * ServerMap::emergeSector(v2s16 p2d)
1963 {
1964         DSTACK("%s: p2d=(%d,%d)",
1965                         __FUNCTION_NAME,
1966                         p2d.X, p2d.Y);
1967         // Check that it doesn't exist already
1968         try{
1969                 return getSectorNoGenerate(p2d);
1970         }
1971         catch(InvalidPositionException &e)
1972         {
1973         }
1974         
1975         /*
1976                 Try to load the sector from disk.
1977         */
1978         if(loadSectorFull(p2d) == true)
1979         {
1980                 return getSectorNoGenerate(p2d);
1981         }
1982
1983         /*
1984                 If there is no master heightmap, throw.
1985         */
1986         if(m_heightmap == NULL)
1987         {
1988                 throw InvalidPositionException("emergeSector(): no heightmap");
1989         }
1990
1991         /*
1992                 Do not generate over-limit
1993         */
1994         if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1995         || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1996         || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1997         || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1998                 throw InvalidPositionException("emergeSector(): pos. over limit");
1999
2000         /*
2001                 Generate sector and heightmaps
2002         */
2003         
2004         // Number of heightmaps in sector in each direction
2005         u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
2006
2007         // Heightmap side width
2008         s16 hm_d = MAP_BLOCKSIZE / hm_split;
2009
2010         ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
2011         
2012         // Sector position on map in nodes
2013         v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2014
2015         /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
2016                         " heightmaps and objects"<<std::endl;*/
2017         
2018         /*
2019                 Calculate some information about local properties
2020         */
2021         
2022         v2s16 mhm_p = p2d * hm_split;
2023         f32 corners[4] = {
2024                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
2025                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
2026                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
2027                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
2028         };
2029         
2030         float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
2031         float avgslope = 0.0;
2032         avgslope += fabs(avgheight - corners[0]);
2033         avgslope += fabs(avgheight - corners[1]);
2034         avgslope += fabs(avgheight - corners[2]);
2035         avgslope += fabs(avgheight - corners[3]);
2036         avgslope /= 4.0;
2037         avgslope /= MAP_BLOCKSIZE;
2038         //dstream<<"avgslope="<<avgslope<<std::endl;
2039
2040         float pitness = 0.0;
2041         v2f32 a;
2042         a = m_heightmap->getSlope(p2d+v2s16(0,0));
2043         pitness += -a.X;
2044         pitness += -a.Y;
2045         a = m_heightmap->getSlope(p2d+v2s16(0,1));
2046         pitness += -a.X;
2047         pitness += a.Y;
2048         a = m_heightmap->getSlope(p2d+v2s16(1,1));
2049         pitness += a.X;
2050         pitness += a.Y;
2051         a = m_heightmap->getSlope(p2d+v2s16(1,0));
2052         pitness += a.X;
2053         pitness += -a.Y;
2054         pitness /= 4.0;
2055         pitness /= MAP_BLOCKSIZE;
2056         //dstream<<"pitness="<<pitness<<std::endl;
2057
2058         /*
2059                 Get local attributes
2060         */
2061         
2062         float local_plants_amount = 0.0;
2063         {
2064                 //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
2065                 //TimeTaker attrtimer("emergeSector() attribute fetch");
2066                 
2067                 // Get plant amount from attributes
2068                 PointAttributeList *palist = m_padb.getList("plants_amount");
2069                 assert(palist);
2070                 /*local_plants_amount =
2071                                 palist->getNearAttr(nodepos2d).getFloat();*/
2072                 local_plants_amount =
2073                                 palist->getInterpolatedFloat(nodepos2d);
2074         }
2075
2076         /*
2077                 Generate sector heightmap
2078         */
2079
2080         // Loop through sub-heightmaps
2081         for(s16 y=0; y<hm_split; y++)
2082         for(s16 x=0; x<hm_split; x++)
2083         {
2084                 v2s16 p_in_sector = v2s16(x,y);
2085                 v2s16 mhm_p = p2d * hm_split + p_in_sector;
2086                 f32 corners[4] = {
2087                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
2088                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
2089                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
2090                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
2091                 };
2092
2093                 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
2094                                 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
2095                                 <<std::endl;*/
2096
2097                 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
2098                                 mhm_p, hm_d);
2099                 sector->setHeightmap(p_in_sector, hm);
2100
2101                 //TODO: Make these values configurable
2102                 //hm->generateContinued(0.0, 0.0, corners);
2103                 //hm->generateContinued(0.25, 0.2, corners);
2104                 //hm->generateContinued(0.5, 0.2, corners);
2105                 //hm->generateContinued(1.0, 0.2, corners);
2106                 //hm->generateContinued(2.0, 0.2, corners);
2107                 //hm->generateContinued(2.0 * avgslope, 0.5, corners);
2108                 hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners);
2109
2110                 //hm->print();
2111         }
2112
2113         /*
2114                 Generate objects
2115         */
2116         
2117         core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
2118         sector->setObjects(objects);
2119
2120         float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
2121
2122         /*
2123                 Plant some trees if there is not much slope
2124         */
2125         {
2126                 // Avgslope is the derivative of a hill
2127                 //float t = avgslope * avgslope;
2128                 float t = avgslope;
2129                 float a = area/16 * m_params.plants_amount * local_plants_amount;
2130                 u32 tree_max;
2131                 //float something = 0.17*0.17;
2132                 float something = 0.3;
2133                 if(t > something)
2134                         tree_max = a / (t/something);
2135                 else
2136                         tree_max = a;
2137                 
2138                 u32 count = (myrand()%(tree_max+1));
2139                 //u32 count = tree_max;
2140                 for(u32 i=0; i<count; i++)
2141                 {
2142                         s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
2143                         s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
2144                         s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2145                         if(y < WATER_LEVEL)
2146                                 continue;
2147                         objects->insert(v3s16(x, y, z),
2148                                         SECTOR_OBJECT_TREE_1);
2149                 }
2150         }
2151         /*
2152                 Plant some bushes if sector is pit-like
2153         */
2154         {
2155                 // Pitness usually goes at around -0.5...0.5
2156                 u32 bush_max = 0;
2157                 u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount;
2158                 if(pitness > 0)
2159                         bush_max = (pitness*a*4);
2160                 if(bush_max > a)
2161                         bush_max = a;
2162                 u32 count = (myrand()%(bush_max+1));
2163                 for(u32 i=0; i<count; i++)
2164                 {
2165                         s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
2166                         s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
2167                         s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2168                         if(y < WATER_LEVEL)
2169                                 continue;
2170                         objects->insert(v3s16(x, y, z),
2171                                         SECTOR_OBJECT_BUSH_1);
2172                 }
2173         }
2174         /*
2175                 Add ravine (randomly)
2176         */
2177         if(m_params.ravines_amount > 0.001)
2178         {
2179                 if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
2180                 {
2181                         s16 s = 6;
2182                         s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2183                         s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2184                         /*s16 x = 8;
2185                         s16 z = 8;*/
2186                         s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2187                         objects->insert(v3s16(x, y, z),
2188                                         SECTOR_OBJECT_RAVINE);
2189                 }
2190         }
2191
2192         /*
2193                 Insert to container
2194         */
2195         JMutexAutoLock lock(m_sector_mutex);
2196         m_sectors.insert(p2d, sector);
2197         
2198         return sector;
2199 }
2200
2201 MapBlock * ServerMap::emergeBlock(
2202                 v3s16 p,
2203                 bool only_from_disk,
2204                 core::map<v3s16, MapBlock*> &changed_blocks,
2205                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2206 )
2207 {
2208         DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
2209                         __FUNCTION_NAME,
2210                         p.X, p.Y, p.Z, only_from_disk);
2211                         
2212         /*dstream<<"ServerMap::emergeBlock(): "
2213                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2214                         <<", only_from_disk="<<only_from_disk<<std::endl;*/
2215         v2s16 p2d(p.X, p.Z);
2216         s16 block_y = p.Y;
2217         /*
2218                 This will create or load a sector if not found in memory.
2219                 If block exists on disk, it will be loaded.
2220
2221                 NOTE: On old save formats, this will be slow, as it generates
2222                       lighting on blocks for them.
2223         */
2224         ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
2225         assert(sector->getId() == MAPSECTOR_SERVER);
2226
2227         // Try to get a block from the sector
2228         MapBlock *block = NULL;
2229         bool not_on_disk = false;
2230         try{
2231                 block = sector->getBlockNoCreate(block_y);
2232                 if(block->isDummy() == true)
2233                         not_on_disk = true;
2234                 else
2235                         return block;
2236         }
2237         catch(InvalidPositionException &e)
2238         {
2239                 not_on_disk = true;
2240         }
2241         
2242         /*
2243                 If block was not found on disk and not going to generate a
2244                 new one, make sure there is a dummy block in place.
2245         */
2246         if(not_on_disk && only_from_disk)
2247         {
2248                 if(block == NULL)
2249                 {
2250                         // Create dummy block
2251                         block = new MapBlock(this, p, true);
2252
2253                         // Add block to sector
2254                         sector->insertBlock(block);
2255                 }
2256                 // Done.
2257                 return block;
2258         }
2259
2260         //dstream<<"Not found on disk, generating."<<std::endl;
2261         // 0ms
2262         //TimeTaker("emergeBlock() generate");
2263
2264         /*
2265                 Do not generate over-limit
2266         */
2267         if(blockpos_over_limit(p))
2268                 throw InvalidPositionException("emergeBlock(): pos. over limit");
2269
2270         /*
2271                 OK; Not found.
2272
2273                 Go on generating the block.
2274
2275                 TODO: If a dungeon gets generated so that it's side gets
2276                       revealed to the outside air, the lighting should be
2277                           recalculated.
2278         */
2279         
2280         /*
2281                 If block doesn't exist, create one.
2282                 If it exists, it is a dummy. In that case unDummify() it.
2283
2284                 NOTE: This already sets the map as the parent of the block
2285         */
2286         if(block == NULL)
2287         {
2288                 block = sector->createBlankBlockNoInsert(block_y);
2289         }
2290         else
2291         {
2292                 // Remove the block so that nobody can get a half-generated one.
2293                 sector->removeBlock(block);
2294                 // Allocate the block to contain the generated data
2295                 block->unDummify();
2296         }
2297         
2298         /*u8 water_material = CONTENT_WATER;
2299         if(g_settings.getBool("endless_water"))
2300                 water_material = CONTENT_WATERSOURCE;*/
2301         u8 water_material = CONTENT_WATERSOURCE;
2302         
2303         s32 lowest_ground_y = 32767;
2304         s32 highest_ground_y = -32768;
2305         
2306         // DEBUG
2307         //sector->printHeightmaps();
2308
2309         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2310         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2311         {
2312                 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
2313
2314                 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
2315                 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
2316                 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
2317                 {
2318                         dstream<<"WARNING: Surface height not found in sector "
2319                                         "for block that is being emerged"<<std::endl;
2320                         surface_y_f = 0.0;
2321                 }
2322
2323                 s16 surface_y = surface_y_f;
2324                 //avg_ground_y += surface_y;
2325                 if(surface_y < lowest_ground_y)
2326                         lowest_ground_y = surface_y;
2327                 if(surface_y > highest_ground_y)
2328                         highest_ground_y = surface_y;
2329
2330                 s32 surface_depth = 0;
2331                 
2332                 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
2333                 
2334                 //float min_slope = 0.45;
2335                 //float max_slope = 0.85;
2336                 float min_slope = 0.60;
2337                 float max_slope = 1.20;
2338                 float min_slope_depth = 5.0;
2339                 float max_slope_depth = 0;
2340
2341                 if(slope < min_slope)
2342                         surface_depth = min_slope_depth;
2343                 else if(slope > max_slope)
2344                         surface_depth = max_slope_depth;
2345                 else
2346                         surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2347
2348                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2349                 {
2350                         s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2351                         MapNode n;
2352                         /*
2353                                 Calculate lighting
2354                                 
2355                                 NOTE: If there are some man-made structures above the
2356                                 newly created block, they won't be taken into account.
2357                         */
2358                         if(real_y > surface_y)
2359                                 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2360
2361                         /*
2362                                 Calculate material
2363                         */
2364
2365                         // If node is over heightmap y, it's air or water
2366                         if(real_y > surface_y)
2367                         {
2368                                 // If under water level, it's water
2369                                 if(real_y < WATER_LEVEL)
2370                                 {
2371                                         n.d = water_material;
2372                                         n.setLight(LIGHTBANK_DAY,
2373                                                         diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2374                                         /*
2375                                                 Add to transforming liquid queue (in case it'd
2376                                                 start flowing)
2377                                         */
2378                                         v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
2379                                         m_transforming_liquid.push_back(real_pos);
2380                                 }
2381                                 // else air
2382                                 else
2383                                         n.d = CONTENT_AIR;
2384                         }
2385                         // Else it's ground or dungeons (air)
2386                         else
2387                         {
2388                                 // If it's surface_depth under ground, it's stone
2389                                 if(real_y <= surface_y - surface_depth)
2390                                 {
2391                                         n.d = CONTENT_STONE;
2392                                 }
2393                                 else
2394                                 {
2395                                         // It is mud if it is under the first ground
2396                                         // level or under water
2397                                         if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2398                                         {
2399                                                 n.d = CONTENT_MUD;
2400                                         }
2401                                         else
2402                                         {
2403                                                 n.d = CONTENT_GRASS;
2404                                         }
2405
2406                                         //n.d = CONTENT_MUD;
2407                                         
2408                                         /*// If under water level, it's mud
2409                                         if(real_y < WATER_LEVEL)
2410                                                 n.d = CONTENT_MUD;
2411                                         // Only the topmost node is grass
2412                                         else if(real_y <= surface_y - 1)
2413                                                 n.d = CONTENT_MUD;
2414                                         else
2415                                                 n.d = CONTENT_GRASS;*/
2416                                 }
2417                         }
2418
2419                         block->setNode(v3s16(x0,y0,z0), n);
2420                 }
2421         }
2422         
2423         /*
2424                 Calculate some helper variables
2425         */
2426         
2427         // Completely underground if the highest part of block is under lowest
2428         // ground height.
2429         // This has to be very sure; it's probably one too strict now but
2430         // that's just better.
2431         bool completely_underground =
2432                         block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2433
2434         bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2435
2436         bool mostly_underwater_surface = false;
2437         if(highest_ground_y < WATER_LEVEL
2438                         && some_part_underground && !completely_underground)
2439                 mostly_underwater_surface = true;
2440
2441         /*
2442                 Get local attributes
2443         */
2444
2445         //dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
2446
2447         float caves_amount = 0;
2448         
2449         {
2450                 /*
2451                         NOTE: BEWARE: Too big amount of attribute points slows verything
2452                         down by a lot.
2453                         1 interpolation from 5000 points takes 2-3ms.
2454                 */
2455                 //TimeTaker timer("emergeBlock() local attribute retrieval");
2456                 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2457                 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
2458                 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
2459         }
2460
2461         //dstream<<"emergeBlock(): Done"<<std::endl;
2462
2463         /*
2464                 Generate dungeons
2465         */
2466
2467         // Initialize temporary table
2468         const s32 ued = MAP_BLOCKSIZE;
2469         bool underground_emptiness[ued*ued*ued];
2470         for(s32 i=0; i<ued*ued*ued; i++)
2471         {
2472                 underground_emptiness[i] = 0;
2473         }
2474         
2475         // Fill table
2476 #if 1
2477         {
2478                 /*
2479                         Initialize orp and ors. Try to find if some neighboring
2480                         MapBlock has a tunnel ended in its side
2481                 */
2482
2483                 v3f orp(
2484                         (float)(myrand()%ued)+0.5,
2485                         (float)(myrand()%ued)+0.5,
2486                         (float)(myrand()%ued)+0.5
2487                 );
2488                 
2489                 bool found_existing = false;
2490
2491                 // Check z-
2492                 try
2493                 {
2494                         s16 z = -1;
2495                         for(s16 y=0; y<ued; y++)
2496                         for(s16 x=0; x<ued; x++)
2497                         {
2498                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2499                                 if(getNode(ap).d == CONTENT_AIR)
2500                                 {
2501                                         orp = v3f(x+1,y+1,0);
2502                                         found_existing = true;
2503                                         goto continue_generating;
2504                                 }
2505                         }
2506                 }
2507                 catch(InvalidPositionException &e){}
2508                 
2509                 // Check z+
2510                 try
2511                 {
2512                         s16 z = ued;
2513                         for(s16 y=0; y<ued; y++)
2514                         for(s16 x=0; x<ued; x++)
2515                         {
2516                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2517                                 if(getNode(ap).d == CONTENT_AIR)
2518                                 {
2519                                         orp = v3f(x+1,y+1,ued-1);
2520                                         found_existing = true;
2521                                         goto continue_generating;
2522                                 }
2523                         }
2524                 }
2525                 catch(InvalidPositionException &e){}
2526                 
2527                 // Check x-
2528                 try
2529                 {
2530                         s16 x = -1;
2531                         for(s16 y=0; y<ued; y++)
2532                         for(s16 z=0; z<ued; z++)
2533                         {
2534                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2535                                 if(getNode(ap).d == CONTENT_AIR)
2536                                 {
2537                                         orp = v3f(0,y+1,z+1);
2538                                         found_existing = true;
2539                                         goto continue_generating;
2540                                 }
2541                         }
2542                 }
2543                 catch(InvalidPositionException &e){}
2544                 
2545                 // Check x+
2546                 try
2547                 {
2548                         s16 x = ued;
2549                         for(s16 y=0; y<ued; y++)
2550                         for(s16 z=0; z<ued; z++)
2551                         {
2552                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2553                                 if(getNode(ap).d == CONTENT_AIR)
2554                                 {
2555                                         orp = v3f(ued-1,y+1,z+1);
2556                                         found_existing = true;
2557                                         goto continue_generating;
2558                                 }
2559                         }
2560                 }
2561                 catch(InvalidPositionException &e){}
2562
2563                 // Check y-
2564                 try
2565                 {
2566                         s16 y = -1;
2567                         for(s16 x=0; x<ued; x++)
2568                         for(s16 z=0; z<ued; z++)
2569                         {
2570                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2571                                 if(getNode(ap).d == CONTENT_AIR)
2572                                 {
2573                                         orp = v3f(x+1,0,z+1);
2574                                         found_existing = true;
2575                                         goto continue_generating;
2576                                 }
2577                         }
2578                 }
2579                 catch(InvalidPositionException &e){}
2580                 
2581                 // Check y+
2582                 try
2583                 {
2584                         s16 y = ued;
2585                         for(s16 x=0; x<ued; x++)
2586                         for(s16 z=0; z<ued; z++)
2587                         {
2588                                 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2589                                 if(getNode(ap).d == CONTENT_AIR)
2590                                 {
2591                                         orp = v3f(x+1,ued-1,z+1);
2592                                         found_existing = true;
2593                                         goto continue_generating;
2594                                 }
2595                         }
2596                 }
2597                 catch(InvalidPositionException &e){}
2598
2599 continue_generating:
2600                 
2601                 /*
2602                         Choose whether to actually generate dungeon
2603                 */
2604                 bool do_generate_dungeons = true;
2605                 // Don't generate if no part is underground
2606                 if(!some_part_underground)
2607                 {
2608                         do_generate_dungeons = false;
2609                 }
2610                 // Don't generate if mostly underwater surface
2611                 else if(mostly_underwater_surface)
2612                 {
2613                         do_generate_dungeons = false;
2614                 }
2615                 // Partly underground = cave
2616                 else if(!completely_underground)
2617                 {
2618                         do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2619                 }
2620                 // Found existing dungeon underground
2621                 else if(found_existing && completely_underground)
2622                 {
2623                         do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2624                 }
2625                 // Underground and no dungeons found
2626                 else
2627                 {
2628                         do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
2629                 }
2630
2631                 if(do_generate_dungeons)
2632                 {
2633                         /*
2634                                 Generate some tunnel starting from orp and ors
2635                         */
2636                         for(u16 i=0; i<3; i++)
2637                         {
2638                                 v3f rp(
2639                                         (float)(myrand()%ued)+0.5,
2640                                         (float)(myrand()%ued)+0.5,
2641                                         (float)(myrand()%ued)+0.5
2642                                 );
2643                                 s16 min_d = 0;
2644                                 s16 max_d = 4;
2645                                 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
2646                                 
2647                                 v3f vec = rp - orp;
2648
2649                                 for(float f=0; f<1.0; f+=0.04)
2650                                 {
2651                                         v3f fp = orp + vec * f;
2652                                         v3s16 cp(fp.X, fp.Y, fp.Z);
2653                                         s16 d0 = -rs/2;
2654                                         s16 d1 = d0 + rs - 1;
2655                                         for(s16 z0=d0; z0<=d1; z0++)
2656                                         {
2657                                                 s16 si = rs - abs(z0);
2658                                                 for(s16 x0=-si; x0<=si-1; x0++)
2659                                                 {
2660                                                         s16 si2 = rs - abs(x0);
2661                                                         for(s16 y0=-si2+1; y0<=si2-1; y0++)
2662                                                         {
2663                                                                 s16 z = cp.Z + z0;
2664                                                                 s16 y = cp.Y + y0;
2665                                                                 s16 x = cp.X + x0;
2666                                                                 v3s16 p(x,y,z);
2667                                                                 if(isInArea(p, ued) == false)
2668                                                                         continue;
2669                                                                 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2670                                                         }
2671                                                 }
2672                                         }
2673                                 }
2674
2675                                 orp = rp;
2676                         }
2677                 }
2678         }
2679 #endif
2680
2681         // Set to true if has caves.
2682         // Set when some non-air is changed to air when making caves.
2683         bool has_dungeons = false;
2684
2685         /*
2686                 Apply temporary cave data to block
2687         */
2688
2689         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2690         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2691         {
2692                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2693                 {
2694                         MapNode n = block->getNode(v3s16(x0,y0,z0));
2695
2696                         // Create dungeons
2697                         if(underground_emptiness[
2698                                         ued*ued*(z0*ued/MAP_BLOCKSIZE)
2699                                         +ued*(y0*ued/MAP_BLOCKSIZE)
2700                                         +(x0*ued/MAP_BLOCKSIZE)])
2701                         {
2702                                 if(is_ground_content(n.d))
2703                                 {
2704                                         // Has now caves
2705                                         has_dungeons = true;
2706                                         // Set air to node
2707                                         n.d = CONTENT_AIR;
2708                                 }
2709                         }
2710
2711                         block->setNode(v3s16(x0,y0,z0), n);
2712                 }
2713         }
2714         
2715         /*
2716                 This is used for guessing whether or not the block should
2717                 receive sunlight from the top if the top block doesn't exist
2718         */
2719         block->setIsUnderground(completely_underground);
2720
2721         /*
2722                 Force lighting update if some part of block is partly
2723                 underground and has caves.
2724         */
2725         /*if(some_part_underground && !completely_underground && has_dungeons)
2726         {
2727                 //dstream<<"Half-ground caves"<<std::endl;
2728                 lighting_invalidated_blocks[block->getPos()] = block;
2729         }*/
2730         
2731         // DEBUG: Always update lighting
2732         //lighting_invalidated_blocks[block->getPos()] = block;
2733
2734         /*
2735                 Add some minerals
2736         */
2737
2738         if(some_part_underground)
2739         {
2740                 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2741
2742                 /*
2743                         Add meseblocks
2744                 */
2745                 for(s16 i=0; i<underground_level/4 + 1; i++)
2746                 {
2747                         if(myrand()%50 == 0)
2748                         {
2749                                 v3s16 cp(
2750                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
2751                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
2752                                         (myrand()%(MAP_BLOCKSIZE-2))+1
2753                                 );
2754
2755                                 MapNode n;
2756                                 n.d = CONTENT_MESE;
2757                                 
2758                                 //if(is_ground_content(block->getNode(cp).d))
2759                                 if(block->getNode(cp).d == CONTENT_STONE)
2760                                         if(myrand()%8 == 0)
2761                                                 block->setNode(cp, n);
2762
2763                                 for(u16 i=0; i<26; i++)
2764                                 {
2765                                         //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2766                                         if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2767                                                 if(myrand()%8 == 0)
2768                                                         block->setNode(cp+g_26dirs[i], n);
2769                                 }
2770                         }
2771                 }
2772
2773                 /*
2774                         Add coal
2775                 */
2776                 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2777                 u16 coal_rareness = 60 / coal_amount;
2778                 if(coal_rareness == 0)
2779                         coal_rareness = 1;
2780                 if(myrand()%coal_rareness == 0)
2781                 {
2782                         u16 a = myrand() % 16;
2783                         u16 amount = coal_amount * a*a*a / 1000;
2784                         for(s16 i=0; i<amount; i++)
2785                         {
2786                                 v3s16 cp(
2787                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
2788                                         (myrand()%(MAP_BLOCKSIZE-2))+1,
2789                                         (myrand()%(MAP_BLOCKSIZE-2))+1
2790                                 );
2791
2792                                 MapNode n;
2793                                 n.d = CONTENT_COALSTONE;
2794
2795                                 //dstream<<"Adding coalstone"<<std::endl;
2796                                 
2797                                 //if(is_ground_content(block->getNode(cp).d))
2798                                 if(block->getNode(cp).d == CONTENT_STONE)
2799                                         if(myrand()%8 == 0)
2800                                                 block->setNode(cp, n);
2801
2802                                 for(u16 i=0; i<26; i++)
2803                                 {
2804                                         //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2805                                         if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2806                                                 if(myrand()%8 == 0)
2807                                                         block->setNode(cp+g_26dirs[i], n);
2808                                 }
2809                         }
2810                 }
2811         }
2812         
2813         /*
2814                 Create a few rats in empty blocks underground
2815         */
2816         if(completely_underground)
2817         {
2818                 //for(u16 i=0; i<2; i++)
2819                 {
2820                         v3s16 cp(
2821                                 (myrand()%(MAP_BLOCKSIZE-2))+1,
2822                                 (myrand()%(MAP_BLOCKSIZE-2))+1,
2823                                 (myrand()%(MAP_BLOCKSIZE-2))+1
2824                         );
2825
2826                         // Check that the place is empty
2827                         //if(!is_ground_content(block->getNode(cp).d))
2828                         if(1)
2829                         {
2830                                 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2831                                 block->addObject(obj);
2832                         }
2833                 }
2834         }
2835         
2836         /*
2837                 Add block to sector.
2838         */
2839         sector->insertBlock(block);
2840         
2841         /*
2842                 Sector object stuff
2843         */
2844                 
2845         // An y-wise container of changed blocks
2846         core::map<s16, MapBlock*> changed_blocks_sector;
2847
2848         /*
2849                 Check if any sector's objects can be placed now.
2850                 If so, place them.
2851         */
2852         core::map<v3s16, u8> *objects = sector->getObjects();
2853         core::list<v3s16> objects_to_remove;
2854         for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2855                         i.atEnd() == false; i++)
2856         {
2857                 v3s16 p = i.getNode()->getKey();
2858                 v2s16 p2d(p.X,p.Z);
2859                 u8 d = i.getNode()->getValue();
2860
2861                 // Ground level point (user for stuff that is on ground)
2862                 v3s16 gp = p;
2863                 bool ground_found = true;
2864                 
2865                 // Search real ground level
2866                 try{
2867                         for(;;)
2868                         {
2869                                 MapNode n = sector->getNode(gp);
2870
2871                                 // If not air, go one up and continue to placing the tree
2872                                 if(n.d != CONTENT_AIR)
2873                                 {
2874                                         gp += v3s16(0,1,0);
2875                                         break;
2876                                 }
2877
2878                                 // If air, go one down
2879                                 gp += v3s16(0,-1,0);
2880                         }
2881                 }catch(InvalidPositionException &e)
2882                 {
2883                         // Ground not found.
2884                         ground_found = false;
2885                         // This is most close to ground
2886                         gp += v3s16(0,1,0);
2887                 }
2888
2889                 try
2890                 {
2891
2892                 if(d == SECTOR_OBJECT_TEST)
2893                 {
2894                         if(sector->isValidArea(p + v3s16(0,0,0),
2895                                         p + v3s16(0,0,0), &changed_blocks_sector))
2896                         {
2897                                 MapNode n;
2898                                 n.d = CONTENT_TORCH;
2899                                 sector->setNode(p, n);
2900                                 objects_to_remove.push_back(p);
2901                         }
2902                 }
2903                 else if(d == SECTOR_OBJECT_TREE_1)
2904                 {
2905                         if(ground_found == false)
2906                                 continue;
2907
2908                         v3s16 p_min = gp + v3s16(-1,0,-1);
2909                         v3s16 p_max = gp + v3s16(1,5,1);
2910                         if(sector->isValidArea(p_min, p_max,
2911                                         &changed_blocks_sector))
2912                         {
2913                                 MapNode n;
2914                                 n.d = CONTENT_TREE;
2915                                 sector->setNode(gp+v3s16(0,0,0), n);
2916                                 sector->setNode(gp+v3s16(0,1,0), n);
2917                                 sector->setNode(gp+v3s16(0,2,0), n);
2918                                 sector->setNode(gp+v3s16(0,3,0), n);
2919
2920                                 n.d = CONTENT_LEAVES;
2921
2922                                 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2923
2924                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2925                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2926                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2927                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2928                                 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2929                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2930                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2931                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2932
2933                                 sector->setNode(gp+v3s16(0,4,0), n);
2934                                 
2935                                 sector->setNode(gp+v3s16(-1,4,0), n);
2936                                 sector->setNode(gp+v3s16(1,4,0), n);
2937                                 sector->setNode(gp+v3s16(0,4,-1), n);
2938                                 sector->setNode(gp+v3s16(0,4,1), n);
2939                                 sector->setNode(gp+v3s16(1,4,1), n);
2940                                 sector->setNode(gp+v3s16(-1,4,1), n);
2941                                 sector->setNode(gp+v3s16(-1,4,-1), n);
2942                                 sector->setNode(gp+v3s16(1,4,-1), n);
2943
2944                                 sector->setNode(gp+v3s16(-1,3,0), n);
2945                                 sector->setNode(gp+v3s16(1,3,0), n);
2946                                 sector->setNode(gp+v3s16(0,3,-1), n);
2947                                 sector->setNode(gp+v3s16(0,3,1), n);
2948                                 sector->setNode(gp+v3s16(1,3,1), n);
2949                                 sector->setNode(gp+v3s16(-1,3,1), n);
2950                                 sector->setNode(gp+v3s16(-1,3,-1), n);
2951                                 sector->setNode(gp+v3s16(1,3,-1), n);
2952                                 
2953                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2954                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2955                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2956                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2957                                 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2958                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2959                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2960                                 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2961                                 
2962                                 // Objects are identified by wanted position
2963                                 objects_to_remove.push_back(p);
2964                                 
2965                                 // Lighting has to be recalculated for this one.
2966                                 sector->getBlocksInArea(p_min, p_max, 
2967                                                 lighting_invalidated_blocks);
2968                         }
2969                 }
2970                 else if(d == SECTOR_OBJECT_BUSH_1)
2971                 {
2972                         if(ground_found == false)
2973                                 continue;
2974                         
2975                         if(sector->isValidArea(gp + v3s16(0,0,0),
2976                                         gp + v3s16(0,0,0), &changed_blocks_sector))
2977                         {
2978                                 MapNode n;
2979                                 n.d = CONTENT_LEAVES;
2980                                 sector->setNode(gp+v3s16(0,0,0), n);
2981                                 
2982                                 // Objects are identified by wanted position
2983                                 objects_to_remove.push_back(p);
2984                         }
2985                 }
2986                 else if(d == SECTOR_OBJECT_RAVINE)
2987                 {
2988                         s16 maxdepth = -20;
2989                         v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2990                         v3s16 p_max = p + v3s16(6,6,6);
2991                         if(sector->isValidArea(p_min, p_max,
2992                                         &changed_blocks_sector))
2993                         {
2994                                 MapNode n;
2995                                 n.d = CONTENT_STONE;
2996                                 MapNode n2;
2997                                 n2.d = CONTENT_AIR;
2998                                 s16 depth = maxdepth + (myrand()%10);
2999                                 s16 z = 0;
3000                                 s16 minz = -6 - (-2);
3001                                 s16 maxz = 6 -1;
3002                                 for(s16 x=-6; x<=6; x++)
3003                                 {
3004                                         z += -1 + (myrand()%3);
3005                                         if(z < minz)
3006                                                 z = minz;
3007                                         if(z > maxz)
3008                                                 z = maxz;
3009                                         for(s16 y=depth+(myrand()%2); y<=6; y++)
3010                                         {
3011                                                 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
3012                                                                 <<std::endl;*/
3013                                                 {
3014                                                         v3s16 p2 = p + v3s16(x,y,z-2);
3015                                                         if(is_ground_content(sector->getNode(p2).d)
3016                                                                         && !is_mineral(sector->getNode(p2).d))
3017                                                                 sector->setNode(p2, n);
3018                                                 }
3019                                                 {
3020                                                         v3s16 p2 = p + v3s16(x,y,z-1);
3021                                                         if(is_ground_content(sector->getNode(p2).d)
3022                                                                         && !is_mineral(sector->getNode(p2).d))
3023                                                                 sector->setNode(p2, n2);
3024                                                 }
3025                                                 {
3026                                                         v3s16 p2 = p + v3s16(x,y,z+0);
3027                                                         if(is_ground_content(sector->getNode(p2).d)
3028                                                                         && !is_mineral(sector->getNode(p2).d))
3029                                                                 sector->setNode(p2, n2);
3030                                                 }
3031                                                 {
3032                                                         v3s16 p2 = p + v3s16(x,y,z+1);
3033                                                         if(is_ground_content(sector->getNode(p2).d)
3034                                                                         && !is_mineral(sector->getNode(p2).d))
3035                                                                 sector->setNode(p2, n);
3036                                                 }
3037
3038                                                 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
3039                                                 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
3040                                         }
3041                                 }
3042                                 
3043                                 objects_to_remove.push_back(p);
3044                                 
3045                                 // Lighting has to be recalculated for this one.
3046                                 sector->getBlocksInArea(p_min, p_max, 
3047                                                 lighting_invalidated_blocks);
3048                         }
3049                 }
3050                 else
3051                 {
3052                         dstream<<"ServerMap::emergeBlock(): "
3053                                         "Invalid heightmap object"
3054                                         <<std::endl;
3055                 }
3056
3057                 }//try
3058                 catch(InvalidPositionException &e)
3059                 {
3060                         dstream<<"WARNING: "<<__FUNCTION_NAME
3061                                         <<": while inserting object "<<(int)d
3062                                         <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
3063                                         <<" InvalidPositionException.what()="
3064                                         <<e.what()<<std::endl;
3065                         // This is not too fatal and seems to happen sometimes.
3066                         assert(0);
3067                 }
3068         }
3069
3070         for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
3071                         i != objects_to_remove.end(); i++)
3072         {
3073                 objects->remove(*i);
3074         }
3075
3076         /*
3077                 Initially update sunlight
3078         */
3079         
3080         {
3081                 core::map<v3s16, bool> light_sources;
3082                 bool black_air_left = false;
3083                 bool bottom_invalid =
3084                                 block->propagateSunlight(light_sources, true,
3085                                 &black_air_left, true);
3086
3087                 // If sunlight didn't reach everywhere and part of block is
3088                 // above ground, lighting has to be properly updated
3089                 if(black_air_left && some_part_underground)
3090                 {
3091                         lighting_invalidated_blocks[block->getPos()] = block;
3092                 }
3093
3094                 if(bottom_invalid)
3095                 {
3096                         lighting_invalidated_blocks[block->getPos()] = block;
3097                 }
3098         }
3099
3100         /*
3101                 Translate sector's changed blocks to global changed blocks
3102         */
3103         
3104         for(core::map<s16, MapBlock*>::Iterator
3105                         i = changed_blocks_sector.getIterator();
3106                         i.atEnd() == false; i++)
3107         {
3108                 MapBlock *block = i.getNode()->getValue();
3109
3110                 changed_blocks.insert(block->getPos(), block);
3111         }
3112
3113         /*
3114                 Debug information
3115         */
3116         if(0)
3117         {
3118                 dstream
3119                 <<"lighting_invalidated_blocks.size()"
3120                 <<", has_dungeons"
3121                 <<", completely_ug"
3122                 <<", some_part_ug"
3123                 <<"  "<<lighting_invalidated_blocks.size()
3124                 <<", "<<has_dungeons
3125                 <<", "<<completely_underground
3126                 <<", "<<some_part_underground
3127                 <<std::endl;
3128         }
3129
3130         /*
3131                 Debug mode operation
3132         */
3133         bool haxmode = g_settings.getBool("haxmode");
3134         if(haxmode)
3135         {
3136                 // Don't calculate lighting at all
3137                 //lighting_invalidated_blocks.clear();
3138         }
3139
3140         return block;
3141 }
3142
3143 void ServerMap::createDir(std::string path)
3144 {
3145         if(fs::CreateDir(path) == false)
3146         {
3147                 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3148                                 <<"\""<<path<<"\""<<std::endl;
3149                 throw BaseException("ServerMap failed to create directory");
3150         }
3151 }
3152
3153 std::string ServerMap::getSectorSubDir(v2s16 pos)
3154 {
3155         char cc[9];
3156         snprintf(cc, 9, "%.4x%.4x",
3157                         (unsigned int)pos.X&0xffff,
3158                         (unsigned int)pos.Y&0xffff);
3159
3160         return std::string(cc);
3161 }
3162
3163 std::string ServerMap::getSectorDir(v2s16 pos)
3164 {
3165         return m_savedir + "/sectors/" + getSectorSubDir(pos);
3166 }
3167
3168 v2s16 ServerMap::getSectorPos(std::string dirname)
3169 {
3170         if(dirname.size() != 8)
3171                 throw InvalidFilenameException("Invalid sector directory name");
3172         unsigned int x, y;
3173         int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
3174         if(r != 2)
3175                 throw InvalidFilenameException("Invalid sector directory name");
3176         v2s16 pos((s16)x, (s16)y);
3177         return pos;
3178 }
3179
3180 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3181 {
3182         v2s16 p2d = getSectorPos(sectordir);
3183
3184         if(blockfile.size() != 4){
3185                 throw InvalidFilenameException("Invalid block filename");
3186         }
3187         unsigned int y;
3188         int r = sscanf(blockfile.c_str(), "%4x", &y);
3189         if(r != 1)
3190                 throw InvalidFilenameException("Invalid block filename");
3191         return v3s16(p2d.X, y, p2d.Y);
3192 }
3193
3194 // Debug helpers
3195 #define ENABLE_SECTOR_SAVING 1
3196 #define ENABLE_SECTOR_LOADING 1
3197 #define ENABLE_BLOCK_SAVING 1
3198 #define ENABLE_BLOCK_LOADING 1
3199
3200 void ServerMap::save(bool only_changed)
3201 {
3202         DSTACK(__FUNCTION_NAME);
3203         if(m_map_saving_enabled == false)
3204         {
3205                 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
3206                 return;
3207         }
3208         
3209         if(only_changed == false)
3210                 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
3211                                 <<std::endl;
3212         
3213         saveMasterHeightmap();
3214         
3215         u32 sector_meta_count = 0;
3216         u32 block_count = 0;
3217         
3218         { //sectorlock
3219         JMutexAutoLock lock(m_sector_mutex);
3220         
3221         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3222         for(; i.atEnd() == false; i++)
3223         {
3224                 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3225                 assert(sector->getId() == MAPSECTOR_SERVER);
3226                 
3227                 if(ENABLE_SECTOR_SAVING)
3228                 {
3229                         if(sector->differs_from_disk || only_changed == false)
3230                         {
3231                                 saveSectorMeta(sector);
3232                                 sector_meta_count++;
3233                         }
3234                 }
3235                 if(ENABLE_BLOCK_SAVING)
3236                 {
3237                         core::list<MapBlock*> blocks;
3238                         sector->getBlocks(blocks);
3239                         core::list<MapBlock*>::Iterator j;
3240                         for(j=blocks.begin(); j!=blocks.end(); j++)
3241                         {
3242                                 MapBlock *block = *j;
3243                                 if(block->getChangedFlag() || only_changed == false)
3244                                 {
3245                                         saveBlock(block);
3246                                         block_count++;
3247                                 }
3248                         }
3249                 }
3250         }
3251
3252         }//sectorlock
3253         
3254         /*
3255                 Only print if something happened or saved whole map
3256         */
3257         if(only_changed == false || sector_meta_count != 0
3258                         || block_count != 0)
3259         {
3260                 dstream<<DTIME<<"ServerMap: Written: "
3261                                 <<sector_meta_count<<" sector metadata files, "
3262                                 <<block_count<<" block files"
3263                                 <<std::endl;
3264         }
3265 }
3266
3267 void ServerMap::loadAll()
3268 {
3269         DSTACK(__FUNCTION_NAME);
3270         dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
3271
3272         loadMasterHeightmap();
3273
3274         std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
3275
3276         dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
3277         
3278         JMutexAutoLock lock(m_sector_mutex);
3279         
3280         s32 counter = 0;
3281         s32 printed_counter = -100000;
3282         s32 count = list.size();
3283
3284         std::vector<fs::DirListNode>::iterator i;
3285         for(i=list.begin(); i!=list.end(); i++)
3286         {
3287                 if(counter > printed_counter + 10)
3288                 {
3289                         dstream<<DTIME<<counter<<"/"<<count<<std::endl;
3290                         printed_counter = counter;
3291                 }
3292                 counter++;
3293
3294                 MapSector *sector = NULL;
3295
3296                 // We want directories
3297                 if(i->dir == false)
3298                         continue;
3299                 try{
3300                         sector = loadSectorMeta(i->name);
3301                 }
3302                 catch(InvalidFilenameException &e)
3303                 {
3304                         // This catches unknown crap in directory
3305                 }
3306                 
3307                 if(ENABLE_BLOCK_LOADING)
3308                 {
3309                         std::vector<fs::DirListNode> list2 = fs::GetDirListing
3310                                         (m_savedir+"/sectors/"+i->name);
3311                         std::vector<fs::DirListNode>::iterator i2;
3312                         for(i2=list2.begin(); i2!=list2.end(); i2++)
3313                         {
3314                                 // We want files
3315                                 if(i2->dir)
3316                                         continue;
3317                                 try{
3318                                         loadBlock(i->name, i2->name, sector);
3319                                 }
3320                                 catch(InvalidFilenameException &e)
3321                                 {
3322                                         // This catches unknown crap in directory
3323                                 }
3324                         }
3325                 }
3326         }
3327         dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
3328 }
3329
3330 void ServerMap::saveMasterHeightmap()
3331 {
3332         DSTACK(__FUNCTION_NAME);
3333         createDir(m_savedir);
3334         
3335         std::string fullpath = m_savedir + "/master_heightmap";
3336         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3337         if(o.good() == false)
3338                 throw FileNotGoodException("Cannot open master heightmap");
3339         
3340         // Format used for writing
3341         u8 version = SER_FMT_VER_HIGHEST;
3342
3343 #if 0
3344         SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
3345         /*
3346                 [0] u8 serialization version
3347                 [1] X master heightmap
3348         */
3349         u32 fullsize = 1 + hmdata.getSize();
3350         SharedBuffer<u8> data(fullsize);
3351
3352         data[0] = version;
3353         memcpy(&data[1], *hmdata, hmdata.getSize());
3354
3355         o.write((const char*)*data, fullsize);
3356 #endif
3357         
3358         m_heightmap->serialize(o, version);
3359 }
3360
3361 void ServerMap::loadMasterHeightmap()
3362 {
3363         DSTACK(__FUNCTION_NAME);
3364         std::string fullpath = m_savedir + "/master_heightmap";
3365         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3366         if(is.good() == false)
3367                 throw FileNotGoodException("Cannot open master heightmap");
3368         
3369         if(m_heightmap != NULL)
3370                 delete m_heightmap;
3371                 
3372         m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
3373 }
3374
3375 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3376 {
3377         DSTACK(__FUNCTION_NAME);
3378         // Format used for writing
3379         u8 version = SER_FMT_VER_HIGHEST;
3380         // Get destination
3381         v2s16 pos = sector->getPos();
3382         createDir(m_savedir);
3383         createDir(m_savedir+"/sectors");
3384         std::string dir = getSectorDir(pos);
3385         createDir(dir);
3386         
3387         std::string fullpath = dir + "/heightmap";
3388         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3389         if(o.good() == false)
3390                 throw FileNotGoodException("Cannot open master heightmap");
3391
3392         sector->serialize(o, version);
3393         
3394         sector->differs_from_disk = false;
3395 }
3396
3397 MapSector* ServerMap::loadSectorMeta(std::string dirname)
3398 {
3399         DSTACK(__FUNCTION_NAME);
3400         // Get destination
3401         v2s16 p2d = getSectorPos(dirname);
3402         std::string dir = m_savedir + "/sectors/" + dirname;
3403         
3404         std::string fullpath = dir + "/heightmap";
3405         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3406         if(is.good() == false)
3407                 throw FileNotGoodException("Cannot open sector heightmap");
3408
3409         ServerMapSector *sector = ServerMapSector::deSerialize
3410                         (is, this, p2d, &m_hwrapper, m_sectors);
3411         
3412         sector->differs_from_disk = false;
3413
3414         return sector;
3415 }
3416
3417 bool ServerMap::loadSectorFull(v2s16 p2d)
3418 {
3419         DSTACK(__FUNCTION_NAME);
3420         std::string sectorsubdir = getSectorSubDir(p2d);
3421
3422         MapSector *sector = NULL;
3423
3424         JMutexAutoLock lock(m_sector_mutex);
3425
3426         try{
3427                 sector = loadSectorMeta(sectorsubdir);
3428         }
3429         catch(InvalidFilenameException &e)
3430         {
3431                 return false;
3432         }
3433         catch(FileNotGoodException &e)
3434         {
3435                 return false;
3436         }
3437         catch(std::exception &e)
3438         {
3439                 return false;
3440         }
3441
3442         if(ENABLE_BLOCK_LOADING)
3443         {
3444                 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3445                                 (m_savedir+"/sectors/"+sectorsubdir);
3446                 std::vector<fs::DirListNode>::iterator i2;
3447                 for(i2=list2.begin(); i2!=list2.end(); i2++)
3448                 {
3449                         // We want files
3450                         if(i2->dir)
3451                                 continue;
3452                         try{
3453                                 loadBlock(sectorsubdir, i2->name, sector);
3454                         }
3455                         catch(InvalidFilenameException &e)
3456                         {
3457                                 // This catches unknown crap in directory
3458                         }
3459                 }
3460         }
3461         return true;
3462 }
3463
3464 #if 0
3465 bool ServerMap::deFlushSector(v2s16 p2d)
3466 {
3467         DSTACK(__FUNCTION_NAME);
3468         // See if it already exists in memory
3469         try{
3470                 MapSector *sector = getSectorNoGenerate(p2d);
3471                 return true;
3472         }
3473         catch(InvalidPositionException &e)
3474         {
3475                 /*
3476                         Try to load the sector from disk.
3477                 */
3478                 if(loadSectorFull(p2d) == true)
3479                 {
3480                         return true;
3481                 }
3482         }
3483         return false;
3484 }
3485 #endif
3486
3487 void ServerMap::saveBlock(MapBlock *block)
3488 {
3489         DSTACK(__FUNCTION_NAME);
3490         /*
3491                 Dummy blocks are not written
3492         */
3493         if(block->isDummy())
3494         {
3495                 /*v3s16 p = block->getPos();
3496                 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3497                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3498                 return;
3499         }
3500
3501         // Format used for writing
3502         u8 version = SER_FMT_VER_HIGHEST;
3503         // Get destination
3504         v3s16 p3d = block->getPos();
3505         v2s16 p2d(p3d.X, p3d.Z);
3506         createDir(m_savedir);
3507         createDir(m_savedir+"/sectors");
3508         std::string dir = getSectorDir(p2d);
3509         createDir(dir);
3510         
3511         // Block file is map/sectors/xxxxxxxx/xxxx
3512         char cc[5];
3513         snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
3514         std::string fullpath = dir + "/" + cc;
3515         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3516         if(o.good() == false)
3517                 throw FileNotGoodException("Cannot open block data");
3518
3519         /*
3520                 [0] u8 serialization version
3521                 [1] data
3522         */
3523         o.write((char*)&version, 1);
3524         
3525         block->serialize(o, version);
3526
3527         /*
3528                 Versions up from 9 have block objects.
3529         */
3530         if(version >= 9)
3531         {
3532                 block->serializeObjects(o, version);
3533         }
3534         
3535         // We just wrote it to the disk
3536         block->resetChangedFlag();
3537 }
3538
3539 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
3540 {
3541         DSTACK(__FUNCTION_NAME);
3542
3543         try{
3544
3545         // Block file is map/sectors/xxxxxxxx/xxxx
3546         std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
3547         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3548         if(is.good() == false)
3549                 throw FileNotGoodException("Cannot open block file");
3550
3551         v3s16 p3d = getBlockPos(sectordir, blockfile);
3552         v2s16 p2d(p3d.X, p3d.Z);
3553         
3554         assert(sector->getPos() == p2d);
3555         
3556         u8 version = SER_FMT_VER_INVALID;
3557         is.read((char*)&version, 1);
3558
3559         /*u32 block_size = MapBlock::serializedLength(version);
3560         SharedBuffer<u8> data(block_size);
3561         is.read((char*)*data, block_size);*/
3562
3563         // This will always return a sector because we're the server
3564         //MapSector *sector = emergeSector(p2d);
3565
3566         MapBlock *block = NULL;
3567         bool created_new = false;
3568         try{
3569                 block = sector->getBlockNoCreate(p3d.Y);
3570         }
3571         catch(InvalidPositionException &e)
3572         {
3573                 block = sector->createBlankBlockNoInsert(p3d.Y);
3574                 created_new = true;
3575         }
3576         
3577         // deserialize block data
3578         block->deSerialize(is, version);
3579         
3580         /*
3581                 Versions up from 9 have block objects.
3582         */
3583         if(version >= 9)
3584         {
3585                 block->updateObjects(is, version, NULL, 0);
3586         }
3587
3588         if(created_new)
3589                 sector->insertBlock(block);
3590         
3591         /*
3592                 Convert old formats to new and save
3593         */
3594
3595         // Save old format blocks in new format
3596         if(version < SER_FMT_VER_HIGHEST)
3597         {
3598                 saveBlock(block);
3599         }
3600         
3601         // We just loaded it from the disk, so it's up-to-date.
3602         block->resetChangedFlag();
3603
3604         }
3605         catch(SerializationError &e)
3606         {
3607                 dstream<<"WARNING: Invalid block data on disk "
3608                                 "(SerializationError). Ignoring."
3609                                 <<std::endl;
3610         }
3611 }
3612
3613 // Gets from master heightmap
3614 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
3615 {
3616         assert(m_heightmap != NULL);
3617         /*
3618                 Corner definition:
3619                 v2s16(0,0),
3620                 v2s16(1,0),
3621                 v2s16(1,1),
3622                 v2s16(0,1),
3623         */
3624         corners[0] = m_heightmap->getGroundHeight
3625                         ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
3626         corners[1] = m_heightmap->getGroundHeight
3627                         ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
3628         corners[2] = m_heightmap->getGroundHeight
3629                         ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
3630         corners[3] = m_heightmap->getGroundHeight
3631                         ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
3632 }
3633
3634 void ServerMap::PrintInfo(std::ostream &out)
3635 {
3636         out<<"ServerMap: ";
3637 }
3638
3639 #ifndef SERVER
3640
3641 /*
3642         ClientMap
3643 */
3644
3645 ClientMap::ClientMap(
3646                 Client *client,
3647                 MapDrawControl &control,
3648                 scene::ISceneNode* parent,
3649                 scene::ISceneManager* mgr,
3650                 s32 id
3651 ):
3652         Map(dout_client),
3653         scene::ISceneNode(parent, mgr, id),
3654         m_client(client),
3655         mesh(NULL),
3656         m_control(control)
3657 {
3658         mesh_mutex.Init();
3659
3660         /*m_box = core::aabbox3d<f32>(0,0,0,
3661                         map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
3662         /*m_box = core::aabbox3d<f32>(0,0,0,
3663                         map->getSizeNodes().X * BS,
3664                         map->getSizeNodes().Y * BS,
3665                         map->getSizeNodes().Z * BS);*/
3666         m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3667                         BS*1000000,BS*1000000,BS*1000000);
3668         
3669         //setPosition(v3f(BS,BS,BS));
3670 }
3671
3672 ClientMap::~ClientMap()
3673 {
3674         JMutexAutoLock lock(mesh_mutex);
3675         
3676         if(mesh != NULL)
3677         {
3678                 mesh->drop();
3679                 mesh = NULL;
3680         }
3681 }
3682
3683 MapSector * ClientMap::emergeSector(v2s16 p2d)
3684 {
3685         DSTACK(__FUNCTION_NAME);
3686         // Check that it doesn't exist already
3687         try{
3688                 return getSectorNoGenerate(p2d);
3689         }
3690         catch(InvalidPositionException &e)
3691         {
3692         }
3693         
3694         // Create a sector with no heightmaps
3695         ClientMapSector *sector = new ClientMapSector(this, p2d);
3696         
3697         {
3698                 JMutexAutoLock lock(m_sector_mutex);
3699                 m_sectors.insert(p2d, sector);
3700         }
3701         
3702         return sector;
3703 }
3704
3705 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3706 {
3707         DSTACK(__FUNCTION_NAME);
3708         ClientMapSector *sector = NULL;
3709
3710         JMutexAutoLock lock(m_sector_mutex);
3711         
3712         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3713
3714         if(n != NULL)
3715         {
3716                 sector = (ClientMapSector*)n->getValue();
3717                 assert(sector->getId() == MAPSECTOR_CLIENT);
3718         }
3719         else
3720         {
3721                 sector = new ClientMapSector(this, p2d);
3722                 {
3723                         JMutexAutoLock lock(m_sector_mutex);
3724                         m_sectors.insert(p2d, sector);
3725                 }
3726         }
3727
3728         sector->deSerialize(is);
3729 }
3730
3731 void ClientMap::OnRegisterSceneNode()
3732 {
3733         if(IsVisible)
3734         {
3735                 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3736                 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3737         }
3738
3739         ISceneNode::OnRegisterSceneNode();
3740 }
3741
3742 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3743 {
3744         //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3745         DSTACK(__FUNCTION_NAME);
3746
3747         bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3748
3749         /*
3750                 Get time for measuring timeout.
3751                 
3752                 Measuring time is very useful for long delays when the
3753                 machine is swapping a lot.
3754         */
3755         int time1 = time(0);
3756
3757         u32 daynight_ratio = m_client->getDayNightRatio();
3758
3759         m_camera_mutex.Lock();
3760         v3f camera_position = m_camera_position;
3761         v3f camera_direction = m_camera_direction;
3762         m_camera_mutex.Unlock();
3763
3764         /*
3765                 Get all blocks and draw all visible ones
3766         */
3767
3768         v3s16 cam_pos_nodes(
3769                         camera_position.X / BS,
3770                         camera_position.Y / BS,
3771                         camera_position.Z / BS);
3772
3773         v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3774
3775         v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3776         v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3777
3778         // Take a fair amount as we will be dropping more out later
3779         v3s16 p_blocks_min(
3780                         p_nodes_min.X / MAP_BLOCKSIZE - 1,
3781                         p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3782                         p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3783         v3s16 p_blocks_max(
3784                         p_nodes_max.X / MAP_BLOCKSIZE + 1,
3785                         p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3786                         p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3787         
3788         u32 vertex_count = 0;
3789         
3790         // For limiting number of mesh updates per frame
3791         u32 mesh_update_count = 0;
3792         
3793         u32 blocks_would_have_drawn = 0;
3794         u32 blocks_drawn = 0;
3795
3796         //NOTE: The sectors map should be locked but we're not doing it
3797         // because it'd cause too much delays
3798
3799         int timecheck_counter = 0;
3800         core::map<v2s16, MapSector*>::Iterator si;
3801         si = m_sectors.getIterator();
3802         for(; si.atEnd() == false; si++)
3803         {
3804                 {
3805                         timecheck_counter++;
3806                         if(timecheck_counter > 50)
3807                         {
3808                                 int time2 = time(0);
3809                                 if(time2 > time1 + 4)
3810                                 {
3811                                         dstream<<"ClientMap::renderMap(): "
3812                                                 "Rendering takes ages, returning."
3813                                                 <<std::endl;
3814                                         return;
3815                                 }
3816                         }
3817                 }
3818
3819                 MapSector *sector = si.getNode()->getValue();
3820                 v2s16 sp = sector->getPos();
3821                 
3822                 if(m_control.range_all == false)
3823                 {
3824                         if(sp.X < p_blocks_min.X
3825                         || sp.X > p_blocks_max.X
3826                         || sp.Y < p_blocks_min.Z
3827                         || sp.Y > p_blocks_max.Z)
3828                                 continue;
3829                 }
3830
3831                 core::list< MapBlock * > sectorblocks;
3832                 sector->getBlocks(sectorblocks);
3833                 
3834                 /*
3835                         Draw blocks
3836                 */
3837
3838                 core::list< MapBlock * >::Iterator i;
3839                 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3840                 {
3841                         MapBlock *block = *i;
3842
3843                         /*
3844                                 Compare block position to camera position, skip
3845                                 if not seen on display
3846                         */
3847                         
3848                         float range = 100000 * BS;
3849                         if(m_control.range_all == false)
3850                                 range = m_control.wanted_range * BS;
3851
3852                         if(isBlockInSight(block->getPos(), camera_position,
3853                                         camera_direction, range) == false)
3854                         {
3855                                 continue;
3856                         }
3857
3858 #if 0                   
3859                         v3s16 blockpos_nodes = block->getPosRelative();
3860                         
3861                         // Block center position
3862                         v3f blockpos(
3863                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3864                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3865                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3866                         );
3867
3868                         // Block position relative to camera
3869                         v3f blockpos_relative = blockpos - camera_position;
3870
3871                         // Distance in camera direction (+=front, -=back)
3872                         f32 dforward = blockpos_relative.dotProduct(camera_direction);
3873
3874                         // Total distance
3875                         f32 d = blockpos_relative.getLength();
3876                         
3877                         if(m_control.range_all == false)
3878                         {
3879                                 // If block is far away, don't draw it
3880                                 if(d > m_control.wanted_range * BS)
3881                                         continue;
3882                         }
3883                         
3884                         // Maximum radius of a block
3885                         f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3886                         
3887                         // If block is (nearly) touching the camera, don't
3888                         // bother validating further (that is, render it anyway)
3889                         if(d > block_max_radius * 1.5)
3890                         {
3891                                 // Cosine of the angle between the camera direction
3892                                 // and the block direction (camera_direction is an unit vector)
3893                                 f32 cosangle = dforward / d;
3894                                 
3895                                 // Compensate for the size of the block
3896                                 // (as the block has to be shown even if it's a bit off FOV)
3897                                 // This is an estimate.
3898                                 cosangle += block_max_radius / dforward;
3899
3900                                 // If block is not in the field of view, skip it
3901                                 //if(cosangle < cos(FOV_ANGLE/2))
3902                                 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3903                                         continue;
3904                         }
3905 #endif                  
3906
3907                         v3s16 blockpos_nodes = block->getPosRelative();
3908                         
3909                         // Block center position
3910                         v3f blockpos(
3911                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3912                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3913                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3914                         );
3915
3916                         // Block position relative to camera
3917                         v3f blockpos_relative = blockpos - camera_position;
3918
3919                         // Total distance
3920                         f32 d = blockpos_relative.getLength();
3921                         
3922 #if 1
3923                         /*
3924                                 Update expired mesh
3925                         */
3926
3927                         bool mesh_expired = false;
3928                         
3929                         {
3930                                 JMutexAutoLock lock(block->mesh_mutex);
3931
3932                                 mesh_expired = block->getMeshExpired();
3933
3934                                 // Mesh has not been expired and there is no mesh:
3935                                 // block has no content
3936                                 if(block->mesh == NULL && mesh_expired == false)
3937                                         continue;
3938                         }
3939
3940                         f32 faraway = BS*50;
3941                         //f32 faraway = m_control.wanted_range * BS;
3942                         
3943                         /*
3944                                 This has to be done with the mesh_mutex unlocked
3945                         */
3946                         // Pretty random but this should work somewhat nicely
3947                         if(mesh_expired && (
3948                                         (mesh_update_count < 3
3949                                                 && (d < faraway || mesh_update_count < 2)
3950                                         )
3951                                         || 
3952                                         (m_control.range_all && mesh_update_count < 20)
3953                                 )
3954                         )
3955                         /*if(mesh_expired && mesh_update_count < 6
3956                                         && (d < faraway || mesh_update_count < 3))*/
3957                         {
3958                                 mesh_update_count++;
3959
3960                                 // Mesh has been expired: generate new mesh
3961                                 //block->updateMeshes(daynight_i);
3962                                 block->updateMesh(daynight_ratio);
3963
3964                                 mesh_expired = false;
3965                         }
3966                         
3967                         /*
3968                                 Don't draw an expired mesh that is far away
3969                         */
3970                         /*if(mesh_expired && d >= faraway)
3971                         //if(mesh_expired)
3972                         {
3973                                 // Instead, delete it
3974                                 JMutexAutoLock lock(block->mesh_mutex);
3975                                 if(block->mesh)
3976                                 {
3977                                         block->mesh->drop();
3978                                         block->mesh = NULL;
3979                                 }
3980                                 // And continue to next block
3981                                 continue;
3982                         }*/
3983 #endif
3984                         /*
3985                                 Draw the faces of the block
3986                         */
3987                         {
3988                                 JMutexAutoLock lock(block->mesh_mutex);
3989
3990                                 scene::SMesh *mesh = block->mesh;
3991
3992                                 if(mesh == NULL)
3993                                         continue;
3994                                 
3995                                 blocks_would_have_drawn++;
3996                                 if(blocks_drawn >= m_control.wanted_max_blocks
3997                                                 && m_control.range_all == false
3998                                                 && d > m_control.wanted_min_range * BS)
3999                                         continue;
4000                                 blocks_drawn++;
4001
4002                                 u32 c = mesh->getMeshBufferCount();
4003
4004                                 for(u32 i=0; i<c; i++)
4005                                 {
4006                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
4007                                         const video::SMaterial& material = buf->getMaterial();
4008                                         video::IMaterialRenderer* rnd =
4009                                                         driver->getMaterialRenderer(material.MaterialType);
4010                                         bool transparent = (rnd && rnd->isTransparent());
4011                                         // Render transparent on transparent pass and likewise.
4012                                         if(transparent == is_transparent_pass)
4013                                         {
4014                                                 driver->setMaterial(buf->getMaterial());
4015                                                 driver->drawMeshBuffer(buf);
4016                                                 vertex_count += buf->getVertexCount();
4017                                         }
4018                                 }
4019                         }
4020                 } // foreach sectorblocks
4021         }
4022         
4023         m_control.blocks_drawn = blocks_drawn;
4024         m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4025
4026         /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4027                         <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4028 }
4029
4030 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod, bool *changed)
4031 {
4032         /*
4033                 Add it to all blocks touching it
4034         */
4035         v3s16 dirs[7] = {
4036                 v3s16(0,0,0), // this
4037                 v3s16(0,0,1), // back
4038                 v3s16(0,1,0), // top
4039                 v3s16(1,0,0), // right
4040                 v3s16(0,0,-1), // front
4041                 v3s16(0,-1,0), // bottom
4042                 v3s16(-1,0,0), // left
4043         };
4044         for(u16 i=0; i<7; i++)
4045         {
4046                 v3s16 p2 = p + dirs[i];
4047                 // Block position of neighbor (or requested) node
4048                 v3s16 blockpos = getNodeBlockPos(p2);
4049                 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4050                 if(blockref == NULL)
4051                         continue;
4052                 // Relative position of requested node
4053                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4054                 if(blockref->setTempMod(relpos, mod))
4055                 {
4056                         if(changed != NULL)
4057                                 *changed = true;
4058                 }
4059         }
4060         return getNodeBlockPos(p);
4061 }
4062 v3s16 ClientMap::clearTempMod(v3s16 p, bool *changed)
4063 {
4064         v3s16 dirs[7] = {
4065                 v3s16(0,0,0), // this
4066                 v3s16(0,0,1), // back
4067                 v3s16(0,1,0), // top
4068                 v3s16(1,0,0), // right
4069                 v3s16(0,0,-1), // front
4070                 v3s16(0,-1,0), // bottom
4071                 v3s16(-1,0,0), // left
4072         };
4073         for(u16 i=0; i<7; i++)
4074         {
4075                 v3s16 p2 = p + dirs[i];
4076                 // Block position of neighbor (or requested) node
4077                 v3s16 blockpos = getNodeBlockPos(p2);
4078                 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4079                 if(blockref == NULL)
4080                         continue;
4081                 // Relative position of requested node
4082                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4083                 if(blockref->clearTempMod(relpos))
4084                 {
4085                         if(changed != NULL)
4086                                 *changed = true;
4087                 }
4088         }
4089         return getNodeBlockPos(p);
4090 }
4091
4092 void ClientMap::PrintInfo(std::ostream &out)
4093 {
4094         out<<"ClientMap: ";
4095 }
4096
4097 #endif // !SERVER
4098
4099 /*
4100         MapVoxelManipulator
4101 */
4102
4103 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4104 {
4105         m_map = map;
4106 }
4107
4108 MapVoxelManipulator::~MapVoxelManipulator()
4109 {
4110         /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4111                         <<std::endl;*/
4112 }
4113
4114 #if 1
4115 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4116 {
4117         TimeTaker timer1("emerge", &emerge_time);
4118
4119         // Units of these are MapBlocks
4120         v3s16 p_min = getNodeBlockPos(a.MinEdge);
4121         v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4122
4123         VoxelArea block_area_nodes
4124                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4125
4126         addArea(block_area_nodes);
4127
4128         for(s32 z=p_min.Z; z<=p_max.Z; z++)
4129         for(s32 y=p_min.Y; y<=p_max.Y; y++)
4130         for(s32 x=p_min.X; x<=p_max.X; x++)
4131         {
4132                 v3s16 p(x,y,z);
4133                 core::map<v3s16, bool>::Node *n;
4134                 n = m_loaded_blocks.find(p);
4135                 if(n != NULL)
4136                         continue;
4137                 
4138                 bool block_data_inexistent = false;
4139                 try
4140                 {
4141                         TimeTaker timer1("emerge load", &emerge_load_time);
4142
4143                         /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4144                                         <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4145                                         <<" wanted area: ";
4146                         a.print(dstream);
4147                         dstream<<std::endl;*/
4148                         
4149                         MapBlock *block = m_map->getBlockNoCreate(p);
4150                         if(block->isDummy())
4151                                 block_data_inexistent = true;
4152                         else
4153                                 block->copyTo(*this);
4154                 }
4155                 catch(InvalidPositionException &e)
4156                 {
4157                         block_data_inexistent = true;
4158                 }
4159
4160                 if(block_data_inexistent)
4161                 {
4162                         VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4163                         // Fill with VOXELFLAG_INEXISTENT
4164                         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4165                         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4166                         {
4167                                 s32 i = m_area.index(a.MinEdge.X,y,z);
4168                                 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4169                         }
4170                 }
4171
4172                 m_loaded_blocks.insert(p, true);
4173         }
4174
4175         //dstream<<"emerge done"<<std::endl;
4176 }
4177 #endif
4178
4179 #if 0
4180 /*
4181         NOTE: This is slow
4182 */
4183 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4184 {
4185         TimeTaker timer1("emerge", &emerge_time);
4186         
4187         v3s16 size = a.getExtent();
4188         
4189         VoxelArea padded = a;
4190         padded.pad(m_area.getExtent() / 4);
4191         addArea(padded);
4192
4193         for(s16 z=0; z<size.Z; z++)
4194         for(s16 y=0; y<size.Y; y++)
4195         for(s16 x=0; x<size.X; x++)
4196         {
4197                 v3s16 p(x,y,z);
4198                 s32 i = m_area.index(a.MinEdge + p);
4199                 // Don't touch nodes that have already been loaded
4200                 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4201                         continue;
4202                 try
4203                 {
4204                         TimeTaker timer1("emerge load", &emerge_load_time);
4205                         MapNode n = m_map->getNode(a.MinEdge + p);
4206                         m_data[i] = n;
4207                         m_flags[i] = 0;
4208                 }
4209                 catch(InvalidPositionException &e)
4210                 {
4211                         m_flags[i] = VOXELFLAG_INEXISTENT;
4212                 }
4213         }
4214 }
4215 #endif
4216
4217
4218 /*
4219         TODO: Add an option to only update eg. water and air nodes.
4220               This will make it interfere less with important stuff if
4221                   run on background.
4222 */
4223 void MapVoxelManipulator::blitBack
4224                 (core::map<v3s16, MapBlock*> & modified_blocks)
4225 {
4226         if(m_area.getExtent() == v3s16(0,0,0))
4227                 return;
4228         
4229         //TimeTaker timer1("blitBack");
4230
4231         /*dstream<<"blitBack(): m_loaded_blocks.size()="
4232                         <<m_loaded_blocks.size()<<std::endl;*/
4233         
4234         /*
4235                 Initialize block cache
4236         */
4237         v3s16 blockpos_last;
4238         MapBlock *block = NULL;
4239         bool block_checked_in_modified = false;
4240
4241         for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4242         for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4243         for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4244         {
4245                 v3s16 p(x,y,z);
4246
4247                 u8 f = m_flags[m_area.index(p)];
4248                 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4249                         continue;
4250
4251                 MapNode &n = m_data[m_area.index(p)];
4252                         
4253                 v3s16 blockpos = getNodeBlockPos(p);
4254                 
4255                 try
4256                 {
4257                         // Get block
4258                         if(block == NULL || blockpos != blockpos_last){
4259                                 block = m_map->getBlockNoCreate(blockpos);
4260                                 blockpos_last = blockpos;
4261                                 block_checked_in_modified = false;
4262                         }
4263                         
4264                         // Calculate relative position in block
4265                         v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4266
4267                         // Don't continue if nothing has changed here
4268                         if(block->getNode(relpos) == n)
4269                                 continue;
4270
4271                         //m_map->setNode(m_area.MinEdge + p, n);
4272                         block->setNode(relpos, n);
4273                         
4274                         /*
4275                                 Make sure block is in modified_blocks
4276                         */
4277                         if(block_checked_in_modified == false)
4278                         {
4279                                 modified_blocks[blockpos] = block;
4280                                 block_checked_in_modified = true;
4281                         }
4282                 }
4283                 catch(InvalidPositionException &e)
4284                 {
4285                 }
4286         }
4287 }
4288
4289 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4290                 MapVoxelManipulator(map)
4291 {
4292 }
4293
4294 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4295 {
4296 }
4297
4298 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4299 {
4300         // Just create the area to avoid segfaults
4301         VoxelManipulator::emerge(a, caller_id);
4302
4303         /*
4304                 Just create the area to avoid segfaults
4305         */
4306         /*addArea(a);
4307         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4308         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4309         for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
4310         {
4311                 s32 i = m_area.index(x,y,z);
4312                 // Don't touch nodes that have already been loaded
4313                 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4314                         continue;
4315                 m_flags[i] = VOXELFLAG_INEXISTENT;
4316         }*/
4317 }
4318
4319 void ManualMapVoxelManipulator::initialEmerge(
4320                 v3s16 blockpos_min, v3s16 blockpos_max)
4321 {
4322         TimeTaker timer1("emerge", &emerge_time);
4323
4324         // Units of these are MapBlocks
4325         v3s16 p_min = blockpos_min;
4326         v3s16 p_max = blockpos_max;
4327
4328         VoxelArea block_area_nodes
4329                         (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4330
4331         addArea(block_area_nodes);
4332
4333         for(s32 z=p_min.Z; z<=p_max.Z; z++)
4334         for(s32 y=p_min.Y; y<=p_max.Y; y++)
4335         for(s32 x=p_min.X; x<=p_max.X; x++)
4336         {
4337                 v3s16 p(x,y,z);
4338                 core::map<v3s16, bool>::Node *n;
4339                 n = m_loaded_blocks.find(p);
4340                 if(n != NULL)
4341                         continue;
4342                 
4343                 bool block_data_inexistent = false;
4344                 try
4345                 {
4346                         TimeTaker timer1("emerge load", &emerge_load_time);
4347
4348                         MapBlock *block = m_map->getBlockNoCreate(p);
4349                         if(block->isDummy())
4350                                 block_data_inexistent = true;
4351                         else
4352                                 block->copyTo(*this);
4353                 }
4354                 catch(InvalidPositionException &e)
4355                 {
4356                         block_data_inexistent = true;
4357                 }
4358
4359                 if(block_data_inexistent)
4360                 {
4361                         VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4362                         // Fill with VOXELFLAG_INEXISTENT
4363                         for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4364                         for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4365                         {
4366                                 s32 i = m_area.index(a.MinEdge.X,y,z);
4367                                 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4368                         }
4369                 }
4370
4371                 m_loaded_blocks.insert(p, true);
4372         }
4373 }
4374
4375 //END