2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
8 #include "jmutexautolock.h"
15 #define sleep_ms(x) Sleep(x)
18 #define sleep_ms(x) usleep(x*1000)
21 Map::Map(std::ostream &dout):
23 m_camera_position(0,0,0),
24 m_camera_direction(0,0,1),
29 m_sector_mutex.Init();
30 m_camera_mutex.Init();
31 assert(m_sector_mutex.IsInitialized());
32 assert(m_camera_mutex.IsInitialized());
34 // Get this so that the player can stay on it at first
35 //getSector(v2s16(0,0));
43 /*updater.setRun(false);
44 while(updater.IsRunning())
50 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
51 for(; i.atEnd() == false; i++)
53 MapSector *sector = i.getNode()->getValue();
58 /*bool Map::sectorExists(v2s16 p)
60 JMutexAutoLock lock(m_sector_mutex);
61 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
65 MapSector * Map::getSectorNoGenerate(v2s16 p)
67 JMutexAutoLock lock(m_sector_mutex);
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;
76 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
77 // If sector doesn't exist, throw an exception
80 throw InvalidPositionException();
83 MapSector *sector = n->getValue();
85 // Cache the last result
87 m_sector_cache = sector;
89 //MapSector * ref(sector);
91 // Reset inactivity timer
92 sector->usage_timer = 0.0;
96 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
98 v2s16 p2d(p3d.X, p3d.Z);
99 MapSector * sector = getSectorNoGenerate(p2d);
101 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
106 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
108 dstream<<"Map::getBlock() with generate=true called"
110 v2s16 p2d(p3d.X, p3d.Z);
111 //MapSector * sector = getSector(p2d, generate);
112 MapSector * sector = getSectorNoGenerate(p2d);
115 throw InvalidPositionException();
117 return sector->getBlockNoCreate(p3d.Y);
120 f32 Map::getGroundHeight(v2s16 p, bool generate)
123 v2s16 sectorpos = getNodeSectorPos(p);
124 MapSector * sref = getSectorNoGenerate(sectorpos);
125 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
126 f32 y = sref->getGroundHeight(relpos);
129 catch(InvalidPositionException &e)
131 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
135 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
137 /*m_dout<<DTIME<<"Map::setGroundHeight(("
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();
148 bool Map::isNodeUnderground(v3s16 p)
150 v3s16 blockpos = getNodeBlockPos(p);
152 MapBlock * block = getBlockNoCreate(blockpos);
153 return block->getIsUnderground();
155 catch(InvalidPositionException &e)
162 //TODO: Remove: Not used.
164 Goes recursively through the neighbours of the node.
166 Alters only transparent nodes.
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.
173 Some things are made strangely to make it as fast as possible.
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);
183 void Map::unLightNeighbors(v3s16 pos, u8 oldlight,
184 core::map<v3s16, bool> & light_sources,
185 core::map<v3s16, MapBlock*> & modified_blocks)
188 v3s16(0,0,1), // back
190 v3s16(1,0,0), // right
191 v3s16(0,0,-1), // front
192 v3s16(0,-1,0), // bottom
193 v3s16(-1,0,0), // left
197 Initialize block cache
200 MapBlock *block = NULL;
201 // Cache this a bit, too
202 bool block_checked_in_modified = false;
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];
209 // Get the block where the node is located
210 v3s16 blockpos = getNodeBlockPos(n2pos);
212 // Only fetch a new block if the block position has changed
214 if(block == NULL || blockpos != blockpos_last)
216 block = getBlockNoCreate(blockpos);
217 blockpos_last = blockpos;
219 block_checked_in_modified = false;
220 //blockchangecount++;
223 catch(InvalidPositionException &e)
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);
237 If the neighbor is dimmer than what was specified
238 as oldlight (the light of the previous node)
240 if(n2.getLight() < oldlight)
243 And the neighbor is transparent and it has some light
245 if(n2.light_propagates() && n2.getLight() != 0)
248 Set light to 0 and recurse.
250 u8 current_light = n2.getLight();
252 block->setNode(relpos, n2);
253 unLightNeighbors(n2pos, current_light,
254 light_sources, modified_blocks);
256 if(block_checked_in_modified == false)
258 // If the block is not found in modified_blocks, add.
259 if(modified_blocks.find(blockpos) == NULL)
261 modified_blocks.insert(blockpos, block);
263 block_checked_in_modified = true;
268 //light_sources.push_back(n2pos);
269 light_sources.insert(n2pos, true);
276 Goes recursively through the neighbours of the node.
278 Alters only transparent nodes.
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.
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.
290 values of from_nodes are lighting values.
292 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
293 core::map<v3s16, bool> & light_sources,
294 core::map<v3s16, MapBlock*> & modified_blocks)
297 v3s16(0,0,1), // back
299 v3s16(1,0,0), // right
300 v3s16(0,0,-1), // front
301 v3s16(0,-1,0), // bottom
302 v3s16(-1,0,0), // left
305 if(from_nodes.size() == 0)
308 u32 blockchangecount = 0;
310 core::map<v3s16, u8> unlighted_nodes;
311 core::map<v3s16, u8>::Iterator j;
312 j = from_nodes.getIterator();
315 Initialize block cache
318 MapBlock *block = NULL;
319 // Cache this a bit, too
320 bool block_checked_in_modified = false;
322 for(; j.atEnd() == false; j++)
324 v3s16 pos = j.getNode()->getKey();
325 v3s16 blockpos = getNodeBlockPos(pos);
327 // Only fetch a new block if the block position has changed
329 if(block == NULL || blockpos != blockpos_last){
330 block = getBlockNoCreate(blockpos);
331 blockpos_last = blockpos;
333 block_checked_in_modified = false;
337 catch(InvalidPositionException &e)
345 // Calculate relative position in block
346 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
348 // Get node straight from the block
349 MapNode n = block->getNode(relpos);
351 u8 oldlight = j.getNode()->getValue();
353 // Loop through 6 neighbors
354 for(u16 i=0; i<6; i++)
356 // Get the position of the neighbor node
357 v3s16 n2pos = pos + dirs[i];
359 // Get the block where the node is located
360 v3s16 blockpos = getNodeBlockPos(n2pos);
364 // Only fetch a new block if the block position has changed
366 if(block == NULL || blockpos != blockpos_last){
367 block = getBlockNoCreate(blockpos);
368 blockpos_last = blockpos;
370 block_checked_in_modified = false;
374 catch(InvalidPositionException &e)
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);
384 bool changed = false;
386 //TODO: Optimize output by optimizing light_sources?
389 If the neighbor is dimmer than what was specified
390 as oldlight (the light of the previous node)
392 if(n2.getLight() < oldlight)
395 And the neighbor is transparent and it has some light
397 if(n2.light_propagates() && n2.getLight() != 0)
400 Set light to 0 and add to queue
403 u8 current_light = n2.getLight();
405 block->setNode(relpos, n2);
407 unlighted_nodes.insert(n2pos, current_light);
411 Remove from light_sources if it is there
412 NOTE: This doesn't happen nearly at all
414 /*if(light_sources.find(n2pos))
416 std::cout<<"Removed from light_sources"<<std::endl;
417 light_sources.remove(n2pos);
422 light_sources.insert(n2pos, true);
425 // Add to modified_blocks
426 if(changed == true && block_checked_in_modified == false)
428 // If the block is not found in modified_blocks, add.
429 if(modified_blocks.find(blockpos) == NULL)
431 modified_blocks.insert(blockpos, block);
433 block_checked_in_modified = true;
436 catch(InvalidPositionException &e)
443 /*dstream<<"unspreadLight(): Changed block "
444 <<blockchangecount<<" times"
445 <<" for "<<from_nodes.size()<<" nodes"
448 if(unlighted_nodes.size() > 0)
449 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
453 A single-node wrapper of the above
455 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
456 core::map<v3s16, bool> & light_sources,
457 core::map<v3s16, MapBlock*> & modified_blocks)
459 core::map<v3s16, u8> from_nodes;
460 from_nodes.insert(pos, lightwas);
462 unspreadLight(from_nodes, light_sources, modified_blocks);
466 Lights neighbors of from_nodes, collects all them and then
469 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
470 core::map<v3s16, MapBlock*> & modified_blocks)
472 const v3s16 dirs[6] = {
473 v3s16(0,0,1), // back
475 v3s16(1,0,0), // right
476 v3s16(0,0,-1), // front
477 v3s16(0,-1,0), // bottom
478 v3s16(-1,0,0), // left
481 if(from_nodes.size() == 0)
484 u32 blockchangecount = 0;
486 core::map<v3s16, bool> lighted_nodes;
487 core::map<v3s16, bool>::Iterator j;
488 j = from_nodes.getIterator();
491 Initialize block cache
494 MapBlock *block = NULL;
495 // Cache this a bit, too
496 bool block_checked_in_modified = false;
498 for(; j.atEnd() == false; j++)
499 //for(; j != from_nodes.end(); j++)
501 v3s16 pos = j.getNode()->getKey();
503 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
504 v3s16 blockpos = getNodeBlockPos(pos);
506 // Only fetch a new block if the block position has changed
508 if(block == NULL || blockpos != blockpos_last){
509 block = getBlockNoCreate(blockpos);
510 blockpos_last = blockpos;
512 block_checked_in_modified = false;
516 catch(InvalidPositionException &e)
524 // Calculate relative position in block
525 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
527 // Get node straight from the block
528 MapNode n = block->getNode(relpos);
530 u8 oldlight = n.getLight();
531 u8 newlight = diminish_light(oldlight);
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];
538 // Get the block where the node is located
539 v3s16 blockpos = getNodeBlockPos(n2pos);
543 // Only fetch a new block if the block position has changed
545 if(block == NULL || blockpos != blockpos_last){
546 block = getBlockNoCreate(blockpos);
547 blockpos_last = blockpos;
549 block_checked_in_modified = false;
553 catch(InvalidPositionException &e)
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);
563 bool changed = false;
565 If the neighbor is brighter than the current node,
566 add to list (it will light up this node on its turn)
568 if(n2.getLight() > undiminish_light(oldlight))
570 lighted_nodes.insert(n2pos, true);
571 //lighted_nodes.push_back(n2pos);
575 If the neighbor is dimmer than how much light this node
576 would spread on it, add to list
578 if(n2.getLight() < newlight)
580 if(n2.light_propagates())
582 n2.setLight(newlight);
583 block->setNode(relpos, n2);
584 lighted_nodes.insert(n2pos, true);
585 //lighted_nodes.push_back(n2pos);
590 // Add to modified_blocks
591 if(changed == true && block_checked_in_modified == false)
593 // If the block is not found in modified_blocks, add.
594 if(modified_blocks.find(blockpos) == NULL)
596 modified_blocks.insert(blockpos, block);
598 block_checked_in_modified = true;
601 catch(InvalidPositionException &e)
608 /*dstream<<"spreadLight(): Changed block "
609 <<blockchangecount<<" times"
610 <<" for "<<from_nodes.size()<<" nodes"
613 if(lighted_nodes.size() > 0)
614 spreadLight(lighted_nodes, modified_blocks);
618 A single-node source variation of the above.
620 void Map::lightNeighbors(v3s16 pos,
621 core::map<v3s16, MapBlock*> & modified_blocks)
623 core::map<v3s16, bool> from_nodes;
624 from_nodes.insert(pos, true);
625 spreadLight(from_nodes, modified_blocks);
628 v3s16 Map::getBrightestNeighbour(v3s16 p)
631 v3s16(0,0,1), // back
633 v3s16(1,0,0), // right
634 v3s16(0,0,-1), // front
635 v3s16(0,-1,0), // bottom
636 v3s16(-1,0,0), // left
639 u8 brightest_light = 0;
640 v3s16 brightest_pos(0,0,0);
641 bool found_something = false;
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];
651 catch(InvalidPositionException &e)
655 if(n2.getLight() > brightest_light || found_something == false){
656 brightest_light = n2.getLight();
657 brightest_pos = n2pos;
658 found_something = true;
662 if(found_something == false)
663 throw InvalidPositionException();
665 return brightest_pos;
669 Propagates sunlight down from a node.
670 Starting point gets sunlight.
672 Returns the lowest y value of where the sunlight went.
674 s16 Map::propagateSunlight(v3s16 start,
675 core::map<v3s16, MapBlock*> & modified_blocks)
680 v3s16 pos(start.X, y, start.Z);
682 v3s16 blockpos = getNodeBlockPos(pos);
685 block = getBlockNoCreate(blockpos);
687 catch(InvalidPositionException &e)
692 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
693 MapNode n = block->getNode(relpos);
695 if(n.sunlight_propagates())
697 n.setLight(LIGHT_SUN);
698 block->setNode(relpos, n);
700 modified_blocks.insert(blockpos, block);
709 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
710 core::map<v3s16, MapBlock*> & modified_blocks)
712 /*m_dout<<DTIME<<"Map::updateLighting(): "
713 <<a_blocks.getSize()<<" blocks... ";*/
717 u32 count_was = modified_blocks.size();
719 /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
720 for(; i != a_blocks.end(); i++)
722 MapBlock *block = *i;*/
724 core::map<v3s16, bool> light_sources;
726 core::map<v3s16, u8> unlight_from;
728 core::map<v3s16, MapBlock*>::Iterator i;
729 i = a_blocks.getIterator();
730 for(; i.atEnd() == false; i++)
732 MapBlock *block = i.getNode()->getValue();
736 // Don't bother with dummy blocks.
740 v3s16 pos = block->getPos();
741 modified_blocks.insert(pos, block);
744 Clear all light from block
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++)
753 MapNode n = block->getNode(v3s16(x,y,z));
754 u8 oldlight = n.getLight();
756 block->setNode(v3s16(x,y,z), n);
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)
763 v3s16 p_map = p + v3s16(
766 MAP_BLOCKSIZE*pos.Z);
767 unlight_from.insert(p_map, oldlight);
770 catch(InvalidPositionException &e)
773 This would happen when dealing with a
777 dstream<<"updateLighting(): InvalidPositionException"
782 bool bottom_valid = block->propagateSunlight(light_sources);
784 // If bottom is valid, we're done.
788 /*dstream<<"Bottom for sunlight-propagated block ("
789 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
792 // Else get the block below and loop to it
796 block = getBlockNoCreate(pos);
798 catch(InvalidPositionException &e)
807 //TimeTaker timer("unspreadLight", g_device);
808 unspreadLight(unlight_from, light_sources, modified_blocks);
813 u32 diff = modified_blocks.size() - count_was;
814 count_was = modified_blocks.size();
815 dstream<<"unspreadLight modified "<<diff<<std::endl;
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.
824 //TimeTaker timer("spreadLight", g_device);
825 spreadLight(light_sources, modified_blocks);
830 u32 diff = modified_blocks.size() - count_was;
831 count_was = modified_blocks.size();
832 dstream<<"spreadLight modified "<<diff<<std::endl;
835 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
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.
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)
849 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
850 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
852 u8 lightwas = getNode(p).getLight();
854 //core::list<v3s16> light_sources;
855 core::map<v3s16, bool> light_sources;
856 //MapNode n = getNode(p);
859 From this node to nodes underneath:
860 If lighting is sunlight (1.0), unlight neighbours and
865 bool node_under_sunlight = true;
867 v3s16 toppos = p + v3s16(0,1,0);
870 If there is a node at top and it doesn't have sunlight,
871 there has not been any sunlight going down.
873 Otherwise there probably is.
876 MapNode topnode = getNode(toppos);
878 if(topnode.getLight() != LIGHT_SUN)
879 node_under_sunlight = false;
881 catch(InvalidPositionException &e)
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);
891 if(isValidPosition(p) == false)
894 // Unlight neighbours of node.
895 // This means setting light of all consequent dimmer nodes
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);
905 If node is under sunlight, take all sunlighted nodes under
906 it and clear light from them and from where the light has
909 if(node_under_sunlight)
913 //m_dout<<DTIME<<"y="<<y<<std::endl;
914 v3s16 n2pos(p.X, y, p.Z);
920 catch(InvalidPositionException &e)
925 if(n2.getLight() == LIGHT_SUN)
927 //m_dout<<DTIME<<"doing"<<std::endl;
928 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
938 Spread light from all nodes that might be capable of doing so
939 TODO: Convert to spreadLight
941 spreadLight(light_sources, modified_blocks);
946 void Map::removeNodeAndUpdate(v3s16 p,
947 core::map<v3s16, MapBlock*> &modified_blocks)
950 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
951 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
953 bool node_under_sunlight = true;
955 v3s16 toppos = p + v3s16(0,1,0);
958 If there is a node at top and it doesn't have sunlight,
959 there will be no sunlight going down.
962 MapNode topnode = getNode(toppos);
964 if(topnode.getLight() != LIGHT_SUN)
965 node_under_sunlight = false;
967 catch(InvalidPositionException &e)
972 Unlight neighbors (in case the node is a light source)
974 //core::list<v3s16> light_sources;
975 core::map<v3s16, bool> light_sources;
976 unLightNeighbors(p, getNode(p).getLight(),
977 light_sources, modified_blocks);
990 spreadLight(light_sources, modified_blocks);
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);
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.
1003 if(node_under_sunlight)
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;*/
1010 for(; y >= ybottom; y--)
1012 v3s16 p2(p.X, y, p.Z);
1013 /*m_dout<<DTIME<<"lighting neighbors of node ("
1014 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1016 lightNeighbors(p2, modified_blocks);
1021 // Set the lighting of this node to 0
1023 MapNode n = getNode(p);
1027 catch(InvalidPositionException &e)
1033 // Get the brightest neighbour node and propagate light from it
1034 v3s16 n2p = getBrightestNeighbour(p);
1036 MapNode n2 = getNode(n2p);
1037 lightNeighbors(n2p, modified_blocks);
1039 catch(InvalidPositionException &e)
1044 void Map::updateMeshes(v3s16 blockpos)
1046 assert(mapType() == MAPTYPE_CLIENT);
1049 v3s16 p = blockpos + v3s16(0,0,0);
1050 MapBlock *b = getBlockNoCreate(p);
1053 catch(InvalidPositionException &e){}
1055 v3s16 p = blockpos + v3s16(-1,0,0);
1056 MapBlock *b = getBlockNoCreate(p);
1059 catch(InvalidPositionException &e){}
1061 v3s16 p = blockpos + v3s16(0,-1,0);
1062 MapBlock *b = getBlockNoCreate(p);
1065 catch(InvalidPositionException &e){}
1067 v3s16 p = blockpos + v3s16(0,0,-1);
1068 MapBlock *b = getBlockNoCreate(p);
1071 catch(InvalidPositionException &e){}
1075 Updates usage timers
1077 void Map::timerUpdate(float dtime)
1079 JMutexAutoLock lock(m_sector_mutex);
1081 core::map<v2s16, MapSector*>::Iterator si;
1083 si = m_sectors.getIterator();
1084 for(; si.atEnd() == false; si++)
1086 MapSector *sector = si.getNode()->getValue();
1087 sector->usage_timer += dtime;
1091 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1093 core::list<v2s16>::Iterator j;
1094 for(j=list.begin(); j!=list.end(); j++)
1096 MapSector *sector = m_sectors[*j];
1099 sector->deleteBlocks();
1104 If sector is in sector cache, remove it from there
1106 if(m_sector_cache == sector)
1108 m_sector_cache = NULL;
1111 Remove from map and delete
1113 m_sectors.remove(*j);
1119 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1120 core::list<v3s16> *deleted_blocks)
1122 JMutexAutoLock lock(m_sector_mutex);
1124 core::list<v2s16> sector_deletion_queue;
1125 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1126 for(; i.atEnd() == false; i++)
1128 MapSector *sector = i.getNode()->getValue();
1130 Delete sector from memory if it hasn't been used in a long time
1132 if(sector->usage_timer > timeout)
1134 sector_deletion_queue.push_back(i.getNode()->getKey());
1136 if(deleted_blocks != NULL)
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++)
1145 deleted_blocks->push_back((*i)->getPos());
1150 deleteSectors(sector_deletion_queue, only_blocks);
1151 return sector_deletion_queue.getSize();
1154 void Map::PrintInfo(std::ostream &out)
1163 ServerMap::ServerMap(std::string savedir, MapgenParams params):
1167 m_savedir = savedir;
1168 m_map_saving_enabled = false;
1172 // If directory exists, check contents and load if possible
1173 if(fs::PathExists(m_savedir))
1175 // If directory is empty, it is safe to save into it.
1176 if(fs::GetDirListing(m_savedir).size() == 0)
1178 dstream<<DTIME<<"Server: Empty save directory is valid."
1180 m_map_saving_enabled = true;
1184 // Load master heightmap
1185 loadMasterHeightmap();
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)");
1191 dstream<<DTIME<<"Server: Successfully loaded master "
1192 "heightmap and sector (0,0) from "<<savedir<<
1193 ", assuming valid save directory."
1196 m_map_saving_enabled = true;
1197 // Map loaded, not creating new one
1201 // If directory doesn't exist, it is safe to save to it
1203 m_map_saving_enabled = true;
1206 catch(std::exception &e)
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;
1214 dstream<<DTIME<<"Initializing new map."<<std::endl;
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);
1225 // Create zero sector
1226 emergeSector(v2s16(0,0));
1228 // Initially write whole map
1232 ServerMap::~ServerMap()
1236 if(m_map_saving_enabled)
1239 // Save only changed parts
1241 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1245 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1248 catch(std::exception &e)
1250 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1251 <<", exception: "<<e.what()<<std::endl;
1254 if(m_heightmap != NULL)
1258 MapSector * ServerMap::emergeSector(v2s16 p2d)
1260 DSTACK("%s: p2d=(%d,%d)",
1263 // Check that it doesn't exist already
1265 return getSectorNoGenerate(p2d);
1267 catch(InvalidPositionException &e)
1272 Try to load the sector from disk.
1274 if(loadSectorFull(p2d) == true)
1276 return getSectorNoGenerate(p2d);
1280 If there is no master heightmap, throw.
1282 if(m_heightmap == NULL)
1284 throw InvalidPositionException("emergeSector(): no heightmap");
1288 Do not generate over-limit
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");
1297 Generate sector and heightmaps
1300 // Number of heightmaps in sector in each direction
1301 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1303 // Heightmap side width
1304 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1306 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1308 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1309 " heightmaps and objects"<<std::endl;*/
1311 // Loop through sub-heightmaps
1312 for(s16 y=0; y<hm_split; y++)
1313 for(s16 x=0; x<hm_split; x++)
1315 v2s16 p_in_sector = v2s16(x,y);
1316 v2s16 mhm_p = p2d * hm_split + p_in_sector;
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)),
1324 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1325 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1328 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1330 sector->setHeightmap(p_in_sector, hm);
1332 //TODO: Make these values configurable
1333 hm->generateContinued(1.0, 0.2, corners);
1334 //hm->generateContinued(2.0, 0.2, corners);
1344 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1345 sector->setObjects(objects);
1347 v2s16 mhm_p = p2d * hm_split;
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),
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]);
1362 avgslope /= MAP_BLOCKSIZE;
1363 //dstream<<"avgslope="<<avgslope<<std::endl;
1365 float pitness = 0.0;
1367 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1370 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1373 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1376 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1380 pitness /= MAP_BLOCKSIZE;
1381 //dstream<<"pitness="<<pitness<<std::endl;
1384 Plant some trees if there is not much slope
1387 // Avgslope is the derivative of a hill
1388 float t = avgslope * avgslope;
1389 float a = MAP_BLOCKSIZE * 2;
1392 tree_max = a / (t/0.03);
1395 u32 count = (rand()%(tree_max+1));
1396 //u32 count = tree_max;
1397 for(u32 i=0; i<count; i++)
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;
1404 objects->insert(v3s16(x, y, z),
1405 SECTOR_OBJECT_TREE_1);
1409 // Pitness usually goes at around -0.5...0.5
1411 u32 a = MAP_BLOCKSIZE * 3;
1413 bush_max = (pitness*a*4);
1416 u32 count = (rand()%(bush_max+1));
1417 for(u32 i=0; i<count; i++)
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;
1424 objects->insert(v3s16(x, y, z),
1425 SECTOR_OBJECT_BUSH_1);
1432 JMutexAutoLock lock(m_sector_mutex);
1433 m_sectors.insert(p2d, sector);
1438 MapBlock * ServerMap::emergeBlock(
1440 bool only_from_disk,
1441 core::map<v3s16, MapBlock*> &changed_blocks,
1442 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1445 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1447 p.X, p.Y, p.Z, only_from_disk);
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);
1455 This will create or load a sector if not found in memory.
1456 If block exists on disk, it will be loaded.
1458 NOTE: On old save formats, this will be slow, as it generates
1459 lighting on blocks for them.
1461 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1462 assert(sector->getId() == MAPSECTOR_SERVER);
1464 // Try to get a block from the sector
1465 MapBlock *block = NULL;
1466 bool not_on_disk = false;
1468 block = sector->getBlockNoCreate(block_y);
1469 if(block->isDummy() == true)
1474 catch(InvalidPositionException &e)
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.
1483 if(not_on_disk && only_from_disk)
1487 // Create dummy block
1488 block = new MapBlock(this, p, true);
1490 // Add block to sector
1491 sector->insertBlock(block);
1497 //dstream<<"Not found on disk, generating."<<std::endl;
1500 Do not generate over-limit
1502 if(blockpos_over_limit(p))
1503 throw InvalidPositionException("emergeBlock(): pos. over limit");
1508 Go on generating the block.
1510 TODO: If a dungeon gets generated so that it's side gets
1511 revealed to the outside air, the lighting should be
1516 If block doesn't exist, create one.
1517 If it exists, it is a dummy. In that case unDummify() it.
1521 block = sector->createBlankBlockNoInsert(block_y);
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.
1531 // Randomize a bit. This makes dungeons.
1532 bool low_block_is_empty = false;
1534 low_block_is_empty = true;
1536 // This is the basic material of what the visible flat ground
1538 u8 material = MATERIAL_GRASS;
1540 s32 lowest_ground_y = 32767;
1543 //sector->printHeightmaps();
1545 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1546 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1548 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1549 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1551 assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
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;
1558 s32 surface_depth = 0;
1560 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
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;
1571 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1573 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++){
1574 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1579 FIXME: If there are some man-made structures above the
1580 newly created block, they won't be taken into account.
1582 if(real_y > surface_y)
1583 n.setLight(LIGHT_SUN);
1587 // If node is very low
1588 if(real_y <= surface_y - 10){
1590 if(low_block_is_empty){
1594 n.d = MATERIAL_STONE;
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)
1603 // If node is over heightmap y
1605 // If under water level, it's water
1606 if(real_y < WATER_LEVEL)
1608 n.d = MATERIAL_WATER;
1609 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1615 block->setNode(v3s16(x0,y0,z0), n);
1620 Calculate is_underground
1622 // Probably underground if the highest part of block is under lowest
1624 bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1625 block->setIsUnderground(is_underground);
1631 if(is_underground && low_block_is_empty == false)
1633 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1634 for(s16 i=0; i<underground_level*3; i++)
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
1648 n.d = MATERIAL_MESE;
1651 block->setNode(cp, n);
1653 for(u16 i=0; i<26; i++)
1656 block->setNode(cp+g_26dirs[i], n);
1663 Create a few rats in empty blocks underground
1665 if(is_underground && low_block_is_empty == true)
1667 //for(u16 i=0; i<2; i++)
1670 RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1671 block->addObject(obj);
1678 Add some objects to the block for testing.
1680 /*if(p == v3s16(0,0,0))
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);
1689 v3s16 pos(8, 11, 8);
1690 SignObject *obj = new SignObject(NULL, -1, intToFloat(pos));
1691 obj->setText("Moicka");
1693 block->addObject(obj);
1697 v3s16 pos(8, 11, 8);
1698 RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1699 block->addObject(obj);
1704 Add block to sector.
1706 sector->insertBlock(block);
1708 // An y-wise container if changed blocks
1709 core::map<s16, MapBlock*> changed_blocks_sector;
1712 Check if any sector's objects can be placed now.
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++)
1720 v3s16 p = i.getNode()->getKey();
1721 u8 d = i.getNode()->getValue();
1723 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1728 if(d == SECTOR_OBJECT_TEST)
1730 if(sector->isValidArea(p + v3s16(0,0,0),
1731 p + v3s16(0,0,0), &changed_blocks_sector))
1734 n.d = MATERIAL_LIGHT;
1735 sector->setNode(p, n);
1736 objects_to_remove.push_back(p);
1739 else if(d == SECTOR_OBJECT_TREE_1)
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))
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);
1753 n.d = MATERIAL_LEAVES;
1755 sector->setNode(p+v3s16(0,4,0), n);
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);
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);
1775 objects_to_remove.push_back(p);
1777 // Lighting has to be recalculated for this one.
1778 sector->getBlocksInArea(p_min, p_max,
1779 lighting_invalidated_blocks);
1782 else if(d == SECTOR_OBJECT_BUSH_1)
1784 if(sector->isValidArea(p + v3s16(0,0,0),
1785 p + v3s16(0,0,0), &changed_blocks_sector))
1788 n.d = MATERIAL_LEAVES;
1789 sector->setNode(p+v3s16(0,0,0), n);
1791 objects_to_remove.push_back(p);
1796 dstream<<"ServerMap::emergeBlock(): "
1797 "Invalid heightmap object"
1802 catch(InvalidPositionException &e)
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.
1814 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
1815 i != objects_to_remove.end(); i++)
1817 objects->remove(*i);
1820 for(core::map<s16, MapBlock*>::Iterator
1821 i = changed_blocks_sector.getIterator();
1822 i.atEnd() == false; i++)
1824 MapBlock *block = i.getNode()->getValue();
1826 changed_blocks.insert(block->getPos(), block);
1832 void ServerMap::createDir(std::string path)
1834 if(fs::CreateDir(path) == false)
1836 m_dout<<DTIME<<"ServerMap: Failed to create directory "
1837 <<"\""<<path<<"\""<<std::endl;
1838 throw BaseException("ServerMap failed to create directory");
1842 std::string ServerMap::getSectorSubDir(v2s16 pos)
1845 snprintf(cc, 9, "%.4x%.4x",
1846 (unsigned int)pos.X&0xffff,
1847 (unsigned int)pos.Y&0xffff);
1849 return std::string(cc);
1852 std::string ServerMap::getSectorDir(v2s16 pos)
1854 return m_savedir + "/sectors/" + getSectorSubDir(pos);
1857 v2s16 ServerMap::getSectorPos(std::string dirname)
1859 if(dirname.size() != 8)
1860 throw InvalidFilenameException("Invalid sector directory name");
1862 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
1864 throw InvalidFilenameException("Invalid sector directory name");
1865 v2s16 pos((s16)x, (s16)y);
1869 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
1871 v2s16 p2d = getSectorPos(sectordir);
1873 if(blockfile.size() != 4){
1874 throw InvalidFilenameException("Invalid block filename");
1877 int r = sscanf(blockfile.c_str(), "%4x", &y);
1879 throw InvalidFilenameException("Invalid block filename");
1880 return v3s16(p2d.X, y, p2d.Y);
1884 #define ENABLE_SECTOR_SAVING 1
1885 #define ENABLE_SECTOR_LOADING 1
1886 #define ENABLE_BLOCK_SAVING 1
1887 #define ENABLE_BLOCK_LOADING 1
1889 void ServerMap::save(bool only_changed)
1891 DSTACK(__FUNCTION_NAME);
1892 if(m_map_saving_enabled == false)
1894 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
1898 if(only_changed == false)
1899 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
1902 saveMasterHeightmap();
1904 u32 sector_meta_count = 0;
1905 u32 block_count = 0;
1908 JMutexAutoLock lock(m_sector_mutex);
1910 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1911 for(; i.atEnd() == false; i++)
1913 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
1914 assert(sector->getId() == MAPSECTOR_SERVER);
1916 if(ENABLE_SECTOR_SAVING)
1918 if(sector->differs_from_disk || only_changed == false)
1920 saveSectorMeta(sector);
1921 sector_meta_count++;
1924 if(ENABLE_BLOCK_SAVING)
1926 core::list<MapBlock*> blocks;
1927 sector->getBlocks(blocks);
1928 core::list<MapBlock*>::Iterator j;
1929 for(j=blocks.begin(); j!=blocks.end(); j++)
1931 MapBlock *block = *j;
1932 if(block->getChangedFlag() || only_changed == false)
1943 u32 deleted_count = 0;
1944 deleted_count = deleteUnusedSectors
1945 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
1948 Only print if something happened or saved whole map
1950 if(only_changed == false || sector_meta_count != 0
1951 || block_count != 0 || deleted_count != 0)
1953 dstream<<DTIME<<"ServerMap: Written: "
1954 <<sector_meta_count<<" sector metadata files, "
1955 <<block_count<<" block files, "
1956 <<deleted_count<<" sectors unloaded from memory."
1961 void ServerMap::loadAll()
1963 DSTACK(__FUNCTION_NAME);
1964 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
1966 loadMasterHeightmap();
1968 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
1970 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
1972 JMutexAutoLock lock(m_sector_mutex);
1975 s32 printed_counter = -100000;
1976 s32 count = list.size();
1978 std::vector<fs::DirListNode>::iterator i;
1979 for(i=list.begin(); i!=list.end(); i++)
1981 if(counter > printed_counter + 10)
1983 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
1984 printed_counter = counter;
1988 MapSector *sector = NULL;
1990 // We want directories
1994 sector = loadSectorMeta(i->name);
1996 catch(InvalidFilenameException &e)
1998 // This catches unknown crap in directory
2001 if(ENABLE_BLOCK_LOADING)
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++)
2012 loadBlock(i->name, i2->name, sector);
2014 catch(InvalidFilenameException &e)
2016 // This catches unknown crap in directory
2021 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2024 void ServerMap::saveMasterHeightmap()
2026 DSTACK(__FUNCTION_NAME);
2027 createDir(m_savedir);
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");
2034 // Format used for writing
2035 u8 version = SER_FMT_VER_HIGHEST;
2038 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2040 [0] u8 serialization version
2041 [1] X master heightmap
2043 u32 fullsize = 1 + hmdata.getSize();
2044 SharedBuffer<u8> data(fullsize);
2047 memcpy(&data[1], *hmdata, hmdata.getSize());
2049 o.write((const char*)*data, fullsize);
2052 m_heightmap->serialize(o, version);
2055 void ServerMap::loadMasterHeightmap()
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");
2063 if(m_heightmap != NULL)
2066 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2069 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2071 DSTACK(__FUNCTION_NAME);
2072 // Format used for writing
2073 u8 version = SER_FMT_VER_HIGHEST;
2075 v2s16 pos = sector->getPos();
2076 createDir(m_savedir);
2077 createDir(m_savedir+"/sectors");
2078 std::string dir = getSectorDir(pos);
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");
2086 sector->serialize(o, version);
2088 sector->differs_from_disk = false;
2091 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2093 DSTACK(__FUNCTION_NAME);
2095 v2s16 p2d = getSectorPos(dirname);
2096 std::string dir = m_savedir + "/sectors/" + dirname;
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");
2103 ServerMapSector *sector = ServerMapSector::deSerialize
2104 (is, this, p2d, &m_hwrapper, m_sectors);
2106 sector->differs_from_disk = false;
2111 bool ServerMap::loadSectorFull(v2s16 p2d)
2113 DSTACK(__FUNCTION_NAME);
2114 std::string sectorsubdir = getSectorSubDir(p2d);
2116 MapSector *sector = NULL;
2118 JMutexAutoLock lock(m_sector_mutex);
2121 sector = loadSectorMeta(sectorsubdir);
2123 catch(InvalidFilenameException &e)
2127 catch(FileNotGoodException &e)
2131 catch(std::exception &e)
2136 if(ENABLE_BLOCK_LOADING)
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++)
2147 loadBlock(sectorsubdir, i2->name, sector);
2149 catch(InvalidFilenameException &e)
2151 // This catches unknown crap in directory
2159 bool ServerMap::deFlushSector(v2s16 p2d)
2161 DSTACK(__FUNCTION_NAME);
2162 // See if it already exists in memory
2164 MapSector *sector = getSectorNoGenerate(p2d);
2167 catch(InvalidPositionException &e)
2170 Try to load the sector from disk.
2172 if(loadSectorFull(p2d) == true)
2181 void ServerMap::saveBlock(MapBlock *block)
2183 DSTACK(__FUNCTION_NAME);
2185 Dummy blocks are not written
2187 if(block->isDummy())
2189 /*v3s16 p = block->getPos();
2190 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2191 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2195 // Format used for writing
2196 u8 version = SER_FMT_VER_HIGHEST;
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);
2205 // Block file is map/sectors/xxxxxxxx/xxxx
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");
2214 [0] u8 serialization version
2217 o.write((char*)&version, 1);
2219 block->serialize(o, version);
2222 Versions up from 9 have block objects.
2226 block->serializeObjects(o, version);
2229 // We just wrote it to the disk
2230 block->resetChangedFlag();
2233 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
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");
2242 v3s16 p3d = getBlockPos(sectordir, blockfile);
2243 v2s16 p2d(p3d.X, p3d.Z);
2245 assert(sector->getPos() == p2d);
2247 u8 version = SER_FMT_VER_INVALID;
2248 is.read((char*)&version, 1);
2250 /*u32 block_size = MapBlock::serializedLength(version);
2251 SharedBuffer<u8> data(block_size);
2252 is.read((char*)*data, block_size);*/
2254 // This will always return a sector because we're the server
2255 //MapSector *sector = emergeSector(p2d);
2257 MapBlock *block = NULL;
2258 bool created_new = false;
2260 block = sector->getBlockNoCreate(p3d.Y);
2262 catch(InvalidPositionException &e)
2264 block = sector->createBlankBlockNoInsert(p3d.Y);
2268 block->deSerialize(is, version);
2271 Versions up from 9 have block objects.
2275 block->updateObjects(is, version, NULL);
2279 sector->insertBlock(block);
2282 Convert old formats to new and save
2285 if(version == 0 || version == 1)
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;
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);
2300 // Save modified blocks
2301 core::map<v3s16, MapBlock * >::Iterator i = modified_blocks.getIterator();
2302 for(; i.atEnd() == false; i++)
2304 MapBlock *b2 = i.getNode()->getValue();
2308 // Save blocks in new format
2309 else if(version < SER_FMT_VER_HIGHEST)
2314 // We just loaded it from the disk, so it's up-to-date.
2315 block->resetChangedFlag();
2318 // Gets from master heightmap
2319 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2321 assert(m_heightmap != NULL);
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);
2339 void ServerMap::PrintInfo(std::ostream &out)
2348 ClientMap::ClientMap(
2350 video::SMaterial *materials,
2351 scene::ISceneNode* parent,
2352 scene::ISceneManager* mgr,
2356 scene::ISceneNode(parent, mgr, id),
2358 m_materials(materials),
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);
2373 ClientMap::~ClientMap()
2375 JMutexAutoLock lock(mesh_mutex);
2384 MapSector * ClientMap::emergeSector(v2s16 p2d)
2386 DSTACK(__FUNCTION_NAME);
2387 // Check that it doesn't exist already
2389 return getSectorNoGenerate(p2d);
2391 catch(InvalidPositionException &e)
2395 // Create a sector with no heightmaps
2396 ClientMapSector *sector = new ClientMapSector(this, p2d);
2399 JMutexAutoLock lock(m_sector_mutex);
2400 m_sectors.insert(p2d, sector);
2406 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2408 DSTACK(__FUNCTION_NAME);
2409 ClientMapSector *sector = NULL;
2411 JMutexAutoLock lock(m_sector_mutex);
2413 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2417 sector = (ClientMapSector*)n->getValue();
2418 assert(sector->getId() == MAPSECTOR_CLIENT);
2422 sector = new ClientMapSector(this, p2d);
2424 JMutexAutoLock lock(m_sector_mutex);
2425 m_sectors.insert(p2d, sector);
2429 sector->deSerialize(is);
2432 void ClientMap::renderMap(video::IVideoDriver* driver,
2433 video::SMaterial *materials, s32 pass)
2435 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2436 DSTACK(__FUNCTION_NAME);
2438 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2441 Draw master heightmap mesh
2445 JMutexAutoLock lock(mesh_mutex);
2448 u32 c = mesh->getMeshBufferCount();
2450 for(u32 i=0; i<c; i++)
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)
2460 driver->setMaterial(buf->getMaterial());
2461 driver->drawMeshBuffer(buf);
2469 Get time for measuring timeout.
2471 Measuring time is very useful for long delays when the
2472 machine is swapping a lot.
2474 int time1 = time(0);
2477 Collect all blocks that are in the view range
2479 Should not optimize more here as we want to auto-update
2480 all changed nodes in viewing range at the next step.
2483 s16 viewing_range_nodes;
2484 bool viewing_range_all;
2486 JMutexAutoLock lock(g_range_mutex);
2487 viewing_range_nodes = g_viewing_range_nodes;
2488 viewing_range_all = g_viewing_range_all;
2491 m_camera_mutex.Lock();
2492 v3f camera_position = m_camera_position;
2493 v3f camera_direction = m_camera_direction;
2494 m_camera_mutex.Unlock();
2497 Get all blocks and draw all visible ones
2500 v3s16 cam_pos_nodes(
2501 camera_position.X / BS,
2502 camera_position.Y / BS,
2503 camera_position.Z / BS);
2505 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2507 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2508 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2510 // Take a fair amount as we will be dropping more out later
2512 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2513 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2514 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2516 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2517 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2518 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2520 u32 vertex_count = 0;
2522 core::map<v2s16, MapSector*>::Iterator si;
2524 //NOTE: The sectors map should be locked but we're not doing it
2525 // because it'd cause too much delays
2527 si = m_sectors.getIterator();
2528 for(; si.atEnd() == false; si++)
2531 static int timecheck_counter = 0;
2532 timecheck_counter++;
2533 if(timecheck_counter > 50)
2535 int time2 = time(0);
2536 if(time2 > time1 + 4)
2538 dstream<<"ClientMap::renderMap(): "
2539 "Rendering takes ages, returning."
2546 MapSector *sector = si.getNode()->getValue();
2547 v2s16 sp = sector->getPos();
2549 if(viewing_range_all == false)
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)
2558 core::list< MapBlock * > sectorblocks;
2559 sector->getBlocks(sectorblocks);
2565 core::list< MapBlock * >::Iterator i;
2566 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2568 MapBlock *block = *i;
2571 Compare block position to camera position, skip
2572 if not seen on display
2575 v3s16 blockpos_nodes = block->getPosRelative();
2577 // Block center position
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
2584 // Block position relative to camera
2585 v3f blockpos_relative = blockpos - camera_position;
2587 // Distance in camera direction (+=front, -=back)
2588 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2591 f32 d = blockpos_relative.getLength();
2593 if(viewing_range_all == false)
2595 // If block is far away, don't draw it
2596 if(d > viewing_range_nodes * BS)
2600 // Maximum radius of a block
2601 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
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)
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;
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;
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.))
2623 Draw the faces of the block
2627 JMutexAutoLock lock(block->mesh_mutex);
2629 // Cancel if block has no mesh
2630 if(block->mesh == NULL)
2633 u32 c = block->mesh->getMeshBufferCount();
2635 for(u32 i=0; i<c; i++)
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)
2645 driver->setMaterial(buf->getMaterial());
2646 driver->drawMeshBuffer(buf);
2647 vertex_count += buf->getVertexCount();
2651 } // foreach sectorblocks
2654 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2655 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2658 void ClientMap::updateMesh()
2661 DSTACK(__FUNCTION_NAME);
2664 Check what sectors don't draw anything useful at ground level
2665 and create a mesh of the rough heightmap at those positions.
2668 m_camera_mutex.Lock();
2669 v3f camera_position = m_camera_position;
2670 v3f camera_direction = m_camera_direction;
2671 m_camera_mutex.Unlock();
2673 v3s16 cam_pos_nodes(
2674 camera_position.X / BS,
2675 camera_position.Y / BS,
2676 camera_position.Z / BS);
2678 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2680 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2681 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2683 // Take a fair amount as we will be dropping more out later
2685 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2686 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2687 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2689 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2690 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2691 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2697 scene::SMesh *mesh_new = new scene::SMesh();
2698 //scene::IMeshBuffer *buf = NULL;
2699 scene::SMeshBuffer *buf = NULL;
2701 u8 material_in_use = 0;
2704 Loop through sectors
2707 for(core::map<v2s16, MapSector*>::Iterator
2708 si = m_sectors.getIterator();
2709 si.atEnd() == false; si++)
2711 MapSector *sector = si.getNode()->getValue();
2713 if(sector->getId() != MAPSECTOR_CLIENT)
2715 dstream<<"WARNING: Client has a non-client sector"
2720 ClientMapSector *cs = (ClientMapSector*)sector;
2722 v2s16 sp = sector->getPos();
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)
2731 Get some ground level info
2743 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
2745 s16 cn_max = -32768;
2746 for(s16 i=0; i<4; i++)
2753 s16 cn_slope = cn_max - cn_min;
2756 Generate this part of the heightmap mesh
2760 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
2762 else if(cn_slope <= MAP_BLOCKSIZE)
2767 if(material != material_in_use || buf == NULL)
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
2775 // This is a "Standard MeshBuffer",
2776 // it's a typedeffed CMeshBuffer<video::S3DVertex>
2777 buf = new scene::SMeshBuffer();
2780 buf->Material = g_mesh_materials[material];
2782 //buf->setHardwareMappingHint(scene::EHM_STATIC);
2784 mesh_new->addMeshBuffer(buf);
2788 material_in_use = material;
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;
2796 //video::SColor c(255,255,255,255);
2798 video::SColor c(255,cc,cc,cc);
2800 video::S3DVertex vertices[4] =
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),
2807 u16 indices[] = {0,1,2,2,3,0};
2809 buf->append(vertices, 4, indices, 6);
2813 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
2821 scene::SMesh *mesh_old = mesh;
2828 mesh_mutex.Unlock();
2830 if(mesh_old != NULL)
2832 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
2834 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
2835 (g_materials[MATERIAL_GRASS]);
2837 dstream<<"grass buf refcount="<<buf->getReferenceCount()
2844 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
2849 void ClientMap::PrintInfo(std::ostream &out)