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