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