2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
8 #include "jmutexautolock.h"
16 #define sleep_ms(x) Sleep(x)
19 #define sleep_ms(x) usleep(x*1000)
22 MapBlockPointerCache::MapBlockPointerCache(Map *map)
25 m_map->m_blockcachelock.cacheCreated();
27 m_from_cache_count = 0;
31 MapBlockPointerCache::~MapBlockPointerCache()
33 m_map->m_blockcachelock.cacheRemoved();
35 dstream<<"MapBlockPointerCache:"
36 <<" from_cache_count="<<m_from_cache_count
37 <<" from_map_count="<<m_from_map_count
41 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
43 core::map<v3s16, MapBlock*>::Node *n = NULL;
53 // Throws InvalidPositionException if not found
54 MapBlock *b = m_map->getBlockNoCreate(p);
63 Map::Map(std::ostream &dout):
65 m_camera_position(0,0,0),
66 m_camera_direction(0,0,1),
71 m_sector_mutex.Init();
72 m_camera_mutex.Init();
73 assert(m_sector_mutex.IsInitialized());
74 assert(m_camera_mutex.IsInitialized());
76 // Get this so that the player can stay on it at first
77 //getSector(v2s16(0,0));
85 /*updater.setRun(false);
86 while(updater.IsRunning())
92 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
93 for(; i.atEnd() == false; i++)
95 MapSector *sector = i.getNode()->getValue();
100 /*bool Map::sectorExists(v2s16 p)
102 JMutexAutoLock lock(m_sector_mutex);
103 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
107 MapSector * Map::getSectorNoGenerate(v2s16 p)
109 JMutexAutoLock lock(m_sector_mutex);
111 if(m_sector_cache != NULL && p == m_sector_cache_p){
112 MapSector * sector = m_sector_cache;
113 // Reset inactivity timer
114 sector->usage_timer = 0.0;
118 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
119 // If sector doesn't exist, throw an exception
122 throw InvalidPositionException();
125 MapSector *sector = n->getValue();
127 // Cache the last result
128 m_sector_cache_p = p;
129 m_sector_cache = sector;
131 //MapSector * ref(sector);
133 // Reset inactivity timer
134 sector->usage_timer = 0.0;
138 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
140 v2s16 p2d(p3d.X, p3d.Z);
141 MapSector * sector = getSectorNoGenerate(p2d);
143 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
148 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
150 dstream<<"Map::getBlock() with generate=true called"
152 v2s16 p2d(p3d.X, p3d.Z);
153 //MapSector * sector = getSector(p2d, generate);
154 MapSector * sector = getSectorNoGenerate(p2d);
157 throw InvalidPositionException();
159 return sector->getBlockNoCreate(p3d.Y);
162 f32 Map::getGroundHeight(v2s16 p, bool generate)
165 v2s16 sectorpos = getNodeSectorPos(p);
166 MapSector * sref = getSectorNoGenerate(sectorpos);
167 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
168 f32 y = sref->getGroundHeight(relpos);
171 catch(InvalidPositionException &e)
173 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
177 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
179 /*m_dout<<DTIME<<"Map::setGroundHeight(("
181 <<"), "<<y<<")"<<std::endl;*/
182 v2s16 sectorpos = getNodeSectorPos(p);
183 MapSector * sref = getSectorNoGenerate(sectorpos);
184 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
185 //sref->mutex.Lock();
186 sref->setGroundHeight(relpos, y);
187 //sref->mutex.Unlock();
190 bool Map::isNodeUnderground(v3s16 p)
192 v3s16 blockpos = getNodeBlockPos(p);
194 MapBlock * block = getBlockNoCreate(blockpos);
195 return block->getIsUnderground();
197 catch(InvalidPositionException &e)
204 void Map::interpolate(v3s16 block,
205 core::map<v3s16, MapBlock*> & modified_blocks)
207 const v3s16 dirs[6] = {
208 v3s16(0,0,1), // back
210 v3s16(1,0,0), // right
211 v3s16(0,0,-1), // front
212 v3s16(0,-1,0), // bottom
213 v3s16(-1,0,0), // left
216 if(from_nodes.size() == 0)
219 u32 blockchangecount = 0;
221 core::map<v3s16, bool> lighted_nodes;
222 core::map<v3s16, bool>::Iterator j;
223 j = from_nodes.getIterator();
226 Initialize block cache
229 MapBlock *block = NULL;
230 // Cache this a bit, too
231 bool block_checked_in_modified = false;
233 for(; j.atEnd() == false; j++)
234 //for(; j != from_nodes.end(); j++)
236 v3s16 pos = j.getNode()->getKey();
238 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
239 v3s16 blockpos = getNodeBlockPos(pos);
241 // Only fetch a new block if the block position has changed
243 if(block == NULL || blockpos != blockpos_last){
244 block = getBlockNoCreate(blockpos);
245 blockpos_last = blockpos;
247 block_checked_in_modified = false;
251 catch(InvalidPositionException &e)
259 // Calculate relative position in block
260 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
262 // Get node straight from the block
263 MapNode n = block->getNode(relpos);
265 u8 oldlight = n.getLight();
266 u8 newlight = diminish_light(oldlight);
268 // Loop through 6 neighbors
269 for(u16 i=0; i<6; i++){
270 // Get the position of the neighbor node
271 v3s16 n2pos = pos + dirs[i];
273 // Get the block where the node is located
274 v3s16 blockpos = getNodeBlockPos(n2pos);
278 // Only fetch a new block if the block position has changed
280 if(block == NULL || blockpos != blockpos_last){
281 block = getBlockNoCreate(blockpos);
282 blockpos_last = blockpos;
284 block_checked_in_modified = false;
288 catch(InvalidPositionException &e)
293 // Calculate relative position in block
294 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
295 // Get node straight from the block
296 MapNode n2 = block->getNode(relpos);
298 bool changed = false;
300 If the neighbor is brighter than the current node,
301 add to list (it will light up this node on its turn)
303 if(n2.getLight() > undiminish_light(oldlight))
305 lighted_nodes.insert(n2pos, true);
306 //lighted_nodes.push_back(n2pos);
310 If the neighbor is dimmer than how much light this node
311 would spread on it, add to list
313 if(n2.getLight() < newlight)
315 if(n2.light_propagates())
317 n2.setLight(newlight);
318 block->setNode(relpos, n2);
319 lighted_nodes.insert(n2pos, true);
320 //lighted_nodes.push_back(n2pos);
325 // Add to modified_blocks
326 if(changed == true && block_checked_in_modified == false)
328 // If the block is not found in modified_blocks, add.
329 if(modified_blocks.find(blockpos) == NULL)
331 modified_blocks.insert(blockpos, block);
333 block_checked_in_modified = true;
336 catch(InvalidPositionException &e)
343 /*dstream<<"spreadLight(): Changed block "
344 <<blockchangecount<<" times"
345 <<" for "<<from_nodes.size()<<" nodes"
348 if(lighted_nodes.size() > 0)
349 spreadLight(lighted_nodes, modified_blocks);
354 Goes recursively through the neighbours of the node.
356 Alters only transparent nodes.
358 If the lighting of the neighbour is lower than the lighting of
359 the node was (before changing it to 0 at the step before), the
360 lighting of the neighbour is set to 0 and then the same stuff
361 repeats for the neighbour.
363 The ending nodes of the routine are stored in light_sources.
364 This is useful when a light is removed. In such case, this
365 routine can be called for the light node and then again for
366 light_sources to re-light the area without the removed light.
368 values of from_nodes are lighting values.
370 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
371 core::map<v3s16, bool> & light_sources,
372 core::map<v3s16, MapBlock*> & modified_blocks)
375 v3s16(0,0,1), // back
377 v3s16(1,0,0), // right
378 v3s16(0,0,-1), // front
379 v3s16(0,-1,0), // bottom
380 v3s16(-1,0,0), // left
383 if(from_nodes.size() == 0)
386 u32 blockchangecount = 0;
388 core::map<v3s16, u8> unlighted_nodes;
389 core::map<v3s16, u8>::Iterator j;
390 j = from_nodes.getIterator();
393 Initialize block cache
396 MapBlock *block = NULL;
397 // Cache this a bit, too
398 bool block_checked_in_modified = false;
400 for(; j.atEnd() == false; j++)
402 v3s16 pos = j.getNode()->getKey();
403 v3s16 blockpos = getNodeBlockPos(pos);
405 // Only fetch a new block if the block position has changed
407 if(block == NULL || blockpos != blockpos_last){
408 block = getBlockNoCreate(blockpos);
409 blockpos_last = blockpos;
411 block_checked_in_modified = false;
415 catch(InvalidPositionException &e)
423 // Calculate relative position in block
424 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
426 // Get node straight from the block
427 MapNode n = block->getNode(relpos);
429 u8 oldlight = j.getNode()->getValue();
431 // Loop through 6 neighbors
432 for(u16 i=0; i<6; i++)
434 // Get the position of the neighbor node
435 v3s16 n2pos = pos + dirs[i];
437 // Get the block where the node is located
438 v3s16 blockpos = getNodeBlockPos(n2pos);
442 // Only fetch a new block if the block position has changed
444 if(block == NULL || blockpos != blockpos_last){
445 block = getBlockNoCreate(blockpos);
446 blockpos_last = blockpos;
448 block_checked_in_modified = false;
452 catch(InvalidPositionException &e)
457 // Calculate relative position in block
458 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
459 // Get node straight from the block
460 MapNode n2 = block->getNode(relpos);
462 bool changed = false;
464 //TODO: Optimize output by optimizing light_sources?
467 If the neighbor is dimmer than what was specified
468 as oldlight (the light of the previous node)
470 if(n2.getLight() < oldlight)
473 And the neighbor is transparent and it has some light
475 if(n2.light_propagates() && n2.getLight() != 0)
478 Set light to 0 and add to queue
481 u8 current_light = n2.getLight();
483 block->setNode(relpos, n2);
485 unlighted_nodes.insert(n2pos, current_light);
489 Remove from light_sources if it is there
490 NOTE: This doesn't happen nearly at all
492 /*if(light_sources.find(n2pos))
494 std::cout<<"Removed from light_sources"<<std::endl;
495 light_sources.remove(n2pos);
500 light_sources.insert(n2pos, true);
503 // Add to modified_blocks
504 if(changed == true && block_checked_in_modified == false)
506 // If the block is not found in modified_blocks, add.
507 if(modified_blocks.find(blockpos) == NULL)
509 modified_blocks.insert(blockpos, block);
511 block_checked_in_modified = true;
514 catch(InvalidPositionException &e)
521 /*dstream<<"unspreadLight(): Changed block "
522 <<blockchangecount<<" times"
523 <<" for "<<from_nodes.size()<<" nodes"
526 if(unlighted_nodes.size() > 0)
527 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
531 A single-node wrapper of the above
533 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
534 core::map<v3s16, bool> & light_sources,
535 core::map<v3s16, MapBlock*> & modified_blocks)
537 core::map<v3s16, u8> from_nodes;
538 from_nodes.insert(pos, lightwas);
540 unspreadLight(from_nodes, light_sources, modified_blocks);
544 Lights neighbors of from_nodes, collects all them and then
547 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
548 core::map<v3s16, MapBlock*> & modified_blocks)
550 const v3s16 dirs[6] = {
551 v3s16(0,0,1), // back
553 v3s16(1,0,0), // right
554 v3s16(0,0,-1), // front
555 v3s16(0,-1,0), // bottom
556 v3s16(-1,0,0), // left
559 if(from_nodes.size() == 0)
562 u32 blockchangecount = 0;
564 core::map<v3s16, bool> lighted_nodes;
565 core::map<v3s16, bool>::Iterator j;
566 j = from_nodes.getIterator();
569 Initialize block cache
572 MapBlock *block = NULL;
573 // Cache this a bit, too
574 bool block_checked_in_modified = false;
576 for(; j.atEnd() == false; j++)
577 //for(; j != from_nodes.end(); j++)
579 v3s16 pos = j.getNode()->getKey();
581 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
582 v3s16 blockpos = getNodeBlockPos(pos);
584 // Only fetch a new block if the block position has changed
586 if(block == NULL || blockpos != blockpos_last){
587 block = getBlockNoCreate(blockpos);
588 blockpos_last = blockpos;
590 block_checked_in_modified = false;
594 catch(InvalidPositionException &e)
602 // Calculate relative position in block
603 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
605 // Get node straight from the block
606 MapNode n = block->getNode(relpos);
608 u8 oldlight = n.getLight();
609 u8 newlight = diminish_light(oldlight);
611 // Loop through 6 neighbors
612 for(u16 i=0; i<6; i++){
613 // Get the position of the neighbor node
614 v3s16 n2pos = pos + dirs[i];
616 // Get the block where the node is located
617 v3s16 blockpos = getNodeBlockPos(n2pos);
621 // Only fetch a new block if the block position has changed
623 if(block == NULL || blockpos != blockpos_last){
624 block = getBlockNoCreate(blockpos);
625 blockpos_last = blockpos;
627 block_checked_in_modified = false;
631 catch(InvalidPositionException &e)
636 // Calculate relative position in block
637 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
638 // Get node straight from the block
639 MapNode n2 = block->getNode(relpos);
641 bool changed = false;
643 If the neighbor is brighter than the current node,
644 add to list (it will light up this node on its turn)
646 if(n2.getLight() > undiminish_light(oldlight))
648 lighted_nodes.insert(n2pos, true);
649 //lighted_nodes.push_back(n2pos);
653 If the neighbor is dimmer than how much light this node
654 would spread on it, add to list
656 if(n2.getLight() < newlight)
658 if(n2.light_propagates())
660 n2.setLight(newlight);
661 block->setNode(relpos, n2);
662 lighted_nodes.insert(n2pos, true);
663 //lighted_nodes.push_back(n2pos);
668 // Add to modified_blocks
669 if(changed == true && block_checked_in_modified == false)
671 // If the block is not found in modified_blocks, add.
672 if(modified_blocks.find(blockpos) == NULL)
674 modified_blocks.insert(blockpos, block);
676 block_checked_in_modified = true;
679 catch(InvalidPositionException &e)
686 /*dstream<<"spreadLight(): Changed block "
687 <<blockchangecount<<" times"
688 <<" for "<<from_nodes.size()<<" nodes"
691 if(lighted_nodes.size() > 0)
692 spreadLight(lighted_nodes, modified_blocks);
696 A single-node source variation of the above.
698 void Map::lightNeighbors(v3s16 pos,
699 core::map<v3s16, MapBlock*> & modified_blocks)
701 core::map<v3s16, bool> from_nodes;
702 from_nodes.insert(pos, true);
703 spreadLight(from_nodes, modified_blocks);
706 v3s16 Map::getBrightestNeighbour(v3s16 p)
709 v3s16(0,0,1), // back
711 v3s16(1,0,0), // right
712 v3s16(0,0,-1), // front
713 v3s16(0,-1,0), // bottom
714 v3s16(-1,0,0), // left
717 u8 brightest_light = 0;
718 v3s16 brightest_pos(0,0,0);
719 bool found_something = false;
721 // Loop through 6 neighbors
722 for(u16 i=0; i<6; i++){
723 // Get the position of the neighbor node
724 v3s16 n2pos = p + dirs[i];
729 catch(InvalidPositionException &e)
733 if(n2.getLight() > brightest_light || found_something == false){
734 brightest_light = n2.getLight();
735 brightest_pos = n2pos;
736 found_something = true;
740 if(found_something == false)
741 throw InvalidPositionException();
743 return brightest_pos;
747 Propagates sunlight down from a node.
748 Starting point gets sunlight.
750 Returns the lowest y value of where the sunlight went.
752 s16 Map::propagateSunlight(v3s16 start,
753 core::map<v3s16, MapBlock*> & modified_blocks)
758 v3s16 pos(start.X, y, start.Z);
760 v3s16 blockpos = getNodeBlockPos(pos);
763 block = getBlockNoCreate(blockpos);
765 catch(InvalidPositionException &e)
770 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
771 MapNode n = block->getNode(relpos);
773 if(n.sunlight_propagates())
775 n.setLight(LIGHT_SUN);
776 block->setNode(relpos, n);
778 modified_blocks.insert(blockpos, block);
787 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
788 core::map<v3s16, MapBlock*> & modified_blocks)
790 /*m_dout<<DTIME<<"Map::updateLighting(): "
791 <<a_blocks.getSize()<<" blocks... ";*/
795 u32 count_was = modified_blocks.size();
797 /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
798 for(; i != a_blocks.end(); i++)
800 MapBlock *block = *i;*/
802 core::map<v3s16, bool> light_sources;
804 core::map<v3s16, u8> unlight_from;
806 core::map<v3s16, MapBlock*>::Iterator i;
807 i = a_blocks.getIterator();
808 for(; i.atEnd() == false; i++)
810 MapBlock *block = i.getNode()->getValue();
814 // Don't bother with dummy blocks.
818 v3s16 pos = block->getPos();
819 modified_blocks.insert(pos, block);
822 Clear all light from block
824 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
825 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
826 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
831 MapNode n = block->getNode(v3s16(x,y,z));
832 u8 oldlight = n.getLight();
834 block->setNode(v3s16(x,y,z), n);
836 // Collect borders for unlighting
837 if(x==0 || x == MAP_BLOCKSIZE-1
838 || y==0 || y == MAP_BLOCKSIZE-1
839 || z==0 || z == MAP_BLOCKSIZE-1)
841 v3s16 p_map = p + v3s16(
844 MAP_BLOCKSIZE*pos.Z);
845 unlight_from.insert(p_map, oldlight);
848 catch(InvalidPositionException &e)
851 This would happen when dealing with a
855 dstream<<"updateLighting(): InvalidPositionException"
860 bool bottom_valid = block->propagateSunlight(light_sources);
862 // If bottom is valid, we're done.
866 /*dstream<<"Bottom for sunlight-propagated block ("
867 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
870 // Else get the block below and loop to it
874 block = getBlockNoCreate(pos);
876 catch(InvalidPositionException &e)
885 //TimeTaker timer("unspreadLight", g_device);
886 unspreadLight(unlight_from, light_sources, modified_blocks);
891 u32 diff = modified_blocks.size() - count_was;
892 count_was = modified_blocks.size();
893 dstream<<"unspreadLight modified "<<diff<<std::endl;
896 // TODO: Spread light from propagated sunlight?
897 // Yes, add it to light_sources... somehow.
898 // It has to be added at somewhere above, in the loop.
902 //TimeTaker timer("spreadLight", g_device);
903 spreadLight(light_sources, modified_blocks);
908 u32 diff = modified_blocks.size() - count_was;
909 count_was = modified_blocks.size();
910 dstream<<"spreadLight modified "<<diff<<std::endl;
913 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
917 This is called after changing a node from transparent to opaque.
918 The lighting value of the node should be left as-is after changing
919 other values. This sets the lighting value to 0.
921 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
922 core::map<v3s16, MapBlock*> &modified_blocks)*/
923 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
924 core::map<v3s16, MapBlock*> &modified_blocks)
927 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
928 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
930 u8 lightwas = getNode(p).getLight();
932 //core::list<v3s16> light_sources;
933 core::map<v3s16, bool> light_sources;
934 //MapNode n = getNode(p);
937 From this node to nodes underneath:
938 If lighting is sunlight (1.0), unlight neighbours and
943 bool node_under_sunlight = true;
945 v3s16 toppos = p + v3s16(0,1,0);
948 If there is a node at top and it doesn't have sunlight,
949 there has not been any sunlight going down.
951 Otherwise there probably is.
954 MapNode topnode = getNode(toppos);
956 if(topnode.getLight() != LIGHT_SUN)
957 node_under_sunlight = false;
959 catch(InvalidPositionException &e)
963 // Add the block of the added node to modified_blocks
964 v3s16 blockpos = getNodeBlockPos(p);
965 MapBlock * block = getBlockNoCreate(blockpos);
966 assert(block != NULL);
967 modified_blocks.insert(blockpos, block);
969 if(isValidPosition(p) == false)
972 // Unlight neighbours of node.
973 // This means setting light of all consequent dimmer nodes
975 // This also collects the nodes at the border which will spread
976 // light again into this.
977 unLightNeighbors(p, lightwas, light_sources, modified_blocks);
983 If node is under sunlight, take all sunlighted nodes under
984 it and clear light from them and from where the light has
987 if(node_under_sunlight)
991 //m_dout<<DTIME<<"y="<<y<<std::endl;
992 v3s16 n2pos(p.X, y, p.Z);
998 catch(InvalidPositionException &e)
1003 if(n2.getLight() == LIGHT_SUN)
1005 //m_dout<<DTIME<<"doing"<<std::endl;
1006 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
1016 Spread light from all nodes that might be capable of doing so
1017 TODO: Convert to spreadLight
1019 spreadLight(light_sources, modified_blocks);
1024 void Map::removeNodeAndUpdate(v3s16 p,
1025 core::map<v3s16, MapBlock*> &modified_blocks)
1027 /*PrintInfo(m_dout);
1028 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1029 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1031 bool node_under_sunlight = true;
1033 v3s16 toppos = p + v3s16(0,1,0);
1036 If there is a node at top and it doesn't have sunlight,
1037 there will be no sunlight going down.
1040 MapNode topnode = getNode(toppos);
1042 if(topnode.getLight() != LIGHT_SUN)
1043 node_under_sunlight = false;
1045 catch(InvalidPositionException &e)
1050 Unlight neighbors (in case the node is a light source)
1052 //core::list<v3s16> light_sources;
1053 core::map<v3s16, bool> light_sources;
1054 unLightNeighbors(p, getNode(p).getLight(),
1055 light_sources, modified_blocks);
1066 Recalculate lighting
1068 spreadLight(light_sources, modified_blocks);
1070 // Add the block of the removed node to modified_blocks
1071 v3s16 blockpos = getNodeBlockPos(p);
1072 MapBlock * block = getBlockNoCreate(blockpos);
1073 assert(block != NULL);
1074 modified_blocks.insert(blockpos, block);
1077 If the removed node was under sunlight, propagate the
1078 sunlight down from it and then light all neighbors
1079 of the propagated blocks.
1081 if(node_under_sunlight)
1083 s16 ybottom = propagateSunlight(p, modified_blocks);
1084 /*m_dout<<DTIME<<"Node was under sunlight. "
1085 "Propagating sunlight";
1086 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1088 for(; y >= ybottom; y--)
1090 v3s16 p2(p.X, y, p.Z);
1091 /*m_dout<<DTIME<<"lighting neighbors of node ("
1092 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1094 lightNeighbors(p2, modified_blocks);
1099 // Set the lighting of this node to 0
1101 MapNode n = getNode(p);
1105 catch(InvalidPositionException &e)
1111 // Get the brightest neighbour node and propagate light from it
1112 v3s16 n2p = getBrightestNeighbour(p);
1114 MapNode n2 = getNode(n2p);
1115 lightNeighbors(n2p, modified_blocks);
1117 catch(InvalidPositionException &e)
1122 void Map::updateMeshes(v3s16 blockpos)
1124 assert(mapType() == MAPTYPE_CLIENT);
1127 v3s16 p = blockpos + v3s16(0,0,0);
1128 MapBlock *b = getBlockNoCreate(p);
1131 catch(InvalidPositionException &e){}
1133 v3s16 p = blockpos + v3s16(-1,0,0);
1134 MapBlock *b = getBlockNoCreate(p);
1137 catch(InvalidPositionException &e){}
1139 v3s16 p = blockpos + v3s16(0,-1,0);
1140 MapBlock *b = getBlockNoCreate(p);
1143 catch(InvalidPositionException &e){}
1145 v3s16 p = blockpos + v3s16(0,0,-1);
1146 MapBlock *b = getBlockNoCreate(p);
1149 catch(InvalidPositionException &e){}
1153 Updates usage timers
1155 void Map::timerUpdate(float dtime)
1157 JMutexAutoLock lock(m_sector_mutex);
1159 core::map<v2s16, MapSector*>::Iterator si;
1161 si = m_sectors.getIterator();
1162 for(; si.atEnd() == false; si++)
1164 MapSector *sector = si.getNode()->getValue();
1165 sector->usage_timer += dtime;
1169 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1172 Wait for caches to be removed before continuing.
1174 This disables the existence of caches while locked
1176 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1178 core::list<v2s16>::Iterator j;
1179 for(j=list.begin(); j!=list.end(); j++)
1181 MapSector *sector = m_sectors[*j];
1184 sector->deleteBlocks();
1189 If sector is in sector cache, remove it from there
1191 if(m_sector_cache == sector)
1193 m_sector_cache = NULL;
1196 Remove from map and delete
1198 m_sectors.remove(*j);
1204 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1205 core::list<v3s16> *deleted_blocks)
1207 JMutexAutoLock lock(m_sector_mutex);
1209 core::list<v2s16> sector_deletion_queue;
1210 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1211 for(; i.atEnd() == false; i++)
1213 MapSector *sector = i.getNode()->getValue();
1215 Delete sector from memory if it hasn't been used in a long time
1217 if(sector->usage_timer > timeout)
1219 sector_deletion_queue.push_back(i.getNode()->getKey());
1221 if(deleted_blocks != NULL)
1223 // Collect positions of blocks of sector
1224 MapSector *sector = i.getNode()->getValue();
1225 core::list<MapBlock*> blocks;
1226 sector->getBlocks(blocks);
1227 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1228 i != blocks.end(); i++)
1230 deleted_blocks->push_back((*i)->getPos());
1235 deleteSectors(sector_deletion_queue, only_blocks);
1236 return sector_deletion_queue.getSize();
1239 void Map::PrintInfo(std::ostream &out)
1248 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1252 m_savedir = savedir;
1253 m_map_saving_enabled = false;
1257 // If directory exists, check contents and load if possible
1258 if(fs::PathExists(m_savedir))
1260 // If directory is empty, it is safe to save into it.
1261 if(fs::GetDirListing(m_savedir).size() == 0)
1263 dstream<<DTIME<<"Server: Empty save directory is valid."
1265 m_map_saving_enabled = true;
1269 // Load master heightmap
1270 loadMasterHeightmap();
1272 // Load sector (0,0) and throw and exception on fail
1273 if(loadSectorFull(v2s16(0,0)) == false)
1274 throw LoadError("Failed to load sector (0,0)");
1276 dstream<<DTIME<<"Server: Successfully loaded master "
1277 "heightmap and sector (0,0) from "<<savedir<<
1278 ", assuming valid save directory."
1281 m_map_saving_enabled = true;
1282 // Map loaded, not creating new one
1286 // If directory doesn't exist, it is safe to save to it
1288 m_map_saving_enabled = true;
1291 catch(std::exception &e)
1293 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1294 <<", exception: "<<e.what()<<std::endl;
1295 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1296 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1299 dstream<<DTIME<<"Initializing new map."<<std::endl;
1301 // Create master heightmap
1302 ValueGenerator *maxgen =
1303 ValueGenerator::deSerialize(hmp.randmax);
1304 ValueGenerator *factorgen =
1305 ValueGenerator::deSerialize(hmp.randfactor);
1306 ValueGenerator *basegen =
1307 ValueGenerator::deSerialize(hmp.base);
1308 m_heightmap = new UnlimitedHeightmap
1309 (hmp.blocksize, maxgen, factorgen, basegen);
1311 // Set map parameters
1314 // Create zero sector
1315 emergeSector(v2s16(0,0));
1317 // Initially write whole map
1321 ServerMap::~ServerMap()
1325 if(m_map_saving_enabled)
1328 // Save only changed parts
1330 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1334 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1337 catch(std::exception &e)
1339 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1340 <<", exception: "<<e.what()<<std::endl;
1343 if(m_heightmap != NULL)
1347 MapSector * ServerMap::emergeSector(v2s16 p2d)
1349 DSTACK("%s: p2d=(%d,%d)",
1352 // Check that it doesn't exist already
1354 return getSectorNoGenerate(p2d);
1356 catch(InvalidPositionException &e)
1361 Try to load the sector from disk.
1363 if(loadSectorFull(p2d) == true)
1365 return getSectorNoGenerate(p2d);
1369 If there is no master heightmap, throw.
1371 if(m_heightmap == NULL)
1373 throw InvalidPositionException("emergeSector(): no heightmap");
1377 Do not generate over-limit
1379 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1380 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1381 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1382 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1383 throw InvalidPositionException("emergeSector(): pos. over limit");
1386 Generate sector and heightmaps
1389 // Number of heightmaps in sector in each direction
1390 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1392 // Heightmap side width
1393 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1395 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1397 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1398 " heightmaps and objects"<<std::endl;*/
1400 // Loop through sub-heightmaps
1401 for(s16 y=0; y<hm_split; y++)
1402 for(s16 x=0; x<hm_split; x++)
1404 v2s16 p_in_sector = v2s16(x,y);
1405 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1407 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1408 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1409 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1410 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1413 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1414 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1417 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1419 sector->setHeightmap(p_in_sector, hm);
1421 //TODO: Make these values configurable
1422 hm->generateContinued(1.0, 0.2, corners);
1423 //hm->generateContinued(2.0, 0.2, corners);
1433 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1434 sector->setObjects(objects);
1436 v2s16 mhm_p = p2d * hm_split;
1438 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1439 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1440 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1441 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1444 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1445 float avgslope = 0.0;
1446 avgslope += fabs(avgheight - corners[0]);
1447 avgslope += fabs(avgheight - corners[1]);
1448 avgslope += fabs(avgheight - corners[2]);
1449 avgslope += fabs(avgheight - corners[3]);
1451 avgslope /= MAP_BLOCKSIZE;
1452 //dstream<<"avgslope="<<avgslope<<std::endl;
1454 float pitness = 0.0;
1456 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1459 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1462 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1465 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1469 pitness /= MAP_BLOCKSIZE;
1470 //dstream<<"pitness="<<pitness<<std::endl;
1473 Plant some trees if there is not much slope
1476 // Avgslope is the derivative of a hill
1477 float t = avgslope * avgslope;
1478 float a = MAP_BLOCKSIZE * 2 * m_params.plants_amount;
1481 tree_max = a / (t/0.03);
1484 u32 count = (rand()%(tree_max+1));
1485 //u32 count = tree_max;
1486 for(u32 i=0; i<count; i++)
1488 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1489 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1490 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1493 objects->insert(v3s16(x, y, z),
1494 SECTOR_OBJECT_TREE_1);
1498 Plant some bushes if sector is pit-like
1501 // Pitness usually goes at around -0.5...0.5
1503 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1505 bush_max = (pitness*a*4);
1508 u32 count = (rand()%(bush_max+1));
1509 for(u32 i=0; i<count; i++)
1511 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1512 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1513 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1516 objects->insert(v3s16(x, y, z),
1517 SECTOR_OBJECT_BUSH_1);
1521 Add ravine (randomly)
1527 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1528 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1531 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1532 objects->insert(v3s16(x, y, z),
1533 SECTOR_OBJECT_RAVINE);
1540 JMutexAutoLock lock(m_sector_mutex);
1541 m_sectors.insert(p2d, sector);
1546 MapBlock * ServerMap::emergeBlock(
1548 bool only_from_disk,
1549 core::map<v3s16, MapBlock*> &changed_blocks,
1550 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1553 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1555 p.X, p.Y, p.Z, only_from_disk);
1557 /*dstream<<"ServerMap::emergeBlock(): "
1558 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1559 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1560 v2s16 p2d(p.X, p.Z);
1563 This will create or load a sector if not found in memory.
1564 If block exists on disk, it will be loaded.
1566 NOTE: On old save formats, this will be slow, as it generates
1567 lighting on blocks for them.
1569 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1570 assert(sector->getId() == MAPSECTOR_SERVER);
1572 // Try to get a block from the sector
1573 MapBlock *block = NULL;
1574 bool not_on_disk = false;
1576 block = sector->getBlockNoCreate(block_y);
1577 if(block->isDummy() == true)
1582 catch(InvalidPositionException &e)
1588 If block was not found on disk and not going to generate a
1589 new one, make sure there is a dummy block in place.
1591 if(not_on_disk && only_from_disk)
1595 // Create dummy block
1596 block = new MapBlock(this, p, true);
1598 // Add block to sector
1599 sector->insertBlock(block);
1605 //dstream<<"Not found on disk, generating."<<std::endl;
1608 Do not generate over-limit
1610 if(blockpos_over_limit(p))
1611 throw InvalidPositionException("emergeBlock(): pos. over limit");
1616 Go on generating the block.
1618 TODO: If a dungeon gets generated so that it's side gets
1619 revealed to the outside air, the lighting should be
1624 If block doesn't exist, create one.
1625 If it exists, it is a dummy. In that case unDummify() it.
1629 block = sector->createBlankBlockNoInsert(block_y);
1633 // Remove the block so that nobody can get a half-generated one.
1634 sector->removeBlock(block);
1635 // Allocate the block to be a proper one.
1639 // Randomize a bit. This makes dungeons.
1640 /*bool low_block_is_empty = false;
1642 low_block_is_empty = true;*/
1645 bool underground_emptiness[ued*ued*ued];
1646 for(s32 i=0; i<ued*ued*ued; i++)
1648 underground_emptiness[i] = ((rand() % 4) == 0);
1651 // This is the basic material of what the visible flat ground
1653 u8 material = MATERIAL_GRASS;
1655 s32 lowest_ground_y = 32767;
1658 //sector->printHeightmaps();
1660 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1661 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1663 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1664 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1665 assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1666 s16 surface_y = surface_y_f;
1667 //avg_ground_y += surface_y;
1668 if(surface_y < lowest_ground_y)
1669 lowest_ground_y = surface_y;
1671 s32 surface_depth = 0;
1673 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1675 float min_slope = 0.45;
1676 float max_slope = 0.85;
1677 float min_slope_depth = 5.0;
1678 float max_slope_depth = 0;
1679 if(slope < min_slope)
1680 surface_depth = min_slope_depth;
1681 else if(slope > max_slope)
1682 surface_depth = max_slope_depth;
1684 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1686 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1688 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1693 NOTE: If there are some man-made structures above the
1694 newly created block, they won't be taken into account.
1696 if(real_y > surface_y)
1697 n.setLight(LIGHT_SUN);
1701 // If node is very low
1702 if(real_y <= surface_y - 7){
1704 if(underground_emptiness[
1705 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1706 +ued*(y0*ued/MAP_BLOCKSIZE)
1707 +(x0*ued/MAP_BLOCKSIZE)])
1713 n.d = MATERIAL_STONE;
1716 // If node is under surface level
1717 else if(real_y <= surface_y - surface_depth)
1718 n.d = MATERIAL_STONE;
1719 // If node is at or under heightmap y
1720 else if(real_y <= surface_y)
1722 // If under water level, it's mud
1723 if(real_y < WATER_LEVEL)
1725 // Else it's the main material
1729 // If node is over heightmap y
1731 // If under water level, it's water
1732 if(real_y < WATER_LEVEL)
1734 n.d = MATERIAL_WATER;
1735 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1741 block->setNode(v3s16(x0,y0,z0), n);
1746 Calculate is_underground
1748 // Probably underground if the highest part of block is under lowest
1750 bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1751 block->setIsUnderground(is_underground);
1754 Force lighting update if underground.
1755 This is needed because of ravines.
1760 lighting_invalidated_blocks[block->getPos()] = block;
1769 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1770 for(s16 i=0; i<underground_level*3; i++)
1775 (rand()%(MAP_BLOCKSIZE-2))+1,
1776 (rand()%(MAP_BLOCKSIZE-2))+1,
1777 (rand()%(MAP_BLOCKSIZE-2))+1
1781 n.d = MATERIAL_MESE;
1784 block->setNode(cp, n);
1786 for(u16 i=0; i<26; i++)
1788 if(!is_ground_material(block->getNode(cp+g_26dirs[i]).d))
1792 block->setNode(cp+g_26dirs[i], n);
1799 Create a few rats in empty blocks underground
1801 /*if(is_underground && low_block_is_empty == true)
1803 //for(u16 i=0; i<2; i++)
1806 RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1807 block->addObject(obj);
1812 Add block to sector.
1814 sector->insertBlock(block);
1817 Do some interpolation for dungeons
1822 TimeTaker timer("interpolation", g_device);
1824 MapVoxelManipulator vmanip(this);
1826 v3s16 relpos = block->getPosRelative();
1828 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1829 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1830 /*vmanip.interpolate(VoxelArea(relpos,
1831 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1833 core::map<v3s16, MapBlock*> modified_blocks;
1834 vmanip.blitBack(modified_blocks);
1835 dstream<<"blitBack modified "<<modified_blocks.size()
1836 <<" blocks"<<std::endl;
1838 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1839 for(core::map<v3s16, MapBlock*>::Iterator
1840 i = modified_blocks.getIterator();
1841 i.atEnd() == false; i++)
1843 MapBlock *block = i.getNode()->getValue();
1845 changed_blocks.insert(block->getPos(), block);
1846 //lighting_invalidated_blocks.insert(block->getPos(), block);
1856 // An y-wise container of changed blocks
1857 core::map<s16, MapBlock*> changed_blocks_sector;
1860 Check if any sector's objects can be placed now.
1863 core::map<v3s16, u8> *objects = sector->getObjects();
1864 core::list<v3s16> objects_to_remove;
1865 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1866 i.atEnd() == false; i++)
1868 v3s16 p = i.getNode()->getKey();
1870 u8 d = i.getNode()->getValue();
1872 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1877 if(d == SECTOR_OBJECT_TEST)
1879 if(sector->isValidArea(p + v3s16(0,0,0),
1880 p + v3s16(0,0,0), &changed_blocks_sector))
1883 n.d = MATERIAL_LIGHT;
1884 sector->setNode(p, n);
1885 objects_to_remove.push_back(p);
1888 else if(d == SECTOR_OBJECT_TREE_1)
1890 v3s16 p_min = p + v3s16(-1,0,-1);
1891 v3s16 p_max = p + v3s16(1,4,1);
1892 if(sector->isValidArea(p_min, p_max,
1893 &changed_blocks_sector))
1896 n.d = MATERIAL_TREE;
1897 sector->setNode(p+v3s16(0,0,0), n);
1898 sector->setNode(p+v3s16(0,1,0), n);
1899 sector->setNode(p+v3s16(0,2,0), n);
1900 sector->setNode(p+v3s16(0,3,0), n);
1902 n.d = MATERIAL_LEAVES;
1904 sector->setNode(p+v3s16(0,4,0), n);
1906 sector->setNode(p+v3s16(-1,4,0), n);
1907 sector->setNode(p+v3s16(1,4,0), n);
1908 sector->setNode(p+v3s16(0,4,-1), n);
1909 sector->setNode(p+v3s16(0,4,1), n);
1910 sector->setNode(p+v3s16(1,4,1), n);
1911 sector->setNode(p+v3s16(-1,4,1), n);
1912 sector->setNode(p+v3s16(-1,4,-1), n);
1913 sector->setNode(p+v3s16(1,4,-1), n);
1915 sector->setNode(p+v3s16(-1,3,0), n);
1916 sector->setNode(p+v3s16(1,3,0), n);
1917 sector->setNode(p+v3s16(0,3,-1), n);
1918 sector->setNode(p+v3s16(0,3,1), n);
1919 sector->setNode(p+v3s16(1,3,1), n);
1920 sector->setNode(p+v3s16(-1,3,1), n);
1921 sector->setNode(p+v3s16(-1,3,-1), n);
1922 sector->setNode(p+v3s16(1,3,-1), n);
1924 objects_to_remove.push_back(p);
1926 // Lighting has to be recalculated for this one.
1927 sector->getBlocksInArea(p_min, p_max,
1928 lighting_invalidated_blocks);
1931 else if(d == SECTOR_OBJECT_BUSH_1)
1933 if(sector->isValidArea(p + v3s16(0,0,0),
1934 p + v3s16(0,0,0), &changed_blocks_sector))
1937 n.d = MATERIAL_LEAVES;
1938 sector->setNode(p+v3s16(0,0,0), n);
1940 objects_to_remove.push_back(p);
1943 else if(d == SECTOR_OBJECT_RAVINE)
1946 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
1947 v3s16 p_max = p + v3s16(6,6,6);
1948 if(sector->isValidArea(p_min, p_max,
1949 &changed_blocks_sector))
1952 n.d = MATERIAL_STONE;
1954 n2.d = MATERIAL_AIR;
1955 s16 depth = maxdepth + (rand()%10);
1957 s16 minz = -6 - (-2);
1959 for(s16 x=-6; x<=6; x++)
1961 z += -1 + (rand()%3);
1966 for(s16 y=depth+(rand()%2); y<=6; y++)
1968 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1971 v3s16 p2 = p + v3s16(x,y,z-2);
1972 if(is_ground_material(sector->getNode(p2).d))
1973 sector->setNode(p2, n);
1976 v3s16 p2 = p + v3s16(x,y,z-1);
1977 if(is_ground_material(sector->getNode(p2).d))
1978 sector->setNode(p2, n2);
1981 v3s16 p2 = p + v3s16(x,y,z+0);
1982 if(is_ground_material(sector->getNode(p2).d))
1983 sector->setNode(p2, n2);
1986 v3s16 p2 = p + v3s16(x,y,z+1);
1987 if(is_ground_material(sector->getNode(p2).d))
1988 sector->setNode(p2, n);
1991 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
1992 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
1996 objects_to_remove.push_back(p);
1998 // Lighting has to be recalculated for this one.
1999 sector->getBlocksInArea(p_min, p_max,
2000 lighting_invalidated_blocks);
2005 dstream<<"ServerMap::emergeBlock(): "
2006 "Invalid heightmap object"
2011 catch(InvalidPositionException &e)
2013 dstream<<"WARNING: "<<__FUNCTION_NAME
2014 <<": while inserting object "<<(int)d
2015 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2016 <<" InvalidPositionException.what()="
2017 <<e.what()<<std::endl;
2018 // This is not too fatal and seems to happen sometimes.
2023 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2024 i != objects_to_remove.end(); i++)
2026 objects->remove(*i);
2029 for(core::map<s16, MapBlock*>::Iterator
2030 i = changed_blocks_sector.getIterator();
2031 i.atEnd() == false; i++)
2033 MapBlock *block = i.getNode()->getValue();
2035 changed_blocks.insert(block->getPos(), block);
2041 void ServerMap::createDir(std::string path)
2043 if(fs::CreateDir(path) == false)
2045 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2046 <<"\""<<path<<"\""<<std::endl;
2047 throw BaseException("ServerMap failed to create directory");
2051 std::string ServerMap::getSectorSubDir(v2s16 pos)
2054 snprintf(cc, 9, "%.4x%.4x",
2055 (unsigned int)pos.X&0xffff,
2056 (unsigned int)pos.Y&0xffff);
2058 return std::string(cc);
2061 std::string ServerMap::getSectorDir(v2s16 pos)
2063 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2066 v2s16 ServerMap::getSectorPos(std::string dirname)
2068 if(dirname.size() != 8)
2069 throw InvalidFilenameException("Invalid sector directory name");
2071 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2073 throw InvalidFilenameException("Invalid sector directory name");
2074 v2s16 pos((s16)x, (s16)y);
2078 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2080 v2s16 p2d = getSectorPos(sectordir);
2082 if(blockfile.size() != 4){
2083 throw InvalidFilenameException("Invalid block filename");
2086 int r = sscanf(blockfile.c_str(), "%4x", &y);
2088 throw InvalidFilenameException("Invalid block filename");
2089 return v3s16(p2d.X, y, p2d.Y);
2093 #define ENABLE_SECTOR_SAVING 1
2094 #define ENABLE_SECTOR_LOADING 1
2095 #define ENABLE_BLOCK_SAVING 1
2096 #define ENABLE_BLOCK_LOADING 1
2098 void ServerMap::save(bool only_changed)
2100 DSTACK(__FUNCTION_NAME);
2101 if(m_map_saving_enabled == false)
2103 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2107 if(only_changed == false)
2108 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2111 saveMasterHeightmap();
2113 u32 sector_meta_count = 0;
2114 u32 block_count = 0;
2117 JMutexAutoLock lock(m_sector_mutex);
2119 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2120 for(; i.atEnd() == false; i++)
2122 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2123 assert(sector->getId() == MAPSECTOR_SERVER);
2125 if(ENABLE_SECTOR_SAVING)
2127 if(sector->differs_from_disk || only_changed == false)
2129 saveSectorMeta(sector);
2130 sector_meta_count++;
2133 if(ENABLE_BLOCK_SAVING)
2135 core::list<MapBlock*> blocks;
2136 sector->getBlocks(blocks);
2137 core::list<MapBlock*>::Iterator j;
2138 for(j=blocks.begin(); j!=blocks.end(); j++)
2140 MapBlock *block = *j;
2141 if(block->getChangedFlag() || only_changed == false)
2152 u32 deleted_count = 0;
2153 deleted_count = deleteUnusedSectors
2154 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2157 Only print if something happened or saved whole map
2159 if(only_changed == false || sector_meta_count != 0
2160 || block_count != 0 || deleted_count != 0)
2162 dstream<<DTIME<<"ServerMap: Written: "
2163 <<sector_meta_count<<" sector metadata files, "
2164 <<block_count<<" block files, "
2165 <<deleted_count<<" sectors unloaded from memory."
2170 void ServerMap::loadAll()
2172 DSTACK(__FUNCTION_NAME);
2173 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2175 loadMasterHeightmap();
2177 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2179 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2181 JMutexAutoLock lock(m_sector_mutex);
2184 s32 printed_counter = -100000;
2185 s32 count = list.size();
2187 std::vector<fs::DirListNode>::iterator i;
2188 for(i=list.begin(); i!=list.end(); i++)
2190 if(counter > printed_counter + 10)
2192 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2193 printed_counter = counter;
2197 MapSector *sector = NULL;
2199 // We want directories
2203 sector = loadSectorMeta(i->name);
2205 catch(InvalidFilenameException &e)
2207 // This catches unknown crap in directory
2210 if(ENABLE_BLOCK_LOADING)
2212 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2213 (m_savedir+"/sectors/"+i->name);
2214 std::vector<fs::DirListNode>::iterator i2;
2215 for(i2=list2.begin(); i2!=list2.end(); i2++)
2221 loadBlock(i->name, i2->name, sector);
2223 catch(InvalidFilenameException &e)
2225 // This catches unknown crap in directory
2230 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2233 void ServerMap::saveMasterHeightmap()
2235 DSTACK(__FUNCTION_NAME);
2236 createDir(m_savedir);
2238 std::string fullpath = m_savedir + "/master_heightmap";
2239 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2240 if(o.good() == false)
2241 throw FileNotGoodException("Cannot open master heightmap");
2243 // Format used for writing
2244 u8 version = SER_FMT_VER_HIGHEST;
2247 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2249 [0] u8 serialization version
2250 [1] X master heightmap
2252 u32 fullsize = 1 + hmdata.getSize();
2253 SharedBuffer<u8> data(fullsize);
2256 memcpy(&data[1], *hmdata, hmdata.getSize());
2258 o.write((const char*)*data, fullsize);
2261 m_heightmap->serialize(o, version);
2264 void ServerMap::loadMasterHeightmap()
2266 DSTACK(__FUNCTION_NAME);
2267 std::string fullpath = m_savedir + "/master_heightmap";
2268 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2269 if(is.good() == false)
2270 throw FileNotGoodException("Cannot open master heightmap");
2272 if(m_heightmap != NULL)
2275 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2278 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2280 DSTACK(__FUNCTION_NAME);
2281 // Format used for writing
2282 u8 version = SER_FMT_VER_HIGHEST;
2284 v2s16 pos = sector->getPos();
2285 createDir(m_savedir);
2286 createDir(m_savedir+"/sectors");
2287 std::string dir = getSectorDir(pos);
2290 std::string fullpath = dir + "/heightmap";
2291 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2292 if(o.good() == false)
2293 throw FileNotGoodException("Cannot open master heightmap");
2295 sector->serialize(o, version);
2297 sector->differs_from_disk = false;
2300 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2302 DSTACK(__FUNCTION_NAME);
2304 v2s16 p2d = getSectorPos(dirname);
2305 std::string dir = m_savedir + "/sectors/" + dirname;
2307 std::string fullpath = dir + "/heightmap";
2308 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2309 if(is.good() == false)
2310 throw FileNotGoodException("Cannot open sector heightmap");
2312 ServerMapSector *sector = ServerMapSector::deSerialize
2313 (is, this, p2d, &m_hwrapper, m_sectors);
2315 sector->differs_from_disk = false;
2320 bool ServerMap::loadSectorFull(v2s16 p2d)
2322 DSTACK(__FUNCTION_NAME);
2323 std::string sectorsubdir = getSectorSubDir(p2d);
2325 MapSector *sector = NULL;
2327 JMutexAutoLock lock(m_sector_mutex);
2330 sector = loadSectorMeta(sectorsubdir);
2332 catch(InvalidFilenameException &e)
2336 catch(FileNotGoodException &e)
2340 catch(std::exception &e)
2345 if(ENABLE_BLOCK_LOADING)
2347 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2348 (m_savedir+"/sectors/"+sectorsubdir);
2349 std::vector<fs::DirListNode>::iterator i2;
2350 for(i2=list2.begin(); i2!=list2.end(); i2++)
2356 loadBlock(sectorsubdir, i2->name, sector);
2358 catch(InvalidFilenameException &e)
2360 // This catches unknown crap in directory
2368 bool ServerMap::deFlushSector(v2s16 p2d)
2370 DSTACK(__FUNCTION_NAME);
2371 // See if it already exists in memory
2373 MapSector *sector = getSectorNoGenerate(p2d);
2376 catch(InvalidPositionException &e)
2379 Try to load the sector from disk.
2381 if(loadSectorFull(p2d) == true)
2390 void ServerMap::saveBlock(MapBlock *block)
2392 DSTACK(__FUNCTION_NAME);
2394 Dummy blocks are not written
2396 if(block->isDummy())
2398 /*v3s16 p = block->getPos();
2399 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2400 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2404 // Format used for writing
2405 u8 version = SER_FMT_VER_HIGHEST;
2407 v3s16 p3d = block->getPos();
2408 v2s16 p2d(p3d.X, p3d.Z);
2409 createDir(m_savedir);
2410 createDir(m_savedir+"/sectors");
2411 std::string dir = getSectorDir(p2d);
2414 // Block file is map/sectors/xxxxxxxx/xxxx
2416 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2417 std::string fullpath = dir + "/" + cc;
2418 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2419 if(o.good() == false)
2420 throw FileNotGoodException("Cannot open block data");
2423 [0] u8 serialization version
2426 o.write((char*)&version, 1);
2428 block->serialize(o, version);
2431 Versions up from 9 have block objects.
2435 block->serializeObjects(o, version);
2438 // We just wrote it to the disk
2439 block->resetChangedFlag();
2442 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2444 DSTACK(__FUNCTION_NAME);
2445 // Block file is map/sectors/xxxxxxxx/xxxx
2446 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2447 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2448 if(is.good() == false)
2449 throw FileNotGoodException("Cannot open block file");
2451 v3s16 p3d = getBlockPos(sectordir, blockfile);
2452 v2s16 p2d(p3d.X, p3d.Z);
2454 assert(sector->getPos() == p2d);
2456 u8 version = SER_FMT_VER_INVALID;
2457 is.read((char*)&version, 1);
2459 /*u32 block_size = MapBlock::serializedLength(version);
2460 SharedBuffer<u8> data(block_size);
2461 is.read((char*)*data, block_size);*/
2463 // This will always return a sector because we're the server
2464 //MapSector *sector = emergeSector(p2d);
2466 MapBlock *block = NULL;
2467 bool created_new = false;
2469 block = sector->getBlockNoCreate(p3d.Y);
2471 catch(InvalidPositionException &e)
2473 block = sector->createBlankBlockNoInsert(p3d.Y);
2477 block->deSerialize(is, version);
2480 Versions up from 9 have block objects.
2484 block->updateObjects(is, version, NULL);
2488 sector->insertBlock(block);
2491 Convert old formats to new and save
2494 if(version == 0 || version == 1)
2496 dstream<<"Block ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2497 " is in old format. Updating lighting and saving"
2498 " modified blocks in new format."<<std::endl;
2500 // Old version has zero lighting, update it
2501 core::map<v3s16, MapBlock*> blocks_changed;
2502 blocks_changed.insert(block->getPos(), block);
2503 core::map<v3s16, MapBlock*> modified_blocks;
2504 updateLighting(blocks_changed, modified_blocks);
2509 // Save modified blocks
2510 core::map<v3s16, MapBlock * >::Iterator i = modified_blocks.getIterator();
2511 for(; i.atEnd() == false; i++)
2513 MapBlock *b2 = i.getNode()->getValue();
2517 // Save blocks in new format
2518 else if(version < SER_FMT_VER_HIGHEST)
2523 // We just loaded it from the disk, so it's up-to-date.
2524 block->resetChangedFlag();
2527 // Gets from master heightmap
2528 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2530 assert(m_heightmap != NULL);
2538 corners[0] = m_heightmap->getGroundHeight
2539 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2540 corners[1] = m_heightmap->getGroundHeight
2541 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2542 corners[2] = m_heightmap->getGroundHeight
2543 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2544 corners[3] = m_heightmap->getGroundHeight
2545 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2548 void ServerMap::PrintInfo(std::ostream &out)
2557 ClientMap::ClientMap(
2559 video::SMaterial *materials,
2560 scene::ISceneNode* parent,
2561 scene::ISceneManager* mgr,
2565 scene::ISceneNode(parent, mgr, id),
2567 m_materials(materials),
2570 /*m_box = core::aabbox3d<f32>(0,0,0,
2571 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2572 /*m_box = core::aabbox3d<f32>(0,0,0,
2573 map->getSizeNodes().X * BS,
2574 map->getSizeNodes().Y * BS,
2575 map->getSizeNodes().Z * BS);*/
2576 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2577 BS*1000000,BS*1000000,BS*1000000);
2582 ClientMap::~ClientMap()
2584 JMutexAutoLock lock(mesh_mutex);
2593 MapSector * ClientMap::emergeSector(v2s16 p2d)
2595 DSTACK(__FUNCTION_NAME);
2596 // Check that it doesn't exist already
2598 return getSectorNoGenerate(p2d);
2600 catch(InvalidPositionException &e)
2604 // Create a sector with no heightmaps
2605 ClientMapSector *sector = new ClientMapSector(this, p2d);
2608 JMutexAutoLock lock(m_sector_mutex);
2609 m_sectors.insert(p2d, sector);
2615 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2617 DSTACK(__FUNCTION_NAME);
2618 ClientMapSector *sector = NULL;
2620 JMutexAutoLock lock(m_sector_mutex);
2622 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2626 sector = (ClientMapSector*)n->getValue();
2627 assert(sector->getId() == MAPSECTOR_CLIENT);
2631 sector = new ClientMapSector(this, p2d);
2633 JMutexAutoLock lock(m_sector_mutex);
2634 m_sectors.insert(p2d, sector);
2638 sector->deSerialize(is);
2641 void ClientMap::renderMap(video::IVideoDriver* driver,
2642 video::SMaterial *materials, s32 pass)
2644 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2645 DSTACK(__FUNCTION_NAME);
2647 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2650 Draw master heightmap mesh
2654 JMutexAutoLock lock(mesh_mutex);
2657 u32 c = mesh->getMeshBufferCount();
2659 for(u32 i=0; i<c; i++)
2661 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2662 const video::SMaterial& material = buf->getMaterial();
2663 video::IMaterialRenderer* rnd =
2664 driver->getMaterialRenderer(material.MaterialType);
2665 bool transparent = (rnd && rnd->isTransparent());
2666 // Render transparent on transparent pass and likewise.
2667 if(transparent == is_transparent_pass)
2669 driver->setMaterial(buf->getMaterial());
2670 driver->drawMeshBuffer(buf);
2678 Get time for measuring timeout.
2680 Measuring time is very useful for long delays when the
2681 machine is swapping a lot.
2683 int time1 = time(0);
2686 Collect all blocks that are in the view range
2688 Should not optimize more here as we want to auto-update
2689 all changed nodes in viewing range at the next step.
2692 s16 viewing_range_nodes;
2693 bool viewing_range_all;
2695 JMutexAutoLock lock(g_range_mutex);
2696 viewing_range_nodes = g_viewing_range_nodes;
2697 viewing_range_all = g_viewing_range_all;
2700 m_camera_mutex.Lock();
2701 v3f camera_position = m_camera_position;
2702 v3f camera_direction = m_camera_direction;
2703 m_camera_mutex.Unlock();
2706 Get all blocks and draw all visible ones
2709 v3s16 cam_pos_nodes(
2710 camera_position.X / BS,
2711 camera_position.Y / BS,
2712 camera_position.Z / BS);
2714 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2716 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2717 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2719 // Take a fair amount as we will be dropping more out later
2721 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2722 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2723 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2725 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2726 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2727 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2729 u32 vertex_count = 0;
2731 core::map<v2s16, MapSector*>::Iterator si;
2733 //NOTE: The sectors map should be locked but we're not doing it
2734 // because it'd cause too much delays
2736 si = m_sectors.getIterator();
2737 for(; si.atEnd() == false; si++)
2740 static int timecheck_counter = 0;
2741 timecheck_counter++;
2742 if(timecheck_counter > 50)
2744 int time2 = time(0);
2745 if(time2 > time1 + 4)
2747 dstream<<"ClientMap::renderMap(): "
2748 "Rendering takes ages, returning."
2755 MapSector *sector = si.getNode()->getValue();
2756 v2s16 sp = sector->getPos();
2758 if(viewing_range_all == false)
2760 if(sp.X < p_blocks_min.X
2761 || sp.X > p_blocks_max.X
2762 || sp.Y < p_blocks_min.Z
2763 || sp.Y > p_blocks_max.Z)
2767 core::list< MapBlock * > sectorblocks;
2768 sector->getBlocks(sectorblocks);
2774 core::list< MapBlock * >::Iterator i;
2775 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2777 MapBlock *block = *i;
2780 Compare block position to camera position, skip
2781 if not seen on display
2784 v3s16 blockpos_nodes = block->getPosRelative();
2786 // Block center position
2788 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2789 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2790 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2793 // Block position relative to camera
2794 v3f blockpos_relative = blockpos - camera_position;
2796 // Distance in camera direction (+=front, -=back)
2797 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2800 f32 d = blockpos_relative.getLength();
2802 if(viewing_range_all == false)
2804 // If block is far away, don't draw it
2805 if(d > viewing_range_nodes * BS)
2809 // Maximum radius of a block
2810 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2812 // If block is (nearly) touching the camera, don't
2813 // bother validating further (that is, render it anyway)
2814 if(d > block_max_radius * 1.5)
2816 // Cosine of the angle between the camera direction
2817 // and the block direction (camera_direction is an unit vector)
2818 f32 cosangle = dforward / d;
2820 // Compensate for the size of the block
2821 // (as the block has to be shown even if it's a bit off FOV)
2822 // This is an estimate.
2823 cosangle += block_max_radius / dforward;
2825 // If block is not in the field of view, skip it
2826 //if(cosangle < cos(FOV_ANGLE/2))
2827 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2832 Draw the faces of the block
2836 JMutexAutoLock lock(block->mesh_mutex);
2838 // Cancel if block has no mesh
2839 if(block->mesh == NULL)
2842 u32 c = block->mesh->getMeshBufferCount();
2844 for(u32 i=0; i<c; i++)
2846 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2847 const video::SMaterial& material = buf->getMaterial();
2848 video::IMaterialRenderer* rnd =
2849 driver->getMaterialRenderer(material.MaterialType);
2850 bool transparent = (rnd && rnd->isTransparent());
2851 // Render transparent on transparent pass and likewise.
2852 if(transparent == is_transparent_pass)
2854 driver->setMaterial(buf->getMaterial());
2855 driver->drawMeshBuffer(buf);
2856 vertex_count += buf->getVertexCount();
2860 } // foreach sectorblocks
2863 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2864 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2867 void ClientMap::updateMesh()
2870 DSTACK(__FUNCTION_NAME);
2873 Check what sectors don't draw anything useful at ground level
2874 and create a mesh of the rough heightmap at those positions.
2877 m_camera_mutex.Lock();
2878 v3f camera_position = m_camera_position;
2879 v3f camera_direction = m_camera_direction;
2880 m_camera_mutex.Unlock();
2882 v3s16 cam_pos_nodes(
2883 camera_position.X / BS,
2884 camera_position.Y / BS,
2885 camera_position.Z / BS);
2887 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2889 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2890 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2892 // Take a fair amount as we will be dropping more out later
2894 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2895 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2896 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2898 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2899 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2900 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2906 scene::SMesh *mesh_new = new scene::SMesh();
2907 //scene::IMeshBuffer *buf = NULL;
2908 scene::SMeshBuffer *buf = NULL;
2910 u8 material_in_use = 0;
2913 Loop through sectors
2916 for(core::map<v2s16, MapSector*>::Iterator
2917 si = m_sectors.getIterator();
2918 si.atEnd() == false; si++)
2920 MapSector *sector = si.getNode()->getValue();
2922 if(sector->getId() != MAPSECTOR_CLIENT)
2924 dstream<<"WARNING: Client has a non-client sector"
2929 ClientMapSector *cs = (ClientMapSector*)sector;
2931 v2s16 sp = sector->getPos();
2933 if(sp.X < p_blocks_min.X
2934 || sp.X > p_blocks_max.X
2935 || sp.Y < p_blocks_min.Z
2936 || sp.Y > p_blocks_max.Z)
2940 Get some ground level info
2952 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
2954 s16 cn_max = -32768;
2955 for(s16 i=0; i<4; i++)
2962 s16 cn_slope = cn_max - cn_min;
2965 Generate this part of the heightmap mesh
2969 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
2971 else if(cn_slope <= MAP_BLOCKSIZE)
2976 if(material != material_in_use || buf == NULL)
2978 // Try to get a meshbuffer associated with the material
2979 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
2980 (g_mesh_materials[material]);
2981 // If not found, create one
2984 // This is a "Standard MeshBuffer",
2985 // it's a typedeffed CMeshBuffer<video::S3DVertex>
2986 buf = new scene::SMeshBuffer();
2989 buf->Material = g_mesh_materials[material];
2991 //buf->setHardwareMappingHint(scene::EHM_STATIC);
2993 mesh_new->addMeshBuffer(buf);
2997 material_in_use = material;
3000 // Sector side width in floating-point units
3001 f32 sd = BS * MAP_BLOCKSIZE;
3002 // Sector position in global floating-point units
3003 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
3005 //video::SColor c(255,255,255,255);
3007 video::SColor c(255,cc,cc,cc);
3009 video::S3DVertex vertices[4] =
3011 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
3012 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
3013 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3014 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3016 u16 indices[] = {0,1,2,2,3,0};
3018 buf->append(vertices, 4, indices, 6);
3022 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3030 scene::SMesh *mesh_old = mesh;
3037 mesh_mutex.Unlock();
3039 if(mesh_old != NULL)
3041 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3043 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3044 (g_materials[MATERIAL_GRASS]);
3046 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3053 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3058 void ClientMap::PrintInfo(std::ostream &out)