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 * 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)
1523 if(m_params.ravines_amount != 0)
1525 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1528 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1529 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1532 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1533 objects->insert(v3s16(x, y, z),
1534 SECTOR_OBJECT_RAVINE);
1541 JMutexAutoLock lock(m_sector_mutex);
1542 m_sectors.insert(p2d, sector);
1547 MapBlock * ServerMap::emergeBlock(
1549 bool only_from_disk,
1550 core::map<v3s16, MapBlock*> &changed_blocks,
1551 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1554 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1556 p.X, p.Y, p.Z, only_from_disk);
1558 /*dstream<<"ServerMap::emergeBlock(): "
1559 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1560 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1561 v2s16 p2d(p.X, p.Z);
1564 This will create or load a sector if not found in memory.
1565 If block exists on disk, it will be loaded.
1567 NOTE: On old save formats, this will be slow, as it generates
1568 lighting on blocks for them.
1570 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1571 assert(sector->getId() == MAPSECTOR_SERVER);
1573 // Try to get a block from the sector
1574 MapBlock *block = NULL;
1575 bool not_on_disk = false;
1577 block = sector->getBlockNoCreate(block_y);
1578 if(block->isDummy() == true)
1583 catch(InvalidPositionException &e)
1589 If block was not found on disk and not going to generate a
1590 new one, make sure there is a dummy block in place.
1592 if(not_on_disk && only_from_disk)
1596 // Create dummy block
1597 block = new MapBlock(this, p, true);
1599 // Add block to sector
1600 sector->insertBlock(block);
1606 //dstream<<"Not found on disk, generating."<<std::endl;
1609 Do not generate over-limit
1611 if(blockpos_over_limit(p))
1612 throw InvalidPositionException("emergeBlock(): pos. over limit");
1617 Go on generating the block.
1619 TODO: If a dungeon gets generated so that it's side gets
1620 revealed to the outside air, the lighting should be
1625 If block doesn't exist, create one.
1626 If it exists, it is a dummy. In that case unDummify() it.
1630 block = sector->createBlankBlockNoInsert(block_y);
1634 // Remove the block so that nobody can get a half-generated one.
1635 sector->removeBlock(block);
1636 // Allocate the block to be a proper one.
1640 // Randomize a bit. This makes dungeons.
1641 /*bool low_block_is_empty = false;
1643 low_block_is_empty = true;*/
1646 bool underground_emptiness[ued*ued*ued];
1647 for(s32 i=0; i<ued*ued*ued; i++)
1649 underground_emptiness[i] = ((rand() % 4) == 0);
1652 // This is the basic material of what the visible flat ground
1654 u8 material = MATERIAL_GRASS;
1656 s32 lowest_ground_y = 32767;
1659 //sector->printHeightmaps();
1661 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1662 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1664 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1666 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1667 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1668 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1670 dstream<<"WARNING: Surface height not found in sector "
1671 "for block that is being emerged"<<std::endl;
1675 s16 surface_y = surface_y_f;
1676 //avg_ground_y += surface_y;
1677 if(surface_y < lowest_ground_y)
1678 lowest_ground_y = surface_y;
1680 s32 surface_depth = 0;
1682 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1684 float min_slope = 0.45;
1685 float max_slope = 0.85;
1686 float min_slope_depth = 5.0;
1687 float max_slope_depth = 0;
1688 if(slope < min_slope)
1689 surface_depth = min_slope_depth;
1690 else if(slope > max_slope)
1691 surface_depth = max_slope_depth;
1693 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1695 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1697 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1702 NOTE: If there are some man-made structures above the
1703 newly created block, they won't be taken into account.
1705 if(real_y > surface_y)
1706 n.setLight(LIGHT_SUN);
1710 // If node is very low
1711 if(real_y <= surface_y - 7){
1713 if(underground_emptiness[
1714 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1715 +ued*(y0*ued/MAP_BLOCKSIZE)
1716 +(x0*ued/MAP_BLOCKSIZE)])
1722 n.d = MATERIAL_STONE;
1725 // If node is under surface level
1726 else if(real_y <= surface_y - surface_depth)
1727 n.d = MATERIAL_STONE;
1728 // If node is at or under heightmap y
1729 else if(real_y <= surface_y)
1731 // If under water level, it's mud
1732 if(real_y < WATER_LEVEL)
1734 // Else it's the main material
1738 // If node is over heightmap y
1740 // If under water level, it's water
1741 if(real_y < WATER_LEVEL)
1743 n.d = MATERIAL_WATER;
1744 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1750 block->setNode(v3s16(x0,y0,z0), n);
1755 Calculate is_underground
1757 // Probably underground if the highest part of block is under lowest
1759 bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1760 block->setIsUnderground(is_underground);
1763 Force lighting update if underground.
1764 This is needed because of ravines.
1769 lighting_invalidated_blocks[block->getPos()] = block;
1778 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1779 for(s16 i=0; i<underground_level*3; i++)
1784 (rand()%(MAP_BLOCKSIZE-2))+1,
1785 (rand()%(MAP_BLOCKSIZE-2))+1,
1786 (rand()%(MAP_BLOCKSIZE-2))+1
1790 n.d = MATERIAL_MESE;
1792 if(is_ground_material(block->getNode(cp).d))
1794 block->setNode(cp, n);
1796 for(u16 i=0; i<26; i++)
1798 if(is_ground_material(block->getNode(cp+g_26dirs[i]).d))
1800 block->setNode(cp+g_26dirs[i], n);
1807 Create a few rats in empty blocks underground
1811 //for(u16 i=0; i<2; i++)
1814 (rand()%(MAP_BLOCKSIZE-2))+1,
1815 (rand()%(MAP_BLOCKSIZE-2))+1,
1816 (rand()%(MAP_BLOCKSIZE-2))+1
1819 // Check that the place is empty
1820 //if(!is_ground_material(block->getNode(cp).d))
1823 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1824 block->addObject(obj);
1830 Add block to sector.
1832 sector->insertBlock(block);
1835 Do some interpolation for dungeons
1840 TimeTaker timer("interpolation", g_device);
1842 MapVoxelManipulator vmanip(this);
1844 v3s16 relpos = block->getPosRelative();
1846 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1847 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1848 /*vmanip.interpolate(VoxelArea(relpos,
1849 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1851 core::map<v3s16, MapBlock*> modified_blocks;
1852 vmanip.blitBack(modified_blocks);
1853 dstream<<"blitBack modified "<<modified_blocks.size()
1854 <<" blocks"<<std::endl;
1856 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1857 for(core::map<v3s16, MapBlock*>::Iterator
1858 i = modified_blocks.getIterator();
1859 i.atEnd() == false; i++)
1861 MapBlock *block = i.getNode()->getValue();
1863 changed_blocks.insert(block->getPos(), block);
1864 //lighting_invalidated_blocks.insert(block->getPos(), block);
1874 // An y-wise container of changed blocks
1875 core::map<s16, MapBlock*> changed_blocks_sector;
1878 Check if any sector's objects can be placed now.
1881 core::map<v3s16, u8> *objects = sector->getObjects();
1882 core::list<v3s16> objects_to_remove;
1883 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1884 i.atEnd() == false; i++)
1886 v3s16 p = i.getNode()->getKey();
1888 u8 d = i.getNode()->getValue();
1890 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1895 if(d == SECTOR_OBJECT_TEST)
1897 if(sector->isValidArea(p + v3s16(0,0,0),
1898 p + v3s16(0,0,0), &changed_blocks_sector))
1901 n.d = MATERIAL_LIGHT;
1902 sector->setNode(p, n);
1903 objects_to_remove.push_back(p);
1906 else if(d == SECTOR_OBJECT_TREE_1)
1908 v3s16 p_min = p + v3s16(-1,0,-1);
1909 v3s16 p_max = p + v3s16(1,4,1);
1910 if(sector->isValidArea(p_min, p_max,
1911 &changed_blocks_sector))
1914 n.d = MATERIAL_TREE;
1915 sector->setNode(p+v3s16(0,0,0), n);
1916 sector->setNode(p+v3s16(0,1,0), n);
1917 sector->setNode(p+v3s16(0,2,0), n);
1918 sector->setNode(p+v3s16(0,3,0), n);
1920 n.d = MATERIAL_LEAVES;
1922 sector->setNode(p+v3s16(0,4,0), n);
1924 sector->setNode(p+v3s16(-1,4,0), n);
1925 sector->setNode(p+v3s16(1,4,0), n);
1926 sector->setNode(p+v3s16(0,4,-1), n);
1927 sector->setNode(p+v3s16(0,4,1), n);
1928 sector->setNode(p+v3s16(1,4,1), n);
1929 sector->setNode(p+v3s16(-1,4,1), n);
1930 sector->setNode(p+v3s16(-1,4,-1), n);
1931 sector->setNode(p+v3s16(1,4,-1), n);
1933 sector->setNode(p+v3s16(-1,3,0), n);
1934 sector->setNode(p+v3s16(1,3,0), n);
1935 sector->setNode(p+v3s16(0,3,-1), n);
1936 sector->setNode(p+v3s16(0,3,1), n);
1937 sector->setNode(p+v3s16(1,3,1), n);
1938 sector->setNode(p+v3s16(-1,3,1), n);
1939 sector->setNode(p+v3s16(-1,3,-1), n);
1940 sector->setNode(p+v3s16(1,3,-1), n);
1942 objects_to_remove.push_back(p);
1944 // Lighting has to be recalculated for this one.
1945 sector->getBlocksInArea(p_min, p_max,
1946 lighting_invalidated_blocks);
1949 else if(d == SECTOR_OBJECT_BUSH_1)
1951 if(sector->isValidArea(p + v3s16(0,0,0),
1952 p + v3s16(0,0,0), &changed_blocks_sector))
1955 n.d = MATERIAL_LEAVES;
1956 sector->setNode(p+v3s16(0,0,0), n);
1958 objects_to_remove.push_back(p);
1961 else if(d == SECTOR_OBJECT_RAVINE)
1964 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
1965 v3s16 p_max = p + v3s16(6,6,6);
1966 if(sector->isValidArea(p_min, p_max,
1967 &changed_blocks_sector))
1970 n.d = MATERIAL_STONE;
1972 n2.d = MATERIAL_AIR;
1973 s16 depth = maxdepth + (rand()%10);
1975 s16 minz = -6 - (-2);
1977 for(s16 x=-6; x<=6; x++)
1979 z += -1 + (rand()%3);
1984 for(s16 y=depth+(rand()%2); y<=6; y++)
1986 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1989 v3s16 p2 = p + v3s16(x,y,z-2);
1990 if(is_ground_material(sector->getNode(p2).d))
1991 sector->setNode(p2, n);
1994 v3s16 p2 = p + v3s16(x,y,z-1);
1995 if(is_ground_material(sector->getNode(p2).d))
1996 sector->setNode(p2, n2);
1999 v3s16 p2 = p + v3s16(x,y,z+0);
2000 if(is_ground_material(sector->getNode(p2).d))
2001 sector->setNode(p2, n2);
2004 v3s16 p2 = p + v3s16(x,y,z+1);
2005 if(is_ground_material(sector->getNode(p2).d))
2006 sector->setNode(p2, n);
2009 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2010 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2014 objects_to_remove.push_back(p);
2016 // Lighting has to be recalculated for this one.
2017 sector->getBlocksInArea(p_min, p_max,
2018 lighting_invalidated_blocks);
2023 dstream<<"ServerMap::emergeBlock(): "
2024 "Invalid heightmap object"
2029 catch(InvalidPositionException &e)
2031 dstream<<"WARNING: "<<__FUNCTION_NAME
2032 <<": while inserting object "<<(int)d
2033 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2034 <<" InvalidPositionException.what()="
2035 <<e.what()<<std::endl;
2036 // This is not too fatal and seems to happen sometimes.
2041 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2042 i != objects_to_remove.end(); i++)
2044 objects->remove(*i);
2047 for(core::map<s16, MapBlock*>::Iterator
2048 i = changed_blocks_sector.getIterator();
2049 i.atEnd() == false; i++)
2051 MapBlock *block = i.getNode()->getValue();
2053 changed_blocks.insert(block->getPos(), block);
2059 void ServerMap::createDir(std::string path)
2061 if(fs::CreateDir(path) == false)
2063 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2064 <<"\""<<path<<"\""<<std::endl;
2065 throw BaseException("ServerMap failed to create directory");
2069 std::string ServerMap::getSectorSubDir(v2s16 pos)
2072 snprintf(cc, 9, "%.4x%.4x",
2073 (unsigned int)pos.X&0xffff,
2074 (unsigned int)pos.Y&0xffff);
2076 return std::string(cc);
2079 std::string ServerMap::getSectorDir(v2s16 pos)
2081 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2084 v2s16 ServerMap::getSectorPos(std::string dirname)
2086 if(dirname.size() != 8)
2087 throw InvalidFilenameException("Invalid sector directory name");
2089 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2091 throw InvalidFilenameException("Invalid sector directory name");
2092 v2s16 pos((s16)x, (s16)y);
2096 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2098 v2s16 p2d = getSectorPos(sectordir);
2100 if(blockfile.size() != 4){
2101 throw InvalidFilenameException("Invalid block filename");
2104 int r = sscanf(blockfile.c_str(), "%4x", &y);
2106 throw InvalidFilenameException("Invalid block filename");
2107 return v3s16(p2d.X, y, p2d.Y);
2111 #define ENABLE_SECTOR_SAVING 1
2112 #define ENABLE_SECTOR_LOADING 1
2113 #define ENABLE_BLOCK_SAVING 1
2114 #define ENABLE_BLOCK_LOADING 1
2116 void ServerMap::save(bool only_changed)
2118 DSTACK(__FUNCTION_NAME);
2119 if(m_map_saving_enabled == false)
2121 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2125 if(only_changed == false)
2126 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2129 saveMasterHeightmap();
2131 u32 sector_meta_count = 0;
2132 u32 block_count = 0;
2135 JMutexAutoLock lock(m_sector_mutex);
2137 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2138 for(; i.atEnd() == false; i++)
2140 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2141 assert(sector->getId() == MAPSECTOR_SERVER);
2143 if(ENABLE_SECTOR_SAVING)
2145 if(sector->differs_from_disk || only_changed == false)
2147 saveSectorMeta(sector);
2148 sector_meta_count++;
2151 if(ENABLE_BLOCK_SAVING)
2153 core::list<MapBlock*> blocks;
2154 sector->getBlocks(blocks);
2155 core::list<MapBlock*>::Iterator j;
2156 for(j=blocks.begin(); j!=blocks.end(); j++)
2158 MapBlock *block = *j;
2159 if(block->getChangedFlag() || only_changed == false)
2170 u32 deleted_count = 0;
2171 deleted_count = deleteUnusedSectors
2172 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2175 Only print if something happened or saved whole map
2177 if(only_changed == false || sector_meta_count != 0
2178 || block_count != 0 || deleted_count != 0)
2180 dstream<<DTIME<<"ServerMap: Written: "
2181 <<sector_meta_count<<" sector metadata files, "
2182 <<block_count<<" block files, "
2183 <<deleted_count<<" sectors unloaded from memory."
2188 void ServerMap::loadAll()
2190 DSTACK(__FUNCTION_NAME);
2191 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2193 loadMasterHeightmap();
2195 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2197 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2199 JMutexAutoLock lock(m_sector_mutex);
2202 s32 printed_counter = -100000;
2203 s32 count = list.size();
2205 std::vector<fs::DirListNode>::iterator i;
2206 for(i=list.begin(); i!=list.end(); i++)
2208 if(counter > printed_counter + 10)
2210 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2211 printed_counter = counter;
2215 MapSector *sector = NULL;
2217 // We want directories
2221 sector = loadSectorMeta(i->name);
2223 catch(InvalidFilenameException &e)
2225 // This catches unknown crap in directory
2228 if(ENABLE_BLOCK_LOADING)
2230 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2231 (m_savedir+"/sectors/"+i->name);
2232 std::vector<fs::DirListNode>::iterator i2;
2233 for(i2=list2.begin(); i2!=list2.end(); i2++)
2239 loadBlock(i->name, i2->name, sector);
2241 catch(InvalidFilenameException &e)
2243 // This catches unknown crap in directory
2248 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2251 void ServerMap::saveMasterHeightmap()
2253 DSTACK(__FUNCTION_NAME);
2254 createDir(m_savedir);
2256 std::string fullpath = m_savedir + "/master_heightmap";
2257 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2258 if(o.good() == false)
2259 throw FileNotGoodException("Cannot open master heightmap");
2261 // Format used for writing
2262 u8 version = SER_FMT_VER_HIGHEST;
2265 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2267 [0] u8 serialization version
2268 [1] X master heightmap
2270 u32 fullsize = 1 + hmdata.getSize();
2271 SharedBuffer<u8> data(fullsize);
2274 memcpy(&data[1], *hmdata, hmdata.getSize());
2276 o.write((const char*)*data, fullsize);
2279 m_heightmap->serialize(o, version);
2282 void ServerMap::loadMasterHeightmap()
2284 DSTACK(__FUNCTION_NAME);
2285 std::string fullpath = m_savedir + "/master_heightmap";
2286 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2287 if(is.good() == false)
2288 throw FileNotGoodException("Cannot open master heightmap");
2290 if(m_heightmap != NULL)
2293 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2296 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2298 DSTACK(__FUNCTION_NAME);
2299 // Format used for writing
2300 u8 version = SER_FMT_VER_HIGHEST;
2302 v2s16 pos = sector->getPos();
2303 createDir(m_savedir);
2304 createDir(m_savedir+"/sectors");
2305 std::string dir = getSectorDir(pos);
2308 std::string fullpath = dir + "/heightmap";
2309 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2310 if(o.good() == false)
2311 throw FileNotGoodException("Cannot open master heightmap");
2313 sector->serialize(o, version);
2315 sector->differs_from_disk = false;
2318 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2320 DSTACK(__FUNCTION_NAME);
2322 v2s16 p2d = getSectorPos(dirname);
2323 std::string dir = m_savedir + "/sectors/" + dirname;
2325 std::string fullpath = dir + "/heightmap";
2326 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2327 if(is.good() == false)
2328 throw FileNotGoodException("Cannot open sector heightmap");
2330 ServerMapSector *sector = ServerMapSector::deSerialize
2331 (is, this, p2d, &m_hwrapper, m_sectors);
2333 sector->differs_from_disk = false;
2338 bool ServerMap::loadSectorFull(v2s16 p2d)
2340 DSTACK(__FUNCTION_NAME);
2341 std::string sectorsubdir = getSectorSubDir(p2d);
2343 MapSector *sector = NULL;
2345 JMutexAutoLock lock(m_sector_mutex);
2348 sector = loadSectorMeta(sectorsubdir);
2350 catch(InvalidFilenameException &e)
2354 catch(FileNotGoodException &e)
2358 catch(std::exception &e)
2363 if(ENABLE_BLOCK_LOADING)
2365 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2366 (m_savedir+"/sectors/"+sectorsubdir);
2367 std::vector<fs::DirListNode>::iterator i2;
2368 for(i2=list2.begin(); i2!=list2.end(); i2++)
2374 loadBlock(sectorsubdir, i2->name, sector);
2376 catch(InvalidFilenameException &e)
2378 // This catches unknown crap in directory
2386 bool ServerMap::deFlushSector(v2s16 p2d)
2388 DSTACK(__FUNCTION_NAME);
2389 // See if it already exists in memory
2391 MapSector *sector = getSectorNoGenerate(p2d);
2394 catch(InvalidPositionException &e)
2397 Try to load the sector from disk.
2399 if(loadSectorFull(p2d) == true)
2408 void ServerMap::saveBlock(MapBlock *block)
2410 DSTACK(__FUNCTION_NAME);
2412 Dummy blocks are not written
2414 if(block->isDummy())
2416 /*v3s16 p = block->getPos();
2417 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2418 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2422 // Format used for writing
2423 u8 version = SER_FMT_VER_HIGHEST;
2425 v3s16 p3d = block->getPos();
2426 v2s16 p2d(p3d.X, p3d.Z);
2427 createDir(m_savedir);
2428 createDir(m_savedir+"/sectors");
2429 std::string dir = getSectorDir(p2d);
2432 // Block file is map/sectors/xxxxxxxx/xxxx
2434 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2435 std::string fullpath = dir + "/" + cc;
2436 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2437 if(o.good() == false)
2438 throw FileNotGoodException("Cannot open block data");
2441 [0] u8 serialization version
2444 o.write((char*)&version, 1);
2446 block->serialize(o, version);
2449 Versions up from 9 have block objects.
2453 block->serializeObjects(o, version);
2456 // We just wrote it to the disk
2457 block->resetChangedFlag();
2460 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2462 DSTACK(__FUNCTION_NAME);
2466 // Block file is map/sectors/xxxxxxxx/xxxx
2467 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2468 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2469 if(is.good() == false)
2470 throw FileNotGoodException("Cannot open block file");
2472 v3s16 p3d = getBlockPos(sectordir, blockfile);
2473 v2s16 p2d(p3d.X, p3d.Z);
2475 assert(sector->getPos() == p2d);
2477 u8 version = SER_FMT_VER_INVALID;
2478 is.read((char*)&version, 1);
2480 /*u32 block_size = MapBlock::serializedLength(version);
2481 SharedBuffer<u8> data(block_size);
2482 is.read((char*)*data, block_size);*/
2484 // This will always return a sector because we're the server
2485 //MapSector *sector = emergeSector(p2d);
2487 MapBlock *block = NULL;
2488 bool created_new = false;
2490 block = sector->getBlockNoCreate(p3d.Y);
2492 catch(InvalidPositionException &e)
2494 block = sector->createBlankBlockNoInsert(p3d.Y);
2498 // deserialize block data
2499 block->deSerialize(is, version);
2502 Versions up from 9 have block objects.
2506 block->updateObjects(is, version, NULL);
2510 sector->insertBlock(block);
2513 Convert old formats to new and save
2516 // Save old format blocks in new format
2517 if(version < SER_FMT_VER_HIGHEST)
2522 // We just loaded it from the disk, so it's up-to-date.
2523 block->resetChangedFlag();
2526 catch(SerializationError &e)
2528 dstream<<"WARNING: Invalid block data on disk "
2529 "(SerializationError). Ignoring."
2534 // Gets from master heightmap
2535 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2537 assert(m_heightmap != NULL);
2545 corners[0] = m_heightmap->getGroundHeight
2546 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2547 corners[1] = m_heightmap->getGroundHeight
2548 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2549 corners[2] = m_heightmap->getGroundHeight
2550 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2551 corners[3] = m_heightmap->getGroundHeight
2552 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2555 void ServerMap::PrintInfo(std::ostream &out)
2564 ClientMap::ClientMap(
2566 video::SMaterial *materials,
2567 scene::ISceneNode* parent,
2568 scene::ISceneManager* mgr,
2572 scene::ISceneNode(parent, mgr, id),
2574 m_materials(materials),
2577 /*m_box = core::aabbox3d<f32>(0,0,0,
2578 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2579 /*m_box = core::aabbox3d<f32>(0,0,0,
2580 map->getSizeNodes().X * BS,
2581 map->getSizeNodes().Y * BS,
2582 map->getSizeNodes().Z * BS);*/
2583 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2584 BS*1000000,BS*1000000,BS*1000000);
2589 ClientMap::~ClientMap()
2591 JMutexAutoLock lock(mesh_mutex);
2600 MapSector * ClientMap::emergeSector(v2s16 p2d)
2602 DSTACK(__FUNCTION_NAME);
2603 // Check that it doesn't exist already
2605 return getSectorNoGenerate(p2d);
2607 catch(InvalidPositionException &e)
2611 // Create a sector with no heightmaps
2612 ClientMapSector *sector = new ClientMapSector(this, p2d);
2615 JMutexAutoLock lock(m_sector_mutex);
2616 m_sectors.insert(p2d, sector);
2622 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2624 DSTACK(__FUNCTION_NAME);
2625 ClientMapSector *sector = NULL;
2627 JMutexAutoLock lock(m_sector_mutex);
2629 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2633 sector = (ClientMapSector*)n->getValue();
2634 assert(sector->getId() == MAPSECTOR_CLIENT);
2638 sector = new ClientMapSector(this, p2d);
2640 JMutexAutoLock lock(m_sector_mutex);
2641 m_sectors.insert(p2d, sector);
2645 sector->deSerialize(is);
2648 void ClientMap::renderMap(video::IVideoDriver* driver,
2649 video::SMaterial *materials, s32 pass)
2651 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2652 DSTACK(__FUNCTION_NAME);
2654 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2657 Draw master heightmap mesh
2661 JMutexAutoLock lock(mesh_mutex);
2664 u32 c = mesh->getMeshBufferCount();
2666 for(u32 i=0; i<c; i++)
2668 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2669 const video::SMaterial& material = buf->getMaterial();
2670 video::IMaterialRenderer* rnd =
2671 driver->getMaterialRenderer(material.MaterialType);
2672 bool transparent = (rnd && rnd->isTransparent());
2673 // Render transparent on transparent pass and likewise.
2674 if(transparent == is_transparent_pass)
2676 driver->setMaterial(buf->getMaterial());
2677 driver->drawMeshBuffer(buf);
2685 Get time for measuring timeout.
2687 Measuring time is very useful for long delays when the
2688 machine is swapping a lot.
2690 int time1 = time(0);
2693 Collect all blocks that are in the view range
2695 Should not optimize more here as we want to auto-update
2696 all changed nodes in viewing range at the next step.
2699 s16 viewing_range_nodes;
2700 bool viewing_range_all;
2702 JMutexAutoLock lock(g_range_mutex);
2703 viewing_range_nodes = g_viewing_range_nodes;
2704 viewing_range_all = g_viewing_range_all;
2707 m_camera_mutex.Lock();
2708 v3f camera_position = m_camera_position;
2709 v3f camera_direction = m_camera_direction;
2710 m_camera_mutex.Unlock();
2713 Get all blocks and draw all visible ones
2716 v3s16 cam_pos_nodes(
2717 camera_position.X / BS,
2718 camera_position.Y / BS,
2719 camera_position.Z / BS);
2721 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2723 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2724 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2726 // Take a fair amount as we will be dropping more out later
2728 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2729 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2730 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2732 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2733 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2734 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2736 u32 vertex_count = 0;
2738 core::map<v2s16, MapSector*>::Iterator si;
2740 //NOTE: The sectors map should be locked but we're not doing it
2741 // because it'd cause too much delays
2743 si = m_sectors.getIterator();
2744 for(; si.atEnd() == false; si++)
2747 static int timecheck_counter = 0;
2748 timecheck_counter++;
2749 if(timecheck_counter > 50)
2751 int time2 = time(0);
2752 if(time2 > time1 + 4)
2754 dstream<<"ClientMap::renderMap(): "
2755 "Rendering takes ages, returning."
2762 MapSector *sector = si.getNode()->getValue();
2763 v2s16 sp = sector->getPos();
2765 if(viewing_range_all == false)
2767 if(sp.X < p_blocks_min.X
2768 || sp.X > p_blocks_max.X
2769 || sp.Y < p_blocks_min.Z
2770 || sp.Y > p_blocks_max.Z)
2774 core::list< MapBlock * > sectorblocks;
2775 sector->getBlocks(sectorblocks);
2781 core::list< MapBlock * >::Iterator i;
2782 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2784 MapBlock *block = *i;
2787 Compare block position to camera position, skip
2788 if not seen on display
2791 v3s16 blockpos_nodes = block->getPosRelative();
2793 // Block center position
2795 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2796 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2797 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2800 // Block position relative to camera
2801 v3f blockpos_relative = blockpos - camera_position;
2803 // Distance in camera direction (+=front, -=back)
2804 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2807 f32 d = blockpos_relative.getLength();
2809 if(viewing_range_all == false)
2811 // If block is far away, don't draw it
2812 if(d > viewing_range_nodes * BS)
2816 // Maximum radius of a block
2817 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2819 // If block is (nearly) touching the camera, don't
2820 // bother validating further (that is, render it anyway)
2821 if(d > block_max_radius * 1.5)
2823 // Cosine of the angle between the camera direction
2824 // and the block direction (camera_direction is an unit vector)
2825 f32 cosangle = dforward / d;
2827 // Compensate for the size of the block
2828 // (as the block has to be shown even if it's a bit off FOV)
2829 // This is an estimate.
2830 cosangle += block_max_radius / dforward;
2832 // If block is not in the field of view, skip it
2833 //if(cosangle < cos(FOV_ANGLE/2))
2834 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2839 Draw the faces of the block
2843 JMutexAutoLock lock(block->mesh_mutex);
2845 // Cancel if block has no mesh
2846 if(block->mesh == NULL)
2849 u32 c = block->mesh->getMeshBufferCount();
2851 for(u32 i=0; i<c; i++)
2853 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2854 const video::SMaterial& material = buf->getMaterial();
2855 video::IMaterialRenderer* rnd =
2856 driver->getMaterialRenderer(material.MaterialType);
2857 bool transparent = (rnd && rnd->isTransparent());
2858 // Render transparent on transparent pass and likewise.
2859 if(transparent == is_transparent_pass)
2861 driver->setMaterial(buf->getMaterial());
2862 driver->drawMeshBuffer(buf);
2863 vertex_count += buf->getVertexCount();
2867 } // foreach sectorblocks
2870 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2871 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2874 void ClientMap::updateMesh()
2877 DSTACK(__FUNCTION_NAME);
2880 Check what sectors don't draw anything useful at ground level
2881 and create a mesh of the rough heightmap at those positions.
2884 m_camera_mutex.Lock();
2885 v3f camera_position = m_camera_position;
2886 v3f camera_direction = m_camera_direction;
2887 m_camera_mutex.Unlock();
2889 v3s16 cam_pos_nodes(
2890 camera_position.X / BS,
2891 camera_position.Y / BS,
2892 camera_position.Z / BS);
2894 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2896 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2897 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2899 // Take a fair amount as we will be dropping more out later
2901 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2902 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2903 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2905 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2906 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2907 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2913 scene::SMesh *mesh_new = new scene::SMesh();
2914 //scene::IMeshBuffer *buf = NULL;
2915 scene::SMeshBuffer *buf = NULL;
2917 u8 material_in_use = 0;
2920 Loop through sectors
2923 for(core::map<v2s16, MapSector*>::Iterator
2924 si = m_sectors.getIterator();
2925 si.atEnd() == false; si++)
2927 MapSector *sector = si.getNode()->getValue();
2929 if(sector->getId() != MAPSECTOR_CLIENT)
2931 dstream<<"WARNING: Client has a non-client sector"
2936 ClientMapSector *cs = (ClientMapSector*)sector;
2938 v2s16 sp = sector->getPos();
2940 if(sp.X < p_blocks_min.X
2941 || sp.X > p_blocks_max.X
2942 || sp.Y < p_blocks_min.Z
2943 || sp.Y > p_blocks_max.Z)
2947 Get some ground level info
2959 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
2961 s16 cn_max = -32768;
2962 for(s16 i=0; i<4; i++)
2969 s16 cn_slope = cn_max - cn_min;
2972 Generate this part of the heightmap mesh
2976 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
2978 else if(cn_slope <= MAP_BLOCKSIZE)
2983 if(material != material_in_use || buf == NULL)
2985 // Try to get a meshbuffer associated with the material
2986 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
2987 (g_mesh_materials[material]);
2988 // If not found, create one
2991 // This is a "Standard MeshBuffer",
2992 // it's a typedeffed CMeshBuffer<video::S3DVertex>
2993 buf = new scene::SMeshBuffer();
2996 buf->Material = g_mesh_materials[material];
2998 //buf->setHardwareMappingHint(scene::EHM_STATIC);
3000 mesh_new->addMeshBuffer(buf);
3004 material_in_use = material;
3007 // Sector side width in floating-point units
3008 f32 sd = BS * MAP_BLOCKSIZE;
3009 // Sector position in global floating-point units
3010 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
3012 //video::SColor c(255,255,255,255);
3014 video::SColor c(255,cc,cc,cc);
3016 video::S3DVertex vertices[4] =
3018 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
3019 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
3020 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3021 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3023 u16 indices[] = {0,1,2,2,3,0};
3025 buf->append(vertices, 4, indices, 6);
3029 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3037 scene::SMesh *mesh_old = mesh;
3044 mesh_mutex.Unlock();
3046 if(mesh_old != NULL)
3048 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3050 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3051 (g_materials[MATERIAL_GRASS]);
3053 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3060 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3065 void ClientMap::PrintInfo(std::ostream &out)