Initial files
[oweals/minetest.git] / src / map.cpp
1 /*
2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
3 */
4
5 #include "map.h"
6 //#include "player.h"
7 #include "main.h"
8 #include "jmutexautolock.h"
9 #include "client.h"
10 #include "filesys.h"
11 #include "utility.h"
12
13 #ifdef _WIN32
14         #include <windows.h>
15         #define sleep_ms(x) Sleep(x)
16 #else
17         #include <unistd.h>
18         #define sleep_ms(x) usleep(x*1000)
19 #endif
20
21 Map::Map(std::ostream &dout):
22         m_dout(dout),
23         m_camera_position(0,0,0),
24         m_camera_direction(0,0,1),
25         m_sector_cache(NULL),
26         m_hwrapper(this),
27         drawoffset(0,0,0)
28 {
29         m_sector_mutex.Init();
30         m_camera_mutex.Init();
31         assert(m_sector_mutex.IsInitialized());
32         assert(m_camera_mutex.IsInitialized());
33         
34         // Get this so that the player can stay on it at first
35         //getSector(v2s16(0,0));
36 }
37
38 Map::~Map()
39 {
40         /*
41                 Stop updater thread
42         */
43         /*updater.setRun(false);
44         while(updater.IsRunning())
45                 sleep_s(1);*/
46
47         /*
48                 Free all MapSectors.
49         */
50         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
51         for(; i.atEnd() == false; i++)
52         {
53                 MapSector *sector = i.getNode()->getValue();
54                 delete sector;
55         }
56 }
57
58 /*bool Map::sectorExists(v2s16 p)
59 {
60         JMutexAutoLock lock(m_sector_mutex);
61         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
62         return (n != NULL);
63 }*/
64
65 MapSector * Map::getSectorNoGenerate(v2s16 p)
66 {
67         JMutexAutoLock lock(m_sector_mutex);
68
69         if(m_sector_cache != NULL && p == m_sector_cache_p){
70                 MapSector * sector = m_sector_cache;
71                 // Reset inactivity timer
72                 sector->usage_timer = 0.0;
73                 return sector;
74         }
75         
76         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
77         // If sector doesn't exist, throw an exception
78         if(n == NULL)
79         {
80                 throw InvalidPositionException();
81         }
82         
83         MapSector *sector = n->getValue();
84         
85         // Cache the last result
86         m_sector_cache_p = p;
87         m_sector_cache = sector;
88
89         //MapSector * ref(sector);
90         
91         // Reset inactivity timer
92         sector->usage_timer = 0.0;
93         return sector;
94 }
95
96 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
97 {       
98         v2s16 p2d(p3d.X, p3d.Z);
99         MapSector * sector = getSectorNoGenerate(p2d);
100
101         MapBlock *block = sector->getBlockNoCreate(p3d.Y);
102
103         return block;
104 }
105
106 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
107 {
108         dstream<<"Map::getBlock() with generate=true called"
109                         <<std::endl;
110         v2s16 p2d(p3d.X, p3d.Z);
111         //MapSector * sector = getSector(p2d, generate);
112         MapSector * sector = getSectorNoGenerate(p2d);
113
114         if(sector == NULL)
115                 throw InvalidPositionException();
116
117         return sector->getBlockNoCreate(p3d.Y);
118 }*/
119
120 f32 Map::getGroundHeight(v2s16 p, bool generate)
121 {
122         try{
123                 v2s16 sectorpos = getNodeSectorPos(p);
124                 MapSector * sref = getSectorNoGenerate(sectorpos);
125                 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
126                 f32 y = sref->getGroundHeight(relpos);
127                 return y;
128         }
129         catch(InvalidPositionException &e)
130         {
131                 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
132         }
133 }
134
135 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
136 {
137         /*m_dout<<DTIME<<"Map::setGroundHeight(("
138                         <<p.X<<","<<p.Y
139                         <<"), "<<y<<")"<<std::endl;*/
140         v2s16 sectorpos = getNodeSectorPos(p);
141         MapSector * sref = getSectorNoGenerate(sectorpos);
142         v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
143         //sref->mutex.Lock();
144         sref->setGroundHeight(relpos, y);
145         //sref->mutex.Unlock();
146 }
147
148 bool Map::isNodeUnderground(v3s16 p)
149 {
150         v3s16 blockpos = getNodeBlockPos(p);
151         try{
152                 MapBlock * block = getBlockNoCreate(blockpos);
153                 return block->getIsUnderground();
154         }
155         catch(InvalidPositionException &e)
156         {
157                 return false;
158         }
159 }
160
161 #ifdef LKJnb
162 //TODO: Remove: Not used.
163 /*
164         Goes recursively through the neighbours of the node.
165
166         Alters only transparent nodes.
167
168         If the lighting of the neighbour is lower than the lighting of
169         the node was (before changing it to 0 at the step before), the
170         lighting of the neighbour is set to 0 and then the same stuff
171         repeats for the neighbour.
172
173         Some things are made strangely to make it as fast as possible.
174
175         Usage: (for clearing all possible spreaded light of a lamp)
176         NOTE: This is outdated
177                 core::list<v3s16> light_sources;
178                 core::map<v3s16, MapBlock*> modified_blocks;
179                 u8 oldlight = node_at_pos.light;
180                 node_at_pos.setLight(0);
181                 unLightNeighbors(pos, oldlight, light_sources, modified_blocks);
182 */
183 void Map::unLightNeighbors(v3s16 pos, u8 oldlight,
184                 core::map<v3s16, bool> & light_sources,
185                 core::map<v3s16, MapBlock*>  & modified_blocks)
186 {
187         v3s16 dirs[6] = {
188                 v3s16(0,0,1), // back
189                 v3s16(0,1,0), // top
190                 v3s16(1,0,0), // right
191                 v3s16(0,0,-1), // front
192                 v3s16(0,-1,0), // bottom
193                 v3s16(-1,0,0), // left
194         };
195         
196         /*
197                 Initialize block cache
198         */
199         v3s16 blockpos_last;
200         MapBlock *block = NULL;
201         // Cache this a bit, too
202         bool block_checked_in_modified = false;
203         
204         // Loop through 6 neighbors
205         for(u16 i=0; i<6; i++){
206                 // Get the position of the neighbor node
207                 v3s16 n2pos = pos + dirs[i];
208                 
209                 // Get the block where the node is located
210                 v3s16 blockpos = getNodeBlockPos(n2pos);
211
212                 // Only fetch a new block if the block position has changed
213                 try{
214                         if(block == NULL || blockpos != blockpos_last)
215                         {
216                                 block = getBlockNoCreate(blockpos);
217                                 blockpos_last = blockpos;
218                                 
219                                 block_checked_in_modified = false;
220                                 //blockchangecount++;
221                         }
222                 }
223                 catch(InvalidPositionException &e)
224                 {
225                         continue;
226                 }
227
228                 if(block->isDummy())
229                         continue;
230                 
231                 // Calculate relative position in block
232                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
233                 // Get node straight from the block
234                 MapNode n2 = block->getNode(relpos);
235                 
236                 /*
237                         If the neighbor is dimmer than what was specified
238                         as oldlight (the light of the previous node)
239                 */
240                 if(n2.getLight() < oldlight)
241                 {
242                         /*
243                                 And the neighbor is transparent and it has some light
244                         */
245                         if(n2.light_propagates() && n2.getLight() != 0)
246                         {
247                                 /*
248                                         Set light to 0 and recurse.
249                                 */
250                                 u8 current_light = n2.getLight();
251                                 n2.setLight(0);
252                                 block->setNode(relpos, n2);
253                                 unLightNeighbors(n2pos, current_light,
254                                                 light_sources, modified_blocks);
255                                 
256                                 if(block_checked_in_modified == false)
257                                 {
258                                         // If the block is not found in modified_blocks, add.
259                                         if(modified_blocks.find(blockpos) == NULL)
260                                         {
261                                                 modified_blocks.insert(blockpos, block);
262                                         }
263                                         block_checked_in_modified = true;
264                                 }
265                         }
266                 }
267                 else{
268                         //light_sources.push_back(n2pos);
269                         light_sources.insert(n2pos, true);
270                 }
271         }
272 }
273 #endif
274
275 /*
276         Goes recursively through the neighbours of the node.
277
278         Alters only transparent nodes.
279
280         If the lighting of the neighbour is lower than the lighting of
281         the node was (before changing it to 0 at the step before), the
282         lighting of the neighbour is set to 0 and then the same stuff
283         repeats for the neighbour.
284
285         The ending nodes of the routine are stored in light_sources.
286         This is useful when a light is removed. In such case, this
287         routine can be called for the light node and then again for
288         light_sources to re-light the area without the removed light.
289
290         values of from_nodes are lighting values.
291 */
292 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
293                 core::map<v3s16, bool> & light_sources,
294                 core::map<v3s16, MapBlock*>  & modified_blocks)
295 {
296         v3s16 dirs[6] = {
297                 v3s16(0,0,1), // back
298                 v3s16(0,1,0), // top
299                 v3s16(1,0,0), // right
300                 v3s16(0,0,-1), // front
301                 v3s16(0,-1,0), // bottom
302                 v3s16(-1,0,0), // left
303         };
304         
305         if(from_nodes.size() == 0)
306                 return;
307         
308         u32 blockchangecount = 0;
309
310         core::map<v3s16, u8> unlighted_nodes;
311         core::map<v3s16, u8>::Iterator j;
312         j = from_nodes.getIterator();
313
314         /*
315                 Initialize block cache
316         */
317         v3s16 blockpos_last;
318         MapBlock *block = NULL;
319         // Cache this a bit, too
320         bool block_checked_in_modified = false;
321         
322         for(; j.atEnd() == false; j++)
323         {
324                 v3s16 pos = j.getNode()->getKey();
325                 v3s16 blockpos = getNodeBlockPos(pos);
326                 
327                 // Only fetch a new block if the block position has changed
328                 try{
329                         if(block == NULL || blockpos != blockpos_last){
330                                 block = getBlockNoCreate(blockpos);
331                                 blockpos_last = blockpos;
332
333                                 block_checked_in_modified = false;
334                                 blockchangecount++;
335                         }
336                 }
337                 catch(InvalidPositionException &e)
338                 {
339                         continue;
340                 }
341
342                 if(block->isDummy())
343                         continue;
344
345                 // Calculate relative position in block
346                 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
347
348                 // Get node straight from the block
349                 MapNode n = block->getNode(relpos);
350                 
351                 u8 oldlight = j.getNode()->getValue();
352                 
353                 // Loop through 6 neighbors
354                 for(u16 i=0; i<6; i++)
355                 {
356                         // Get the position of the neighbor node
357                         v3s16 n2pos = pos + dirs[i];
358                         
359                         // Get the block where the node is located
360                         v3s16 blockpos = getNodeBlockPos(n2pos);
361
362                         try
363                         {
364                                 // Only fetch a new block if the block position has changed
365                                 try{
366                                         if(block == NULL || blockpos != blockpos_last){
367                                                 block = getBlockNoCreate(blockpos);
368                                                 blockpos_last = blockpos;
369
370                                                 block_checked_in_modified = false;
371                                                 blockchangecount++;
372                                         }
373                                 }
374                                 catch(InvalidPositionException &e)
375                                 {
376                                         continue;
377                                 }
378                                 
379                                 // Calculate relative position in block
380                                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
381                                 // Get node straight from the block
382                                 MapNode n2 = block->getNode(relpos);
383                                 
384                                 bool changed = false;
385
386                                 //TODO: Optimize output by optimizing light_sources?
387
388                                 /*
389                                         If the neighbor is dimmer than what was specified
390                                         as oldlight (the light of the previous node)
391                                 */
392                                 if(n2.getLight() < oldlight)
393                                 {
394                                         /*
395                                                 And the neighbor is transparent and it has some light
396                                         */
397                                         if(n2.light_propagates() && n2.getLight() != 0)
398                                         {
399                                                 /*
400                                                         Set light to 0 and add to queue
401                                                 */
402
403                                                 u8 current_light = n2.getLight();
404                                                 n2.setLight(0);
405                                                 block->setNode(relpos, n2);
406
407                                                 unlighted_nodes.insert(n2pos, current_light);
408                                                 changed = true;
409
410                                                 /*
411                                                         Remove from light_sources if it is there
412                                                         NOTE: This doesn't happen nearly at all
413                                                 */
414                                                 /*if(light_sources.find(n2pos))
415                                                 {
416                                                         std::cout<<"Removed from light_sources"<<std::endl;
417                                                         light_sources.remove(n2pos);
418                                                 }*/
419                                         }
420                                 }
421                                 else{
422                                         light_sources.insert(n2pos, true);
423                                 }
424
425                                 // Add to modified_blocks
426                                 if(changed == true && block_checked_in_modified == false)
427                                 {
428                                         // If the block is not found in modified_blocks, add.
429                                         if(modified_blocks.find(blockpos) == NULL)
430                                         {
431                                                 modified_blocks.insert(blockpos, block);
432                                         }
433                                         block_checked_in_modified = true;
434                                 }
435                         }
436                         catch(InvalidPositionException &e)
437                         {
438                                 continue;
439                         }
440                 }
441         }
442
443         /*dstream<<"unspreadLight(): Changed block "
444                         <<blockchangecount<<" times"
445                         <<" for "<<from_nodes.size()<<" nodes"
446                         <<std::endl;*/
447         
448         if(unlighted_nodes.size() > 0)
449                 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
450 }
451
452 /*
453         A single-node wrapper of the above
454 */
455 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
456                 core::map<v3s16, bool> & light_sources,
457                 core::map<v3s16, MapBlock*>  & modified_blocks)
458 {
459         core::map<v3s16, u8> from_nodes;
460         from_nodes.insert(pos, lightwas);
461
462         unspreadLight(from_nodes, light_sources, modified_blocks);
463 }
464
465 /*
466         Lights neighbors of from_nodes, collects all them and then
467         goes on recursively.
468 */
469 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
470                 core::map<v3s16, MapBlock*> & modified_blocks)
471 {
472         const v3s16 dirs[6] = {
473                 v3s16(0,0,1), // back
474                 v3s16(0,1,0), // top
475                 v3s16(1,0,0), // right
476                 v3s16(0,0,-1), // front
477                 v3s16(0,-1,0), // bottom
478                 v3s16(-1,0,0), // left
479         };
480
481         if(from_nodes.size() == 0)
482                 return;
483         
484         u32 blockchangecount = 0;
485
486         core::map<v3s16, bool> lighted_nodes;
487         core::map<v3s16, bool>::Iterator j;
488         j = from_nodes.getIterator();
489
490         /*
491                 Initialize block cache
492         */
493         v3s16 blockpos_last;
494         MapBlock *block = NULL;
495         // Cache this a bit, too
496         bool block_checked_in_modified = false;
497         
498         for(; j.atEnd() == false; j++)
499         //for(; j != from_nodes.end(); j++)
500         {
501                 v3s16 pos = j.getNode()->getKey();
502                 //v3s16 pos = *j;
503                 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
504                 v3s16 blockpos = getNodeBlockPos(pos);
505                 
506                 // Only fetch a new block if the block position has changed
507                 try{
508                         if(block == NULL || blockpos != blockpos_last){
509                                 block = getBlockNoCreate(blockpos);
510                                 blockpos_last = blockpos;
511
512                                 block_checked_in_modified = false;
513                                 blockchangecount++;
514                         }
515                 }
516                 catch(InvalidPositionException &e)
517                 {
518                         continue;
519                 }
520
521                 if(block->isDummy())
522                         continue;
523
524                 // Calculate relative position in block
525                 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
526
527                 // Get node straight from the block
528                 MapNode n = block->getNode(relpos);
529
530                 u8 oldlight = n.getLight();
531                 u8 newlight = diminish_light(oldlight);
532
533                 // Loop through 6 neighbors
534                 for(u16 i=0; i<6; i++){
535                         // Get the position of the neighbor node
536                         v3s16 n2pos = pos + dirs[i];
537                         
538                         // Get the block where the node is located
539                         v3s16 blockpos = getNodeBlockPos(n2pos);
540
541                         try
542                         {
543                                 // Only fetch a new block if the block position has changed
544                                 try{
545                                         if(block == NULL || blockpos != blockpos_last){
546                                                 block = getBlockNoCreate(blockpos);
547                                                 blockpos_last = blockpos;
548
549                                                 block_checked_in_modified = false;
550                                                 blockchangecount++;
551                                         }
552                                 }
553                                 catch(InvalidPositionException &e)
554                                 {
555                                         continue;
556                                 }
557                                 
558                                 // Calculate relative position in block
559                                 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
560                                 // Get node straight from the block
561                                 MapNode n2 = block->getNode(relpos);
562                                 
563                                 bool changed = false;
564                                 /*
565                                         If the neighbor is brighter than the current node,
566                                         add to list (it will light up this node on its turn)
567                                 */
568                                 if(n2.getLight() > undiminish_light(oldlight))
569                                 {
570                                         lighted_nodes.insert(n2pos, true);
571                                         //lighted_nodes.push_back(n2pos);
572                                         changed = true;
573                                 }
574                                 /*
575                                         If the neighbor is dimmer than how much light this node
576                                         would spread on it, add to list
577                                 */
578                                 if(n2.getLight() < newlight)
579                                 {
580                                         if(n2.light_propagates())
581                                         {
582                                                 n2.setLight(newlight);
583                                                 block->setNode(relpos, n2);
584                                                 lighted_nodes.insert(n2pos, true);
585                                                 //lighted_nodes.push_back(n2pos);
586                                                 changed = true;
587                                         }
588                                 }
589
590                                 // Add to modified_blocks
591                                 if(changed == true && block_checked_in_modified == false)
592                                 {
593                                         // If the block is not found in modified_blocks, add.
594                                         if(modified_blocks.find(blockpos) == NULL)
595                                         {
596                                                 modified_blocks.insert(blockpos, block);
597                                         }
598                                         block_checked_in_modified = true;
599                                 }
600                         }
601                         catch(InvalidPositionException &e)
602                         {
603                                 continue;
604                         }
605                 }
606         }
607
608         /*dstream<<"spreadLight(): Changed block "
609                         <<blockchangecount<<" times"
610                         <<" for "<<from_nodes.size()<<" nodes"
611                         <<std::endl;*/
612         
613         if(lighted_nodes.size() > 0)
614                 spreadLight(lighted_nodes, modified_blocks);
615 }
616
617 /*
618         A single-node source variation of the above.
619 */
620 void Map::lightNeighbors(v3s16 pos,
621                 core::map<v3s16, MapBlock*> & modified_blocks)
622 {
623         core::map<v3s16, bool> from_nodes;
624         from_nodes.insert(pos, true);
625         spreadLight(from_nodes, modified_blocks);
626 }
627
628 v3s16 Map::getBrightestNeighbour(v3s16 p)
629 {
630         v3s16 dirs[6] = {
631                 v3s16(0,0,1), // back
632                 v3s16(0,1,0), // top
633                 v3s16(1,0,0), // right
634                 v3s16(0,0,-1), // front
635                 v3s16(0,-1,0), // bottom
636                 v3s16(-1,0,0), // left
637         };
638         
639         u8 brightest_light = 0;
640         v3s16 brightest_pos(0,0,0);
641         bool found_something = false;
642
643         // Loop through 6 neighbors
644         for(u16 i=0; i<6; i++){
645                 // Get the position of the neighbor node
646                 v3s16 n2pos = p + dirs[i];
647                 MapNode n2;
648                 try{
649                         n2 = getNode(n2pos);
650                 }
651                 catch(InvalidPositionException &e)
652                 {
653                         continue;
654                 }
655                 if(n2.getLight() > brightest_light || found_something == false){
656                         brightest_light = n2.getLight();
657                         brightest_pos = n2pos;
658                         found_something = true;
659                 }
660         }
661
662         if(found_something == false)
663                 throw InvalidPositionException();
664                 
665         return brightest_pos;
666 }
667
668 /*
669         Propagates sunlight down from a node.
670         Starting point gets sunlight.
671
672         Returns the lowest y value of where the sunlight went.
673 */
674 s16 Map::propagateSunlight(v3s16 start,
675                 core::map<v3s16, MapBlock*> & modified_blocks)
676 {
677         s16 y = start.Y;
678         for(; ; y--)
679         {
680                 v3s16 pos(start.X, y, start.Z);
681                 
682                 v3s16 blockpos = getNodeBlockPos(pos);
683                 MapBlock *block;
684                 try{
685                         block = getBlockNoCreate(blockpos);
686                 }
687                 catch(InvalidPositionException &e)
688                 {
689                         break;
690                 }
691
692                 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
693                 MapNode n = block->getNode(relpos);
694
695                 if(n.sunlight_propagates())
696                 {
697                         n.setLight(LIGHT_SUN);
698                         block->setNode(relpos, n);
699
700                         modified_blocks.insert(blockpos, block);
701                 }
702                 else{
703                         break;
704                 }
705         }
706         return y + 1;
707 }
708
709 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
710                 core::map<v3s16, MapBlock*> & modified_blocks)
711 {
712         /*m_dout<<DTIME<<"Map::updateLighting(): "
713                         <<a_blocks.getSize()<<" blocks... ";*/
714         
715         // For debugging
716         bool debug=false;
717         u32 count_was = modified_blocks.size();
718
719         /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
720         for(; i != a_blocks.end(); i++)
721         {
722                 MapBlock *block = *i;*/
723
724         core::map<v3s16, bool> light_sources;
725         
726         core::map<v3s16, u8> unlight_from;
727                 
728         core::map<v3s16, MapBlock*>::Iterator i;
729         i = a_blocks.getIterator();
730         for(; i.atEnd() == false; i++)
731         {
732                 MapBlock *block = i.getNode()->getValue();
733                 
734                 for(;;)
735                 {
736                         // Don't bother with dummy blocks.
737                         if(block->isDummy())
738                                 break;
739                 
740                         v3s16 pos = block->getPos();
741                         modified_blocks.insert(pos, block);
742
743                         /*
744                                 Clear all light from block
745                         */
746                         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
747                         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
748                         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
749                         {
750                                 
751                                 try{
752                                         v3s16 p(x,y,z);
753                                         MapNode n = block->getNode(v3s16(x,y,z));
754                                         u8 oldlight = n.getLight();
755                                         n.setLight(0);
756                                         block->setNode(v3s16(x,y,z), n);
757                                         
758                                         // Collect borders for unlighting
759                                         if(x==0 || x == MAP_BLOCKSIZE-1
760                                                         || y==0 || y == MAP_BLOCKSIZE-1
761                                                         || z==0 || z == MAP_BLOCKSIZE-1)
762                                         {
763                                                 v3s16 p_map = p + v3s16(
764                                                                 MAP_BLOCKSIZE*pos.X,
765                                                                 MAP_BLOCKSIZE*pos.Y,
766                                                                 MAP_BLOCKSIZE*pos.Z);
767                                                 unlight_from.insert(p_map, oldlight);
768                                         }
769                                 }
770                                 catch(InvalidPositionException &e)
771                                 {
772                                         /*
773                                                 This would happen when dealing with a
774                                                 dummy block.
775                                         */
776                                         //assert(0);
777                                         dstream<<"updateLighting(): InvalidPositionException"
778                                                         <<std::endl;
779                                 }
780                         }
781                         
782                         bool bottom_valid = block->propagateSunlight(light_sources);
783
784                         // If bottom is valid, we're done.
785                         if(bottom_valid)
786                                 break;
787                                 
788                         /*dstream<<"Bottom for sunlight-propagated block ("
789                                         <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
790                                         <<std::endl;*/
791
792                         // Else get the block below and loop to it
793
794                         pos.Y--;
795                         try{
796                                 block = getBlockNoCreate(pos);
797                         }
798                         catch(InvalidPositionException &e)
799                         {
800                                 assert(0);
801                         }
802                         
803                 }
804         }
805         
806         {
807                 //TimeTaker timer("unspreadLight", g_device);
808                 unspreadLight(unlight_from, light_sources, modified_blocks);
809         }
810         
811         if(debug)
812         {
813                 u32 diff = modified_blocks.size() - count_was;
814                 count_was = modified_blocks.size();
815                 dstream<<"unspreadLight modified "<<diff<<std::endl;
816         }
817
818         // TODO: Spread light from propagated sunlight?
819         // Yes, add it to light_sources... somehow.
820         // It has to be added at somewhere above, in the loop.
821         // TODO
822
823         {
824                 //TimeTaker timer("spreadLight", g_device);
825                 spreadLight(light_sources, modified_blocks);
826         }
827         
828         if(debug)
829         {
830                 u32 diff = modified_blocks.size() - count_was;
831                 count_was = modified_blocks.size();
832                 dstream<<"spreadLight modified "<<diff<<std::endl;
833         }
834
835         //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
836 }
837
838 /*
839         This is called after changing a node from transparent to opaque.
840         The lighting value of the node should be left as-is after changing
841         other values. This sets the lighting value to 0.
842 */
843 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
844                 core::map<v3s16, MapBlock*> &modified_blocks)*/
845 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
846                 core::map<v3s16, MapBlock*> &modified_blocks)
847 {
848         /*PrintInfo(m_dout);
849         m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
850                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
851
852         u8 lightwas = getNode(p).getLight();
853
854         //core::list<v3s16> light_sources;
855         core::map<v3s16, bool> light_sources;
856         //MapNode n = getNode(p);
857
858         /*
859                 From this node to nodes underneath:
860                 If lighting is sunlight (1.0), unlight neighbours and
861                 set lighting to 0.
862                 Else discontinue.
863         */
864
865         bool node_under_sunlight = true;
866         
867         v3s16 toppos = p + v3s16(0,1,0);
868
869         /*
870                 If there is a node at top and it doesn't have sunlight,
871                 there has not been any sunlight going down.
872
873                 Otherwise there probably is.
874         */
875         try{
876                 MapNode topnode = getNode(toppos);
877
878                 if(topnode.getLight() != LIGHT_SUN)
879                         node_under_sunlight = false;
880         }
881         catch(InvalidPositionException &e)
882         {
883         }
884
885         // Add the block of the added node to modified_blocks
886         v3s16 blockpos = getNodeBlockPos(p);
887         MapBlock * block = getBlockNoCreate(blockpos);
888         assert(block != NULL);
889         modified_blocks.insert(blockpos, block);
890         
891         if(isValidPosition(p) == false)
892                 throw;
893                 
894         // Unlight neighbours of node.
895         // This means setting light of all consequent dimmer nodes
896         // to 0.
897         // This also collects the nodes at the border which will spread
898         // light again into this.
899         unLightNeighbors(p, lightwas, light_sources, modified_blocks);
900
901         n.setLight(0);
902         setNode(p, n);
903         
904         /*
905                 If node is under sunlight, take all sunlighted nodes under
906                 it and clear light from them and from where the light has
907                 been spread.
908         */
909         if(node_under_sunlight)
910         {
911                 s16 y = p.Y - 1;
912                 for(;; y--){
913                         //m_dout<<DTIME<<"y="<<y<<std::endl;
914                         v3s16 n2pos(p.X, y, p.Z);
915                         
916                         MapNode n2;
917                         try{
918                                 n2 = getNode(n2pos);
919                         }
920                         catch(InvalidPositionException &e)
921                         {
922                                 break;
923                         }
924
925                         if(n2.getLight() == LIGHT_SUN)
926                         {
927                                 //m_dout<<DTIME<<"doing"<<std::endl;
928                                 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
929                                 n2.setLight(0);
930                                 setNode(n2pos, n2);
931                         }
932                         else
933                                 break;
934                 }
935         }
936         
937         /*
938                 Spread light from all nodes that might be capable of doing so
939                 TODO: Convert to spreadLight
940         */
941         spreadLight(light_sources, modified_blocks);
942 }
943
944 /*
945 */
946 void Map::removeNodeAndUpdate(v3s16 p,
947                 core::map<v3s16, MapBlock*> &modified_blocks)
948 {
949         /*PrintInfo(m_dout);
950         m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
951                         <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
952         
953         bool node_under_sunlight = true;
954         
955         v3s16 toppos = p + v3s16(0,1,0);
956         
957         /*
958                 If there is a node at top and it doesn't have sunlight,
959                 there will be no sunlight going down.
960         */
961         try{
962                 MapNode topnode = getNode(toppos);
963
964                 if(topnode.getLight() != LIGHT_SUN)
965                         node_under_sunlight = false;
966         }
967         catch(InvalidPositionException &e)
968         {
969         }
970
971         /*
972                 Unlight neighbors (in case the node is a light source)
973         */
974         //core::list<v3s16> light_sources;
975         core::map<v3s16, bool> light_sources;
976         unLightNeighbors(p, getNode(p).getLight(),
977                         light_sources, modified_blocks);
978
979         /*
980                 Remove the node
981         */
982         MapNode n;
983         n.d = MATERIAL_AIR;
984         n.setLight(0);
985         setNode(p, n);
986         
987         /*
988                 Recalculate lighting
989         */
990         spreadLight(light_sources, modified_blocks);
991
992         // Add the block of the removed node to modified_blocks
993         v3s16 blockpos = getNodeBlockPos(p);
994         MapBlock * block = getBlockNoCreate(blockpos);
995         assert(block != NULL);
996         modified_blocks.insert(blockpos, block);
997
998         /*
999                 If the removed node was under sunlight, propagate the
1000                 sunlight down from it and then light all neighbors
1001                 of the propagated blocks.
1002         */
1003         if(node_under_sunlight)
1004         {
1005                 s16 ybottom = propagateSunlight(p, modified_blocks);
1006                 /*m_dout<<DTIME<<"Node was under sunlight. "
1007                                 "Propagating sunlight";
1008                 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1009                 s16 y = p.Y;
1010                 for(; y >= ybottom; y--)
1011                 {
1012                         v3s16 p2(p.X, y, p.Z);
1013                         /*m_dout<<DTIME<<"lighting neighbors of node ("
1014                                         <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1015                                         <<std::endl;*/
1016                         lightNeighbors(p2, modified_blocks);
1017                 }
1018         }
1019         else
1020         {
1021                 // Set the lighting of this node to 0
1022                 try{
1023                         MapNode n = getNode(p);
1024                         n.setLight(0);
1025                         setNode(p, n);
1026                 }
1027                 catch(InvalidPositionException &e)
1028                 {
1029                         throw;
1030                 }
1031         }
1032
1033         // Get the brightest neighbour node and propagate light from it
1034         v3s16 n2p = getBrightestNeighbour(p);
1035         try{
1036                 MapNode n2 = getNode(n2p);
1037                 lightNeighbors(n2p, modified_blocks);
1038         }
1039         catch(InvalidPositionException &e)
1040         {
1041         }
1042 }
1043
1044 void Map::updateMeshes(v3s16 blockpos)
1045 {
1046         assert(mapType() == MAPTYPE_CLIENT);
1047
1048         try{
1049                 v3s16 p = blockpos + v3s16(0,0,0);
1050                 MapBlock *b = getBlockNoCreate(p);
1051                 b->updateMesh();
1052         }
1053         catch(InvalidPositionException &e){}
1054         try{
1055                 v3s16 p = blockpos + v3s16(-1,0,0);
1056                 MapBlock *b = getBlockNoCreate(p);
1057                 b->updateMesh();
1058         }
1059         catch(InvalidPositionException &e){}
1060         try{
1061                 v3s16 p = blockpos + v3s16(0,-1,0);
1062                 MapBlock *b = getBlockNoCreate(p);
1063                 b->updateMesh();
1064         }
1065         catch(InvalidPositionException &e){}
1066         try{
1067                 v3s16 p = blockpos + v3s16(0,0,-1);
1068                 MapBlock *b = getBlockNoCreate(p);
1069                 b->updateMesh();
1070         }
1071         catch(InvalidPositionException &e){}
1072 }
1073
1074 /*
1075         Updates usage timers
1076 */
1077 void Map::timerUpdate(float dtime)
1078 {
1079         JMutexAutoLock lock(m_sector_mutex);
1080
1081         core::map<v2s16, MapSector*>::Iterator si;
1082
1083         si = m_sectors.getIterator();
1084         for(; si.atEnd() == false; si++)
1085         {
1086                 MapSector *sector = si.getNode()->getValue();
1087                 sector->usage_timer += dtime;
1088         }
1089 }
1090
1091 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1092 {
1093         core::list<v2s16>::Iterator j;
1094         for(j=list.begin(); j!=list.end(); j++)
1095         {
1096                 MapSector *sector = m_sectors[*j];
1097                 if(only_blocks)
1098                 {
1099                         sector->deleteBlocks();
1100                 }
1101                 else
1102                 {
1103                         /*
1104                                 If sector is in sector cache, remove it from there
1105                         */
1106                         if(m_sector_cache == sector)
1107                         {
1108                                 m_sector_cache = NULL;
1109                         }
1110                         /*
1111                                 Remove from map and delete
1112                         */
1113                         m_sectors.remove(*j);
1114                         delete sector;
1115                 }
1116         }
1117 }
1118
1119 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1120                 core::list<v3s16> *deleted_blocks)
1121 {
1122         JMutexAutoLock lock(m_sector_mutex);
1123
1124         core::list<v2s16> sector_deletion_queue;
1125         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1126         for(; i.atEnd() == false; i++)
1127         {
1128                 MapSector *sector = i.getNode()->getValue();
1129                 /*
1130                         Delete sector from memory if it hasn't been used in a long time
1131                 */
1132                 if(sector->usage_timer > timeout)
1133                 {
1134                         sector_deletion_queue.push_back(i.getNode()->getKey());
1135                         
1136                         if(deleted_blocks != NULL)
1137                         {
1138                                 // Collect positions of blocks of sector
1139                                 MapSector *sector = i.getNode()->getValue();
1140                                 core::list<MapBlock*> blocks;
1141                                 sector->getBlocks(blocks);
1142                                 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1143                                                 i != blocks.end(); i++)
1144                                 {
1145                                         deleted_blocks->push_back((*i)->getPos());
1146                                 }
1147                         }
1148                 }
1149         }
1150         deleteSectors(sector_deletion_queue, only_blocks);
1151         return sector_deletion_queue.getSize();
1152 }
1153
1154 void Map::PrintInfo(std::ostream &out)
1155 {
1156         out<<"Map: ";
1157 }
1158
1159 /*
1160         ServerMap
1161 */
1162
1163 ServerMap::ServerMap(std::string savedir, MapgenParams params):
1164         Map(dout_server),
1165         m_heightmap(NULL)
1166 {
1167         m_savedir = savedir;
1168         m_map_saving_enabled = false;
1169         
1170         try
1171         {
1172                 // If directory exists, check contents and load if possible
1173                 if(fs::PathExists(m_savedir))
1174                 {
1175                         // If directory is empty, it is safe to save into it.
1176                         if(fs::GetDirListing(m_savedir).size() == 0)
1177                         {
1178                                 dstream<<DTIME<<"Server: Empty save directory is valid."
1179                                                 <<std::endl;
1180                                 m_map_saving_enabled = true;
1181                         }
1182                         else
1183                         {
1184                                 // Load master heightmap
1185                                 loadMasterHeightmap();
1186                                 
1187                                 // Load sector (0,0) and throw and exception on fail
1188                                 if(loadSectorFull(v2s16(0,0)) == false)
1189                                         throw LoadError("Failed to load sector (0,0)");
1190
1191                                 dstream<<DTIME<<"Server: Successfully loaded master "
1192                                                 "heightmap and sector (0,0) from "<<savedir<<
1193                                                 ", assuming valid save directory."
1194                                                 <<std::endl;
1195
1196                                 m_map_saving_enabled = true;
1197                                 // Map loaded, not creating new one
1198                                 return;
1199                         }
1200                 }
1201                 // If directory doesn't exist, it is safe to save to it
1202                 else{
1203                         m_map_saving_enabled = true;
1204                 }
1205         }
1206         catch(std::exception &e)
1207         {
1208                 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1209                                 <<", exception: "<<e.what()<<std::endl;
1210                 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1211                 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1212         }
1213
1214         dstream<<DTIME<<"Initializing new map."<<std::endl;
1215
1216         ValueGenerator *maxgen =
1217                         ValueGenerator::deSerialize(params.height_randmax);
1218         ValueGenerator *factorgen =
1219                         ValueGenerator::deSerialize(params.height_randfactor);
1220         ValueGenerator *basegen =
1221                         ValueGenerator::deSerialize(params.height_base);
1222         m_heightmap = new UnlimitedHeightmap
1223                         (params.heightmap_blocksize, maxgen, factorgen, basegen);
1224         
1225         // Create zero sector
1226         emergeSector(v2s16(0,0));
1227
1228         // Initially write whole map
1229         save(false);
1230 }
1231
1232 ServerMap::~ServerMap()
1233 {
1234         try
1235         {
1236                 if(m_map_saving_enabled)
1237                 {
1238                         //save(false);
1239                         // Save only changed parts
1240                         save(true);
1241                         dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1242                 }
1243                 else
1244                 {
1245                         dstream<<DTIME<<"Server: map not saved"<<std::endl;
1246                 }
1247         }
1248         catch(std::exception &e)
1249         {
1250                 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1251                                 <<", exception: "<<e.what()<<std::endl;
1252         }
1253         
1254         if(m_heightmap != NULL)
1255                 delete m_heightmap;
1256 }
1257
1258 MapSector * ServerMap::emergeSector(v2s16 p2d)
1259 {
1260         DSTACK("%s: p2d=(%d,%d)",
1261                         __FUNCTION_NAME,
1262                         p2d.X, p2d.Y);
1263         // Check that it doesn't exist already
1264         try{
1265                 return getSectorNoGenerate(p2d);
1266         }
1267         catch(InvalidPositionException &e)
1268         {
1269         }
1270         
1271         /*
1272                 Try to load the sector from disk.
1273         */
1274         if(loadSectorFull(p2d) == true)
1275         {
1276                 return getSectorNoGenerate(p2d);
1277         }
1278
1279         /*
1280                 If there is no master heightmap, throw.
1281         */
1282         if(m_heightmap == NULL)
1283         {
1284                 throw InvalidPositionException("emergeSector(): no heightmap");
1285         }
1286
1287         /*
1288                 Do not generate over-limit
1289         */
1290         if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1291         || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1292         || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1293         || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1294                 throw InvalidPositionException("emergeSector(): pos. over limit");
1295
1296         /*
1297                 Generate sector and heightmaps
1298         */
1299         
1300         // Number of heightmaps in sector in each direction
1301         u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1302
1303         // Heightmap side width
1304         s16 hm_d = MAP_BLOCKSIZE / hm_split;
1305
1306         ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1307
1308         /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1309                         " heightmaps and objects"<<std::endl;*/
1310         
1311         // Loop through sub-heightmaps
1312         for(s16 y=0; y<hm_split; y++)
1313         for(s16 x=0; x<hm_split; x++)
1314         {
1315                 v2s16 p_in_sector = v2s16(x,y);
1316                 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1317                 f32 corners[4] = {
1318                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1319                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1320                         m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1321                         m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1322                 };
1323
1324                 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1325                                 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1326                                 <<std::endl;*/
1327
1328                 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1329                                 mhm_p, hm_d);
1330                 sector->setHeightmap(p_in_sector, hm);
1331
1332                 //TODO: Make these values configurable
1333                 hm->generateContinued(1.0, 0.2, corners);
1334                 //hm->generateContinued(2.0, 0.2, corners);
1335
1336                 //hm->print();
1337                 
1338         }
1339
1340         /*
1341                 Generate objects
1342         */
1343         
1344         core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1345         sector->setObjects(objects);
1346         
1347         v2s16 mhm_p = p2d * hm_split;
1348         f32 corners[4] = {
1349                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1350                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1351                 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1352                 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1353         };
1354         
1355         float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1356         float avgslope = 0.0;
1357         avgslope += fabs(avgheight - corners[0]);
1358         avgslope += fabs(avgheight - corners[1]);
1359         avgslope += fabs(avgheight - corners[2]);
1360         avgslope += fabs(avgheight - corners[3]);
1361         avgslope /= 4.0;
1362         avgslope /= MAP_BLOCKSIZE;
1363         //dstream<<"avgslope="<<avgslope<<std::endl;
1364
1365         float pitness = 0.0;
1366         v2f32 a;
1367         a = m_heightmap->getSlope(p2d+v2s16(0,0));
1368         pitness += -a.X;
1369         pitness += -a.Y;
1370         a = m_heightmap->getSlope(p2d+v2s16(0,1));
1371         pitness += -a.X;
1372         pitness += a.Y;
1373         a = m_heightmap->getSlope(p2d+v2s16(1,1));
1374         pitness += a.X;
1375         pitness += a.Y;
1376         a = m_heightmap->getSlope(p2d+v2s16(1,0));
1377         pitness += a.X;
1378         pitness += -a.Y;
1379         pitness /= 4.0;
1380         pitness /= MAP_BLOCKSIZE;
1381         //dstream<<"pitness="<<pitness<<std::endl;
1382         
1383         /*
1384                 Plant some trees if there is not much slope
1385         */
1386         {
1387                 // Avgslope is the derivative of a hill
1388                 float t = avgslope * avgslope;
1389                 float a = MAP_BLOCKSIZE * 2;
1390                 u32 tree_max;
1391                 if(t > 0.03)
1392                         tree_max = a / (t/0.03);
1393                 else
1394                         tree_max = a;
1395                 u32 count = (rand()%(tree_max+1));
1396                 //u32 count = tree_max;
1397                 for(u32 i=0; i<count; i++)
1398                 {
1399                         s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1400                         s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1401                         s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1402                         if(y < WATER_LEVEL)
1403                                 continue;
1404                         objects->insert(v3s16(x, y, z),
1405                                         SECTOR_OBJECT_TREE_1);
1406                 }
1407         }
1408         {
1409                 // Pitness usually goes at around -0.5...0.5
1410                 u32 bush_max = 0;
1411                 u32 a = MAP_BLOCKSIZE * 3;
1412                 if(pitness > 0)
1413                         bush_max = (pitness*a*4);
1414                 if(bush_max > a)
1415                         bush_max = a;
1416                 u32 count = (rand()%(bush_max+1));
1417                 for(u32 i=0; i<count; i++)
1418                 {
1419                         s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1420                         s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1421                         s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1422                         if(y < WATER_LEVEL)
1423                                 continue;
1424                         objects->insert(v3s16(x, y, z),
1425                                         SECTOR_OBJECT_BUSH_1);
1426                 }
1427         }
1428
1429         /*
1430                 Insert to container
1431         */
1432         JMutexAutoLock lock(m_sector_mutex);
1433         m_sectors.insert(p2d, sector);
1434         
1435         return sector;
1436 }
1437
1438 MapBlock * ServerMap::emergeBlock(
1439                 v3s16 p,
1440                 bool only_from_disk,
1441                 core::map<v3s16, MapBlock*> &changed_blocks,
1442                 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1443 )
1444 {
1445         DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1446                         __FUNCTION_NAME,
1447                         p.X, p.Y, p.Z, only_from_disk);
1448                         
1449         /*dstream<<"ServerMap::emergeBlock(): "
1450                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1451                         <<", only_from_disk="<<only_from_disk<<std::endl;*/
1452         v2s16 p2d(p.X, p.Z);
1453         s16 block_y = p.Y;
1454         /*
1455                 This will create or load a sector if not found in memory.
1456                 If block exists on disk, it will be loaded.
1457
1458                 NOTE: On old save formats, this will be slow, as it generates
1459                       lighting on blocks for them.
1460         */
1461         ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1462         assert(sector->getId() == MAPSECTOR_SERVER);
1463
1464         // Try to get a block from the sector
1465         MapBlock *block = NULL;
1466         bool not_on_disk = false;
1467         try{
1468                 block = sector->getBlockNoCreate(block_y);
1469                 if(block->isDummy() == true)
1470                         not_on_disk = true;
1471                 else
1472                         return block;
1473         }
1474         catch(InvalidPositionException &e)
1475         {
1476                 not_on_disk = true;
1477         }
1478         
1479         /*
1480                 If block was not found on disk and not going to generate a
1481                 new one, make sure there is a dummy block in place.
1482         */
1483         if(not_on_disk && only_from_disk)
1484         {
1485                 if(block == NULL)
1486                 {
1487                         // Create dummy block
1488                         block = new MapBlock(this, p, true);
1489
1490                         // Add block to sector
1491                         sector->insertBlock(block);
1492                 }
1493                 // Done.
1494                 return block;
1495         }
1496
1497         //dstream<<"Not found on disk, generating."<<std::endl;
1498
1499         /*
1500                 Do not generate over-limit
1501         */
1502         if(blockpos_over_limit(p))
1503                 throw InvalidPositionException("emergeBlock(): pos. over limit");
1504
1505         /*
1506                 OK; Not found.
1507
1508                 Go on generating the block.
1509
1510                 TODO: If a dungeon gets generated so that it's side gets
1511                       revealed to the outside air, the lighting should be
1512                           recalculated.
1513         */
1514         
1515         /*
1516                 If block doesn't exist, create one.
1517                 If it exists, it is a dummy. In that case unDummify() it.
1518         */
1519         if(block == NULL)
1520         {
1521                 block = sector->createBlankBlockNoInsert(block_y);
1522         }
1523         else
1524         {
1525                 // Remove the block so that nobody can get a half-generated one.
1526                 sector->removeBlock(block);
1527                 // Allocate the block to be a proper one.
1528                 block->unDummify();
1529         }
1530
1531         // Randomize a bit. This makes dungeons.
1532         bool low_block_is_empty = false;
1533         if(rand() % 4 == 0)
1534                 low_block_is_empty = true;
1535         
1536         // This is the basic material of what the visible flat ground
1537         // will consist of
1538         u8 material = MATERIAL_GRASS;
1539         
1540         s32 lowest_ground_y = 32767;
1541         
1542         // DEBUG
1543         //sector->printHeightmaps();
1544
1545         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1546         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1547         {
1548                 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1549                 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1550
1551                 assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1552                 
1553                 s16 surface_y = surface_y_f;
1554                 //avg_ground_y += surface_y;
1555                 if(surface_y < lowest_ground_y)
1556                         lowest_ground_y = surface_y;
1557
1558                 s32 surface_depth = 0;
1559                 
1560                 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1561                 
1562                 float min_slope = 0.45;
1563                 float max_slope = 0.85;
1564                 float min_slope_depth = 5.0;
1565                 float max_slope_depth = 0;
1566                 if(slope < min_slope)
1567                         surface_depth = min_slope_depth;
1568                 else if(slope > max_slope)
1569                         surface_depth = max_slope_depth;
1570                 else
1571                         surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1572
1573                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++){
1574                         s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1575                         MapNode n;
1576                         /*
1577                                 Calculate lighting
1578                                 
1579                                 FIXME: If there are some man-made structures above the
1580                                 newly created block, they won't be taken into account.
1581                         */
1582                         if(real_y > surface_y)
1583                                 n.setLight(LIGHT_SUN);
1584                         /*
1585                                 Calculate material
1586                         */
1587                         // If node is very low
1588                         if(real_y <= surface_y - 10){
1589                                 // Create dungeons
1590                                 if(low_block_is_empty){
1591                                         n.d = MATERIAL_AIR;
1592                                 }
1593                                 else{
1594                                         n.d = MATERIAL_STONE;
1595                                 }
1596                         }
1597                         // If node is under surface level
1598                         else if(real_y <= surface_y - surface_depth)
1599                                 n.d = MATERIAL_STONE;
1600                         // If node is at or under heightmap y
1601                         else if(real_y <= surface_y)
1602                                 n.d = material;
1603                         // If node is over heightmap y
1604                         else{
1605                                 // If under water level, it's water
1606                                 if(real_y < WATER_LEVEL)
1607                                 {
1608                                         n.d = MATERIAL_WATER;
1609                                         n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1610                                 }
1611                                 // else air
1612                                 else
1613                                         n.d = MATERIAL_AIR;
1614                         }
1615                         block->setNode(v3s16(x0,y0,z0), n);
1616                 }
1617         }
1618
1619         /*
1620                 Calculate is_underground
1621         */
1622         // Probably underground if the highest part of block is under lowest
1623         // ground height
1624         bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1625         block->setIsUnderground(is_underground);
1626
1627         /*
1628                 Add some minerals
1629         */
1630
1631         if(is_underground && low_block_is_empty == false)
1632         {
1633                 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1634                 for(s16 i=0; i<underground_level*3; i++)
1635                 {
1636                         if(rand()%2 == 0)
1637                         {
1638                                 v3s16 cp(
1639                                         /*(rand()%(MAP_BLOCKSIZE-4))+2,
1640                                         (rand()%(MAP_BLOCKSIZE-4))+2,
1641                                         (rand()%(MAP_BLOCKSIZE-4))+2*/
1642                                         (rand()%(MAP_BLOCKSIZE-2))+1,
1643                                         (rand()%(MAP_BLOCKSIZE-2))+1,
1644                                         (rand()%(MAP_BLOCKSIZE-2))+1
1645                                 );
1646
1647                                 MapNode n;
1648                                 n.d = MATERIAL_MESE;
1649                                 
1650                                 if(rand()%8 == 0)
1651                                         block->setNode(cp, n);
1652
1653                                 for(u16 i=0; i<26; i++)
1654                                 {
1655                                         if(rand()%8 == 0)
1656                                                 block->setNode(cp+g_26dirs[i], n);
1657                                 }
1658                         }
1659                 }
1660         }
1661         
1662         /*
1663                 Create a few rats in empty blocks underground
1664         */
1665         if(is_underground && low_block_is_empty == true)
1666         {
1667                 //for(u16 i=0; i<2; i++)
1668                 {
1669                         v3s16 pos(8, 1, 8);
1670                         RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1671                         block->addObject(obj);
1672                 }
1673         }
1674         
1675         /*
1676                 TODO: REMOVE
1677                 DEBUG
1678                 Add some objects to the block for testing.
1679         */
1680         /*if(p == v3s16(0,0,0))
1681         {
1682                 //TestObject *obj = new TestObject(NULL, -1, v3f(BS*8,BS*8,BS*8));
1683                 Test2Object *obj = new Test2Object(NULL, -1, v3f(BS*8,BS*15,BS*8));
1684                 block->addObject(obj);
1685         }*/
1686         
1687         /*
1688         {
1689                 v3s16 pos(8, 11, 8);
1690                 SignObject *obj = new SignObject(NULL, -1, intToFloat(pos));
1691                 obj->setText("Moicka");
1692                 obj->setYaw(45);
1693                 block->addObject(obj);
1694         }
1695
1696         {
1697                 v3s16 pos(8, 11, 8);
1698                 RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1699                 block->addObject(obj);
1700         }
1701         */
1702
1703         /*
1704                 Add block to sector.
1705         */
1706         sector->insertBlock(block);
1707         
1708         // An y-wise container if changed blocks
1709         core::map<s16, MapBlock*> changed_blocks_sector;
1710
1711         /*
1712                 Check if any sector's objects can be placed now.
1713                 If so, place them.
1714         */
1715         core::map<v3s16, u8> *objects = sector->getObjects();
1716         core::list<v3s16> objects_to_remove;
1717         for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1718                         i.atEnd() == false; i++)
1719         {
1720                 v3s16 p = i.getNode()->getKey();
1721                 u8 d = i.getNode()->getValue();
1722
1723                 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1724                 
1725                 try
1726                 {
1727
1728                 if(d == SECTOR_OBJECT_TEST)
1729                 {
1730                         if(sector->isValidArea(p + v3s16(0,0,0),
1731                                         p + v3s16(0,0,0), &changed_blocks_sector))
1732                         {
1733                                 MapNode n;
1734                                 n.d = MATERIAL_LIGHT;
1735                                 sector->setNode(p, n);
1736                                 objects_to_remove.push_back(p);
1737                         }
1738                 }
1739                 else if(d == SECTOR_OBJECT_TREE_1)
1740                 {
1741                         v3s16 p_min = p + v3s16(-1,0,-1);
1742                         v3s16 p_max = p + v3s16(1,4,1);
1743                         if(sector->isValidArea(p_min, p_max,
1744                                         &changed_blocks_sector))
1745                         {
1746                                 MapNode n;
1747                                 n.d = MATERIAL_TREE;
1748                                 sector->setNode(p+v3s16(0,0,0), n);
1749                                 sector->setNode(p+v3s16(0,1,0), n);
1750                                 sector->setNode(p+v3s16(0,2,0), n);
1751                                 sector->setNode(p+v3s16(0,3,0), n);
1752
1753                                 n.d = MATERIAL_LEAVES;
1754
1755                                 sector->setNode(p+v3s16(0,4,0), n);
1756                                 
1757                                 sector->setNode(p+v3s16(-1,4,0), n);
1758                                 sector->setNode(p+v3s16(1,4,0), n);
1759                                 sector->setNode(p+v3s16(0,4,-1), n);
1760                                 sector->setNode(p+v3s16(0,4,1), n);
1761                                 sector->setNode(p+v3s16(1,4,1), n);
1762                                 sector->setNode(p+v3s16(-1,4,1), n);
1763                                 sector->setNode(p+v3s16(-1,4,-1), n);
1764                                 sector->setNode(p+v3s16(1,4,-1), n);
1765
1766                                 sector->setNode(p+v3s16(-1,3,0), n);
1767                                 sector->setNode(p+v3s16(1,3,0), n);
1768                                 sector->setNode(p+v3s16(0,3,-1), n);
1769                                 sector->setNode(p+v3s16(0,3,1), n);
1770                                 sector->setNode(p+v3s16(1,3,1), n);
1771                                 sector->setNode(p+v3s16(-1,3,1), n);
1772                                 sector->setNode(p+v3s16(-1,3,-1), n);
1773                                 sector->setNode(p+v3s16(1,3,-1), n);
1774                                 
1775                                 objects_to_remove.push_back(p);
1776                                 
1777                                 // Lighting has to be recalculated for this one.
1778                                 sector->getBlocksInArea(p_min, p_max, 
1779                                                 lighting_invalidated_blocks);
1780                         }
1781                 }
1782                 else if(d == SECTOR_OBJECT_BUSH_1)
1783                 {
1784                         if(sector->isValidArea(p + v3s16(0,0,0),
1785                                         p + v3s16(0,0,0), &changed_blocks_sector))
1786                         {
1787                                 MapNode n;
1788                                 n.d = MATERIAL_LEAVES;
1789                                 sector->setNode(p+v3s16(0,0,0), n);
1790                                 
1791                                 objects_to_remove.push_back(p);
1792                         }
1793                 }
1794                 else
1795                 {
1796                         dstream<<"ServerMap::emergeBlock(): "
1797                                         "Invalid heightmap object"
1798                                         <<std::endl;
1799                 }
1800
1801                 }//try
1802                 catch(InvalidPositionException &e)
1803                 {
1804                         dstream<<"WARNING: "<<__FUNCTION_NAME
1805                                         <<": while inserting object "<<(int)d
1806                                         <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1807                                         <<" InvalidPositionException.what()="
1808                                         <<e.what()<<std::endl;
1809                         // This is not too fatal and seems to happen sometimes.
1810                         assert(0);
1811                 }
1812         }
1813
1814         for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
1815                         i != objects_to_remove.end(); i++)
1816         {
1817                 objects->remove(*i);
1818         }
1819
1820         for(core::map<s16, MapBlock*>::Iterator
1821                         i = changed_blocks_sector.getIterator();
1822                         i.atEnd() == false; i++)
1823         {
1824                 MapBlock *block = i.getNode()->getValue();
1825
1826                 changed_blocks.insert(block->getPos(), block);
1827         }
1828
1829         return block;
1830 }
1831
1832 void ServerMap::createDir(std::string path)
1833 {
1834         if(fs::CreateDir(path) == false)
1835         {
1836                 m_dout<<DTIME<<"ServerMap: Failed to create directory "
1837                                 <<"\""<<path<<"\""<<std::endl;
1838                 throw BaseException("ServerMap failed to create directory");
1839         }
1840 }
1841
1842 std::string ServerMap::getSectorSubDir(v2s16 pos)
1843 {
1844         char cc[9];
1845         snprintf(cc, 9, "%.4x%.4x",
1846                         (unsigned int)pos.X&0xffff,
1847                         (unsigned int)pos.Y&0xffff);
1848
1849         return std::string(cc);
1850 }
1851
1852 std::string ServerMap::getSectorDir(v2s16 pos)
1853 {
1854         return m_savedir + "/sectors/" + getSectorSubDir(pos);
1855 }
1856
1857 v2s16 ServerMap::getSectorPos(std::string dirname)
1858 {
1859         if(dirname.size() != 8)
1860                 throw InvalidFilenameException("Invalid sector directory name");
1861         unsigned int x, y;
1862         int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
1863         if(r != 2)
1864                 throw InvalidFilenameException("Invalid sector directory name");
1865         v2s16 pos((s16)x, (s16)y);
1866         return pos;
1867 }
1868
1869 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
1870 {
1871         v2s16 p2d = getSectorPos(sectordir);
1872
1873         if(blockfile.size() != 4){
1874                 throw InvalidFilenameException("Invalid block filename");
1875         }
1876         unsigned int y;
1877         int r = sscanf(blockfile.c_str(), "%4x", &y);
1878         if(r != 1)
1879                 throw InvalidFilenameException("Invalid block filename");
1880         return v3s16(p2d.X, y, p2d.Y);
1881 }
1882
1883 // Debug helpers
1884 #define ENABLE_SECTOR_SAVING 1
1885 #define ENABLE_SECTOR_LOADING 1
1886 #define ENABLE_BLOCK_SAVING 1
1887 #define ENABLE_BLOCK_LOADING 1
1888
1889 void ServerMap::save(bool only_changed)
1890 {
1891         DSTACK(__FUNCTION_NAME);
1892         if(m_map_saving_enabled == false)
1893         {
1894                 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
1895                 return;
1896         }
1897         
1898         if(only_changed == false)
1899                 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
1900                                 <<std::endl;
1901         
1902         saveMasterHeightmap();
1903         
1904         u32 sector_meta_count = 0;
1905         u32 block_count = 0;
1906         
1907         { //sectorlock
1908         JMutexAutoLock lock(m_sector_mutex);
1909         
1910         core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1911         for(; i.atEnd() == false; i++)
1912         {
1913                 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
1914                 assert(sector->getId() == MAPSECTOR_SERVER);
1915                 
1916                 if(ENABLE_SECTOR_SAVING)
1917                 {
1918                         if(sector->differs_from_disk || only_changed == false)
1919                         {
1920                                 saveSectorMeta(sector);
1921                                 sector_meta_count++;
1922                         }
1923                 }
1924                 if(ENABLE_BLOCK_SAVING)
1925                 {
1926                         core::list<MapBlock*> blocks;
1927                         sector->getBlocks(blocks);
1928                         core::list<MapBlock*>::Iterator j;
1929                         for(j=blocks.begin(); j!=blocks.end(); j++)
1930                         {
1931                                 MapBlock *block = *j;
1932                                 if(block->getChangedFlag() || only_changed == false)
1933                                 {
1934                                         saveBlock(block);
1935                                         block_count++;
1936                                 }
1937                         }
1938                 }
1939         }
1940
1941         }//sectorlock
1942         
1943         u32 deleted_count = 0;
1944         deleted_count = deleteUnusedSectors
1945                         (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
1946         
1947         /*
1948                 Only print if something happened or saved whole map
1949         */
1950         if(only_changed == false || sector_meta_count != 0
1951                         || block_count != 0 || deleted_count != 0)
1952         {
1953                 dstream<<DTIME<<"ServerMap: Written: "
1954                                 <<sector_meta_count<<" sector metadata files, "
1955                                 <<block_count<<" block files, "
1956                                 <<deleted_count<<" sectors unloaded from memory."
1957                                 <<std::endl;
1958         }
1959 }
1960
1961 void ServerMap::loadAll()
1962 {
1963         DSTACK(__FUNCTION_NAME);
1964         dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
1965
1966         loadMasterHeightmap();
1967
1968         std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
1969
1970         dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
1971         
1972         JMutexAutoLock lock(m_sector_mutex);
1973         
1974         s32 counter = 0;
1975         s32 printed_counter = -100000;
1976         s32 count = list.size();
1977
1978         std::vector<fs::DirListNode>::iterator i;
1979         for(i=list.begin(); i!=list.end(); i++)
1980         {
1981                 if(counter > printed_counter + 10)
1982                 {
1983                         dstream<<DTIME<<counter<<"/"<<count<<std::endl;
1984                         printed_counter = counter;
1985                 }
1986                 counter++;
1987
1988                 MapSector *sector = NULL;
1989
1990                 // We want directories
1991                 if(i->dir == false)
1992                         continue;
1993                 try{
1994                         sector = loadSectorMeta(i->name);
1995                 }
1996                 catch(InvalidFilenameException &e)
1997                 {
1998                         // This catches unknown crap in directory
1999                 }
2000                 
2001                 if(ENABLE_BLOCK_LOADING)
2002                 {
2003                         std::vector<fs::DirListNode> list2 = fs::GetDirListing
2004                                         (m_savedir+"/sectors/"+i->name);
2005                         std::vector<fs::DirListNode>::iterator i2;
2006                         for(i2=list2.begin(); i2!=list2.end(); i2++)
2007                         {
2008                                 // We want files
2009                                 if(i2->dir)
2010                                         continue;
2011                                 try{
2012                                         loadBlock(i->name, i2->name, sector);
2013                                 }
2014                                 catch(InvalidFilenameException &e)
2015                                 {
2016                                         // This catches unknown crap in directory
2017                                 }
2018                         }
2019                 }
2020         }
2021         dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2022 }
2023
2024 void ServerMap::saveMasterHeightmap()
2025 {
2026         DSTACK(__FUNCTION_NAME);
2027         createDir(m_savedir);
2028         
2029         std::string fullpath = m_savedir + "/master_heightmap";
2030         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2031         if(o.good() == false)
2032                 throw FileNotGoodException("Cannot open master heightmap");
2033         
2034         // Format used for writing
2035         u8 version = SER_FMT_VER_HIGHEST;
2036
2037 #if 0
2038         SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2039         /*
2040                 [0] u8 serialization version
2041                 [1] X master heightmap
2042         */
2043         u32 fullsize = 1 + hmdata.getSize();
2044         SharedBuffer<u8> data(fullsize);
2045
2046         data[0] = version;
2047         memcpy(&data[1], *hmdata, hmdata.getSize());
2048
2049         o.write((const char*)*data, fullsize);
2050 #endif
2051         
2052         m_heightmap->serialize(o, version);
2053 }
2054
2055 void ServerMap::loadMasterHeightmap()
2056 {
2057         DSTACK(__FUNCTION_NAME);
2058         std::string fullpath = m_savedir + "/master_heightmap";
2059         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2060         if(is.good() == false)
2061                 throw FileNotGoodException("Cannot open master heightmap");
2062         
2063         if(m_heightmap != NULL)
2064                 delete m_heightmap;
2065                 
2066         m_heightmap = UnlimitedHeightmap::deSerialize(is);
2067 }
2068
2069 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2070 {
2071         DSTACK(__FUNCTION_NAME);
2072         // Format used for writing
2073         u8 version = SER_FMT_VER_HIGHEST;
2074         // Get destination
2075         v2s16 pos = sector->getPos();
2076         createDir(m_savedir);
2077         createDir(m_savedir+"/sectors");
2078         std::string dir = getSectorDir(pos);
2079         createDir(dir);
2080         
2081         std::string fullpath = dir + "/heightmap";
2082         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2083         if(o.good() == false)
2084                 throw FileNotGoodException("Cannot open master heightmap");
2085
2086         sector->serialize(o, version);
2087         
2088         sector->differs_from_disk = false;
2089 }
2090
2091 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2092 {
2093         DSTACK(__FUNCTION_NAME);
2094         // Get destination
2095         v2s16 p2d = getSectorPos(dirname);
2096         std::string dir = m_savedir + "/sectors/" + dirname;
2097         
2098         std::string fullpath = dir + "/heightmap";
2099         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2100         if(is.good() == false)
2101                 throw FileNotGoodException("Cannot open sector heightmap");
2102
2103         ServerMapSector *sector = ServerMapSector::deSerialize
2104                         (is, this, p2d, &m_hwrapper, m_sectors);
2105         
2106         sector->differs_from_disk = false;
2107
2108         return sector;
2109 }
2110
2111 bool ServerMap::loadSectorFull(v2s16 p2d)
2112 {
2113         DSTACK(__FUNCTION_NAME);
2114         std::string sectorsubdir = getSectorSubDir(p2d);
2115
2116         MapSector *sector = NULL;
2117
2118         JMutexAutoLock lock(m_sector_mutex);
2119
2120         try{
2121                 sector = loadSectorMeta(sectorsubdir);
2122         }
2123         catch(InvalidFilenameException &e)
2124         {
2125                 return false;
2126         }
2127         catch(FileNotGoodException &e)
2128         {
2129                 return false;
2130         }
2131         catch(std::exception &e)
2132         {
2133                 return false;
2134         }
2135
2136         if(ENABLE_BLOCK_LOADING)
2137         {
2138                 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2139                                 (m_savedir+"/sectors/"+sectorsubdir);
2140                 std::vector<fs::DirListNode>::iterator i2;
2141                 for(i2=list2.begin(); i2!=list2.end(); i2++)
2142                 {
2143                         // We want files
2144                         if(i2->dir)
2145                                 continue;
2146                         try{
2147                                 loadBlock(sectorsubdir, i2->name, sector);
2148                         }
2149                         catch(InvalidFilenameException &e)
2150                         {
2151                                 // This catches unknown crap in directory
2152                         }
2153                 }
2154         }
2155         return true;
2156 }
2157
2158 #if 0
2159 bool ServerMap::deFlushSector(v2s16 p2d)
2160 {
2161         DSTACK(__FUNCTION_NAME);
2162         // See if it already exists in memory
2163         try{
2164                 MapSector *sector = getSectorNoGenerate(p2d);
2165                 return true;
2166         }
2167         catch(InvalidPositionException &e)
2168         {
2169                 /*
2170                         Try to load the sector from disk.
2171                 */
2172                 if(loadSectorFull(p2d) == true)
2173                 {
2174                         return true;
2175                 }
2176         }
2177         return false;
2178 }
2179 #endif
2180
2181 void ServerMap::saveBlock(MapBlock *block)
2182 {
2183         DSTACK(__FUNCTION_NAME);
2184         /*
2185                 Dummy blocks are not written
2186         */
2187         if(block->isDummy())
2188         {
2189                 /*v3s16 p = block->getPos();
2190                 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2191                                 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2192                 return;
2193         }
2194
2195         // Format used for writing
2196         u8 version = SER_FMT_VER_HIGHEST;
2197         // Get destination
2198         v3s16 p3d = block->getPos();
2199         v2s16 p2d(p3d.X, p3d.Z);
2200         createDir(m_savedir);
2201         createDir(m_savedir+"/sectors");
2202         std::string dir = getSectorDir(p2d);
2203         createDir(dir);
2204         
2205         // Block file is map/sectors/xxxxxxxx/xxxx
2206         char cc[5];
2207         snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2208         std::string fullpath = dir + "/" + cc;
2209         std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2210         if(o.good() == false)
2211                 throw FileNotGoodException("Cannot open block data");
2212
2213         /*
2214                 [0] u8 serialization version
2215                 [1] data
2216         */
2217         o.write((char*)&version, 1);
2218         
2219         block->serialize(o, version);
2220
2221         /*
2222                 Versions up from 9 have block objects.
2223         */
2224         if(version >= 9)
2225         {
2226                 block->serializeObjects(o, version);
2227         }
2228         
2229         // We just wrote it to the disk
2230         block->resetChangedFlag();
2231 }
2232
2233 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2234 {
2235         DSTACK(__FUNCTION_NAME);
2236         // Block file is map/sectors/xxxxxxxx/xxxx
2237         std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2238         std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2239         if(is.good() == false)
2240                 throw FileNotGoodException("Cannot open block file");
2241
2242         v3s16 p3d = getBlockPos(sectordir, blockfile);
2243         v2s16 p2d(p3d.X, p3d.Z);
2244         
2245         assert(sector->getPos() == p2d);
2246         
2247         u8 version = SER_FMT_VER_INVALID;
2248         is.read((char*)&version, 1);
2249
2250         /*u32 block_size = MapBlock::serializedLength(version);
2251         SharedBuffer<u8> data(block_size);
2252         is.read((char*)*data, block_size);*/
2253
2254         // This will always return a sector because we're the server
2255         //MapSector *sector = emergeSector(p2d);
2256
2257         MapBlock *block = NULL;
2258         bool created_new = false;
2259         try{
2260                 block = sector->getBlockNoCreate(p3d.Y);
2261         }
2262         catch(InvalidPositionException &e)
2263         {
2264                 block = sector->createBlankBlockNoInsert(p3d.Y);
2265                 created_new = true;
2266         }
2267
2268         block->deSerialize(is, version);
2269         
2270         /*
2271                 Versions up from 9 have block objects.
2272         */
2273         if(version >= 9)
2274         {
2275                 block->updateObjects(is, version, NULL);
2276         }
2277
2278         if(created_new)
2279                 sector->insertBlock(block);
2280         
2281         /*
2282                 Convert old formats to new and save
2283         */
2284
2285         if(version == 0 || version == 1)
2286         {
2287                 dstream<<"Block ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2288                                 " is in old format. Updating lighting and saving"
2289                                 " modified blocks in new format."<<std::endl;
2290
2291                 // Old version has zero lighting, update it
2292                 core::map<v3s16, MapBlock*> blocks_changed;
2293                 blocks_changed.insert(block->getPos(), block);
2294                 core::map<v3s16, MapBlock*> modified_blocks;
2295                 updateLighting(blocks_changed, modified_blocks);
2296                 
2297                 // Close input file
2298                 is.close();
2299                 
2300                 // Save modified blocks
2301                 core::map<v3s16, MapBlock * >::Iterator i = modified_blocks.getIterator();
2302                 for(; i.atEnd() == false; i++)
2303                 {
2304                         MapBlock *b2 = i.getNode()->getValue();
2305                         saveBlock(b2);
2306                 }
2307         }
2308         // Save blocks in new format
2309         else if(version < SER_FMT_VER_HIGHEST)
2310         {
2311                 saveBlock(block);
2312         }
2313         
2314         // We just loaded it from the disk, so it's up-to-date.
2315         block->resetChangedFlag();
2316 }
2317
2318 // Gets from master heightmap
2319 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2320 {
2321         assert(m_heightmap != NULL);
2322         /*
2323                 Corner definition:
2324                 v2s16(0,0),
2325                 v2s16(1,0),
2326                 v2s16(1,1),
2327                 v2s16(0,1),
2328         */
2329         corners[0] = m_heightmap->getGroundHeight
2330                         ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2331         corners[1] = m_heightmap->getGroundHeight
2332                         ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2333         corners[2] = m_heightmap->getGroundHeight
2334                         ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2335         corners[3] = m_heightmap->getGroundHeight
2336                         ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2337 }
2338
2339 void ServerMap::PrintInfo(std::ostream &out)
2340 {
2341         out<<"ServerMap: ";
2342 }
2343
2344 /*
2345         ClientMap
2346 */
2347
2348 ClientMap::ClientMap(
2349                 Client *client,
2350                 video::SMaterial *materials,
2351                 scene::ISceneNode* parent,
2352                 scene::ISceneManager* mgr,
2353                 s32 id
2354 ):
2355         Map(dout_client),
2356         scene::ISceneNode(parent, mgr, id),
2357         m_client(client),
2358         m_materials(materials),
2359         mesh(NULL)
2360 {
2361         /*m_box = core::aabbox3d<f32>(0,0,0,
2362                         map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2363         /*m_box = core::aabbox3d<f32>(0,0,0,
2364                         map->getSizeNodes().X * BS,
2365                         map->getSizeNodes().Y * BS,
2366                         map->getSizeNodes().Z * BS);*/
2367         m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2368                         BS*1000000,BS*1000000,BS*1000000);
2369
2370         mesh_mutex.Init();
2371 }
2372
2373 ClientMap::~ClientMap()
2374 {
2375         JMutexAutoLock lock(mesh_mutex);
2376         
2377         if(mesh != NULL)
2378         {
2379                 mesh->drop();
2380                 mesh = NULL;
2381         }
2382 }
2383
2384 MapSector * ClientMap::emergeSector(v2s16 p2d)
2385 {
2386         DSTACK(__FUNCTION_NAME);
2387         // Check that it doesn't exist already
2388         try{
2389                 return getSectorNoGenerate(p2d);
2390         }
2391         catch(InvalidPositionException &e)
2392         {
2393         }
2394         
2395         // Create a sector with no heightmaps
2396         ClientMapSector *sector = new ClientMapSector(this, p2d);
2397         
2398         {
2399                 JMutexAutoLock lock(m_sector_mutex);
2400                 m_sectors.insert(p2d, sector);
2401         }
2402         
2403         return sector;
2404 }
2405
2406 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2407 {
2408         DSTACK(__FUNCTION_NAME);
2409         ClientMapSector *sector = NULL;
2410
2411         JMutexAutoLock lock(m_sector_mutex);
2412         
2413         core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2414
2415         if(n != NULL)
2416         {
2417                 sector = (ClientMapSector*)n->getValue();
2418                 assert(sector->getId() == MAPSECTOR_CLIENT);
2419         }
2420         else
2421         {
2422                 sector = new ClientMapSector(this, p2d);
2423                 {
2424                         JMutexAutoLock lock(m_sector_mutex);
2425                         m_sectors.insert(p2d, sector);
2426                 }
2427         }
2428
2429         sector->deSerialize(is);
2430 }
2431
2432 void ClientMap::renderMap(video::IVideoDriver* driver,
2433         video::SMaterial *materials, s32 pass)
2434 {
2435         //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2436         DSTACK(__FUNCTION_NAME);
2437
2438         bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2439 #if 0
2440         /*
2441                 Draw master heightmap mesh
2442         */
2443         
2444         {
2445                 JMutexAutoLock lock(mesh_mutex);
2446                 if(mesh != NULL)
2447                 {
2448                         u32 c = mesh->getMeshBufferCount();
2449
2450                         for(u32 i=0; i<c; i++)
2451                         {
2452                                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2453                                 const video::SMaterial& material = buf->getMaterial();
2454                                 video::IMaterialRenderer* rnd =
2455                                                 driver->getMaterialRenderer(material.MaterialType);
2456                                 bool transparent = (rnd && rnd->isTransparent());
2457                                 // Render transparent on transparent pass and likewise.
2458                                 if(transparent == is_transparent_pass)
2459                                 {
2460                                         driver->setMaterial(buf->getMaterial());
2461                                         driver->drawMeshBuffer(buf);
2462                                 }
2463                         }
2464                 }
2465         }
2466 #endif
2467
2468         /*
2469                 Get time for measuring timeout.
2470                 
2471                 Measuring time is very useful for long delays when the
2472                 machine is swapping a lot.
2473         */
2474         int time1 = time(0);
2475
2476         /*
2477                 Collect all blocks that are in the view range
2478
2479                 Should not optimize more here as we want to auto-update
2480                 all changed nodes in viewing range at the next step.
2481         */
2482
2483         s16 viewing_range_nodes;
2484         bool viewing_range_all;
2485         {
2486                 JMutexAutoLock lock(g_range_mutex);
2487                 viewing_range_nodes = g_viewing_range_nodes;
2488                 viewing_range_all = g_viewing_range_all;
2489         }
2490
2491         m_camera_mutex.Lock();
2492         v3f camera_position = m_camera_position;
2493         v3f camera_direction = m_camera_direction;
2494         m_camera_mutex.Unlock();
2495
2496         /*
2497                 Get all blocks and draw all visible ones
2498         */
2499
2500         v3s16 cam_pos_nodes(
2501                         camera_position.X / BS,
2502                         camera_position.Y / BS,
2503                         camera_position.Z / BS);
2504
2505         v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2506
2507         v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2508         v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2509
2510         // Take a fair amount as we will be dropping more out later
2511         v3s16 p_blocks_min(
2512                         p_nodes_min.X / MAP_BLOCKSIZE - 1,
2513                         p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2514                         p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2515         v3s16 p_blocks_max(
2516                         p_nodes_max.X / MAP_BLOCKSIZE + 1,
2517                         p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2518                         p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2519         
2520         u32 vertex_count = 0;
2521         
2522         core::map<v2s16, MapSector*>::Iterator si;
2523
2524         //NOTE: The sectors map should be locked but we're not doing it
2525         // because it'd cause too much delays
2526
2527         si = m_sectors.getIterator();
2528         for(; si.atEnd() == false; si++)
2529         {
2530                 {
2531                         static int timecheck_counter = 0;
2532                         timecheck_counter++;
2533                         if(timecheck_counter > 50)
2534                         {
2535                                 int time2 = time(0);
2536                                 if(time2 > time1 + 4)
2537                                 {
2538                                         dstream<<"ClientMap::renderMap(): "
2539                                                 "Rendering takes ages, returning."
2540                                                 <<std::endl;
2541                                         return;
2542                                 }
2543                         }
2544                 }
2545
2546                 MapSector *sector = si.getNode()->getValue();
2547                 v2s16 sp = sector->getPos();
2548                 
2549                 if(viewing_range_all == false)
2550                 {
2551                         if(sp.X < p_blocks_min.X
2552                         || sp.X > p_blocks_max.X
2553                         || sp.Y < p_blocks_min.Z
2554                         || sp.Y > p_blocks_max.Z)
2555                                 continue;
2556                 }
2557
2558                 core::list< MapBlock * > sectorblocks;
2559                 sector->getBlocks(sectorblocks);
2560                 
2561                 /*
2562                         Draw blocks
2563                 */
2564
2565                 core::list< MapBlock * >::Iterator i;
2566                 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2567                 {
2568                         MapBlock *block = *i;
2569
2570                         /*
2571                                 Compare block position to camera position, skip
2572                                 if not seen on display
2573                         */
2574                         
2575                         v3s16 blockpos_nodes = block->getPosRelative();
2576                         
2577                         // Block center position
2578                         v3f blockpos(
2579                                         ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2580                                         ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2581                                         ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2582                         );
2583
2584                         // Block position relative to camera
2585                         v3f blockpos_relative = blockpos - camera_position;
2586
2587                         // Distance in camera direction (+=front, -=back)
2588                         f32 dforward = blockpos_relative.dotProduct(camera_direction);
2589
2590                         // Total distance
2591                         f32 d = blockpos_relative.getLength();
2592                         
2593                         if(viewing_range_all == false)
2594                         {
2595                                 // If block is far away, don't draw it
2596                                 if(d > viewing_range_nodes * BS)
2597                                         continue;
2598                         }
2599                         
2600                         // Maximum radius of a block
2601                         f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2602                         
2603                         // If block is (nearly) touching the camera, don't
2604                         // bother validating further (that is, render it anyway)
2605                         if(d > block_max_radius * 1.5)
2606                         {
2607                                 // Cosine of the angle between the camera direction
2608                                 // and the block direction (camera_direction is an unit vector)
2609                                 f32 cosangle = dforward / d;
2610                                 
2611                                 // Compensate for the size of the block
2612                                 // (as the block has to be shown even if it's a bit off FOV)
2613                                 // This is an estimate.
2614                                 cosangle += block_max_radius / dforward;
2615
2616                                 // If block is not in the field of view, skip it
2617                                 //if(cosangle < cos(FOV_ANGLE/2))
2618                                 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2619                                         continue;
2620                         }
2621                         
2622                         /*
2623                                 Draw the faces of the block
2624                         */
2625                         
2626                         {
2627                                 JMutexAutoLock lock(block->mesh_mutex);
2628
2629                                 // Cancel if block has no mesh
2630                                 if(block->mesh == NULL)
2631                                         continue;
2632
2633                                 u32 c = block->mesh->getMeshBufferCount();
2634
2635                                 for(u32 i=0; i<c; i++)
2636                                 {
2637                                         scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2638                                         const video::SMaterial& material = buf->getMaterial();
2639                                         video::IMaterialRenderer* rnd =
2640                                                         driver->getMaterialRenderer(material.MaterialType);
2641                                         bool transparent = (rnd && rnd->isTransparent());
2642                                         // Render transparent on transparent pass and likewise.
2643                                         if(transparent == is_transparent_pass)
2644                                         {
2645                                                 driver->setMaterial(buf->getMaterial());
2646                                                 driver->drawMeshBuffer(buf);
2647                                                 vertex_count += buf->getVertexCount();
2648                                         }
2649                                 }
2650                         }
2651                 } // foreach sectorblocks
2652         }
2653
2654         /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2655                         <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2656 }
2657
2658 void ClientMap::updateMesh()
2659 {
2660 #if 0
2661         DSTACK(__FUNCTION_NAME);
2662         //TODO
2663         /*
2664                 Check what sectors don't draw anything useful at ground level
2665                 and create a mesh of the rough heightmap at those positions.
2666         */
2667
2668         m_camera_mutex.Lock();
2669         v3f camera_position = m_camera_position;
2670         v3f camera_direction = m_camera_direction;
2671         m_camera_mutex.Unlock();
2672
2673         v3s16 cam_pos_nodes(
2674                         camera_position.X / BS,
2675                         camera_position.Y / BS,
2676                         camera_position.Z / BS);
2677
2678         v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2679
2680         v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2681         v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2682
2683         // Take a fair amount as we will be dropping more out later
2684         v3s16 p_blocks_min(
2685                         p_nodes_min.X / MAP_BLOCKSIZE - 1,
2686                         p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2687                         p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2688         v3s16 p_blocks_max(
2689                         p_nodes_max.X / MAP_BLOCKSIZE + 1,
2690                         p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2691                         p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2692         
2693         /*
2694                 Initialize new mesh
2695         */
2696         
2697         scene::SMesh *mesh_new = new scene::SMesh();
2698         //scene::IMeshBuffer *buf = NULL;
2699         scene::SMeshBuffer *buf = NULL;
2700
2701         u8 material_in_use = 0;
2702
2703         /*
2704                 Loop through sectors
2705         */
2706         
2707         for(core::map<v2s16, MapSector*>::Iterator
2708                         si = m_sectors.getIterator();
2709                         si.atEnd() == false; si++)
2710         {
2711                 MapSector *sector = si.getNode()->getValue();
2712                 
2713                 if(sector->getId() != MAPSECTOR_CLIENT)
2714                 {
2715                         dstream<<"WARNING: Client has a non-client sector"
2716                                         <<std::endl;
2717                         continue;
2718                 }
2719                 
2720                 ClientMapSector *cs = (ClientMapSector*)sector;
2721
2722                 v2s16 sp = sector->getPos();
2723
2724                 if(sp.X < p_blocks_min.X
2725                 || sp.X > p_blocks_max.X
2726                 || sp.Y < p_blocks_min.Z
2727                 || sp.Y > p_blocks_max.Z)
2728                         continue;
2729                 
2730                 /*
2731                         Get some ground level info
2732                 */
2733                 
2734                 s16 a = -5;
2735
2736                 s16 cn[4] = 
2737                 {
2738                         cs->getCorner(0)+a,
2739                         cs->getCorner(1)+a,
2740                         cs->getCorner(2)+a,
2741                         cs->getCorner(3)+a,
2742                 };
2743                 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
2744                 s16 cn_min = 32767;
2745                 s16 cn_max = -32768;
2746                 for(s16 i=0; i<4; i++)
2747                 {
2748                         if(cn[i] < cn_min)
2749                                 cn_min = cn[i];
2750                         if(cn[i] > cn_max)
2751                                 cn_max = cn[i];
2752                 }
2753                 s16 cn_slope = cn_max - cn_min;
2754                 
2755                 /*
2756                         Generate this part of the heightmap mesh
2757                 */
2758
2759                 u8 material;
2760                 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
2761                         material = 0;
2762                 else if(cn_slope <= MAP_BLOCKSIZE)
2763                         material = 1;
2764                 else
2765                         material = 2;
2766
2767                 if(material != material_in_use || buf == NULL)
2768                 {
2769                         // Try to get a meshbuffer associated with the material
2770                         buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
2771                                         (g_mesh_materials[material]);
2772                         // If not found, create one
2773                         if(buf == NULL)
2774                         {
2775                                 // This is a "Standard MeshBuffer",
2776                                 // it's a typedeffed CMeshBuffer<video::S3DVertex>
2777                                 buf = new scene::SMeshBuffer();
2778
2779                                 // Set material
2780                                 buf->Material = g_mesh_materials[material];
2781                                 // Use VBO
2782                                 //buf->setHardwareMappingHint(scene::EHM_STATIC);
2783                                 // Add to mesh
2784                                 mesh_new->addMeshBuffer(buf);
2785                                 // Mesh grabbed it
2786                                 buf->drop();
2787                         }
2788                         material_in_use = material;
2789                 }
2790
2791                 // Sector side width in floating-point units
2792                 f32 sd = BS * MAP_BLOCKSIZE;
2793                 // Sector position in global floating-point units
2794                 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
2795
2796                 //video::SColor c(255,255,255,255);
2797                 u8 cc = 180;
2798                 video::SColor c(255,cc,cc,cc);
2799                 
2800                 video::S3DVertex vertices[4] =
2801                 {
2802                         video::S3DVertex(spf.X,   (f32)BS*cn[0],spf.Z,   0,0,0, c, 0,1),
2803                         video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z,   0,0,0, c, 1,1),
2804                         video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
2805                         video::S3DVertex(spf.X,   (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
2806                 };
2807                 u16 indices[] = {0,1,2,2,3,0};
2808                 
2809                 buf->append(vertices, 4, indices, 6);
2810         }
2811         
2812         // Set VBO on
2813         //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
2814
2815         /*
2816                 Replace the mesh
2817         */
2818
2819         mesh_mutex.Lock();
2820
2821         scene::SMesh *mesh_old = mesh;
2822
2823         //DEBUG
2824         /*mesh = NULL;
2825         mesh_new->drop();*/
2826         mesh = mesh_new;
2827         
2828         mesh_mutex.Unlock();
2829
2830         if(mesh_old != NULL)
2831         {
2832                 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
2833                                 <<std::endl;
2834                 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
2835                                 (g_materials[MATERIAL_GRASS]);
2836                 if(buf != NULL)
2837                         dstream<<"grass buf refcount="<<buf->getReferenceCount()
2838                                         <<std::endl;*/
2839
2840                 mesh_old->drop();
2841         }
2842         else
2843         {
2844                 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
2845         }
2846 #endif
2847 }
2848
2849 void ClientMap::PrintInfo(std::ostream &out)
2850 {
2851         out<<"ClientMap: ";
2852 }
2853
2854