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;
1783 if(is_ground_material(block->getNode(cp).d))
1785 block->setNode(cp, n);
1787 for(u16 i=0; i<26; i++)
1789 if(is_ground_material(block->getNode(cp+g_26dirs[i]).d))
1791 block->setNode(cp+g_26dirs[i], n);
1798 Create a few rats in empty blocks underground
1802 //for(u16 i=0; i<2; i++)
1805 (rand()%(MAP_BLOCKSIZE-2))+1,
1806 (rand()%(MAP_BLOCKSIZE-2))+1,
1807 (rand()%(MAP_BLOCKSIZE-2))+1
1810 // Check that the place is empty
1811 //if(!is_ground_material(block->getNode(cp).d))
1814 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1815 block->addObject(obj);
1821 Add block to sector.
1823 sector->insertBlock(block);
1826 Do some interpolation for dungeons
1831 TimeTaker timer("interpolation", g_device);
1833 MapVoxelManipulator vmanip(this);
1835 v3s16 relpos = block->getPosRelative();
1837 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1838 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1839 /*vmanip.interpolate(VoxelArea(relpos,
1840 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1842 core::map<v3s16, MapBlock*> modified_blocks;
1843 vmanip.blitBack(modified_blocks);
1844 dstream<<"blitBack modified "<<modified_blocks.size()
1845 <<" blocks"<<std::endl;
1847 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1848 for(core::map<v3s16, MapBlock*>::Iterator
1849 i = modified_blocks.getIterator();
1850 i.atEnd() == false; i++)
1852 MapBlock *block = i.getNode()->getValue();
1854 changed_blocks.insert(block->getPos(), block);
1855 //lighting_invalidated_blocks.insert(block->getPos(), block);
1865 // An y-wise container of changed blocks
1866 core::map<s16, MapBlock*> changed_blocks_sector;
1869 Check if any sector's objects can be placed now.
1872 core::map<v3s16, u8> *objects = sector->getObjects();
1873 core::list<v3s16> objects_to_remove;
1874 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1875 i.atEnd() == false; i++)
1877 v3s16 p = i.getNode()->getKey();
1879 u8 d = i.getNode()->getValue();
1881 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1886 if(d == SECTOR_OBJECT_TEST)
1888 if(sector->isValidArea(p + v3s16(0,0,0),
1889 p + v3s16(0,0,0), &changed_blocks_sector))
1892 n.d = MATERIAL_LIGHT;
1893 sector->setNode(p, n);
1894 objects_to_remove.push_back(p);
1897 else if(d == SECTOR_OBJECT_TREE_1)
1899 v3s16 p_min = p + v3s16(-1,0,-1);
1900 v3s16 p_max = p + v3s16(1,4,1);
1901 if(sector->isValidArea(p_min, p_max,
1902 &changed_blocks_sector))
1905 n.d = MATERIAL_TREE;
1906 sector->setNode(p+v3s16(0,0,0), n);
1907 sector->setNode(p+v3s16(0,1,0), n);
1908 sector->setNode(p+v3s16(0,2,0), n);
1909 sector->setNode(p+v3s16(0,3,0), n);
1911 n.d = MATERIAL_LEAVES;
1913 sector->setNode(p+v3s16(0,4,0), n);
1915 sector->setNode(p+v3s16(-1,4,0), n);
1916 sector->setNode(p+v3s16(1,4,0), n);
1917 sector->setNode(p+v3s16(0,4,-1), n);
1918 sector->setNode(p+v3s16(0,4,1), n);
1919 sector->setNode(p+v3s16(1,4,1), n);
1920 sector->setNode(p+v3s16(-1,4,1), n);
1921 sector->setNode(p+v3s16(-1,4,-1), n);
1922 sector->setNode(p+v3s16(1,4,-1), n);
1924 sector->setNode(p+v3s16(-1,3,0), n);
1925 sector->setNode(p+v3s16(1,3,0), n);
1926 sector->setNode(p+v3s16(0,3,-1), n);
1927 sector->setNode(p+v3s16(0,3,1), n);
1928 sector->setNode(p+v3s16(1,3,1), n);
1929 sector->setNode(p+v3s16(-1,3,1), n);
1930 sector->setNode(p+v3s16(-1,3,-1), n);
1931 sector->setNode(p+v3s16(1,3,-1), n);
1933 objects_to_remove.push_back(p);
1935 // Lighting has to be recalculated for this one.
1936 sector->getBlocksInArea(p_min, p_max,
1937 lighting_invalidated_blocks);
1940 else if(d == SECTOR_OBJECT_BUSH_1)
1942 if(sector->isValidArea(p + v3s16(0,0,0),
1943 p + v3s16(0,0,0), &changed_blocks_sector))
1946 n.d = MATERIAL_LEAVES;
1947 sector->setNode(p+v3s16(0,0,0), n);
1949 objects_to_remove.push_back(p);
1952 else if(d == SECTOR_OBJECT_RAVINE)
1955 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
1956 v3s16 p_max = p + v3s16(6,6,6);
1957 if(sector->isValidArea(p_min, p_max,
1958 &changed_blocks_sector))
1961 n.d = MATERIAL_STONE;
1963 n2.d = MATERIAL_AIR;
1964 s16 depth = maxdepth + (rand()%10);
1966 s16 minz = -6 - (-2);
1968 for(s16 x=-6; x<=6; x++)
1970 z += -1 + (rand()%3);
1975 for(s16 y=depth+(rand()%2); y<=6; y++)
1977 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1980 v3s16 p2 = p + v3s16(x,y,z-2);
1981 if(is_ground_material(sector->getNode(p2).d))
1982 sector->setNode(p2, n);
1985 v3s16 p2 = p + v3s16(x,y,z-1);
1986 if(is_ground_material(sector->getNode(p2).d))
1987 sector->setNode(p2, n2);
1990 v3s16 p2 = p + v3s16(x,y,z+0);
1991 if(is_ground_material(sector->getNode(p2).d))
1992 sector->setNode(p2, n2);
1995 v3s16 p2 = p + v3s16(x,y,z+1);
1996 if(is_ground_material(sector->getNode(p2).d))
1997 sector->setNode(p2, n);
2000 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2001 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2005 objects_to_remove.push_back(p);
2007 // Lighting has to be recalculated for this one.
2008 sector->getBlocksInArea(p_min, p_max,
2009 lighting_invalidated_blocks);
2014 dstream<<"ServerMap::emergeBlock(): "
2015 "Invalid heightmap object"
2020 catch(InvalidPositionException &e)
2022 dstream<<"WARNING: "<<__FUNCTION_NAME
2023 <<": while inserting object "<<(int)d
2024 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2025 <<" InvalidPositionException.what()="
2026 <<e.what()<<std::endl;
2027 // This is not too fatal and seems to happen sometimes.
2032 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2033 i != objects_to_remove.end(); i++)
2035 objects->remove(*i);
2038 for(core::map<s16, MapBlock*>::Iterator
2039 i = changed_blocks_sector.getIterator();
2040 i.atEnd() == false; i++)
2042 MapBlock *block = i.getNode()->getValue();
2044 changed_blocks.insert(block->getPos(), block);
2050 void ServerMap::createDir(std::string path)
2052 if(fs::CreateDir(path) == false)
2054 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2055 <<"\""<<path<<"\""<<std::endl;
2056 throw BaseException("ServerMap failed to create directory");
2060 std::string ServerMap::getSectorSubDir(v2s16 pos)
2063 snprintf(cc, 9, "%.4x%.4x",
2064 (unsigned int)pos.X&0xffff,
2065 (unsigned int)pos.Y&0xffff);
2067 return std::string(cc);
2070 std::string ServerMap::getSectorDir(v2s16 pos)
2072 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2075 v2s16 ServerMap::getSectorPos(std::string dirname)
2077 if(dirname.size() != 8)
2078 throw InvalidFilenameException("Invalid sector directory name");
2080 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2082 throw InvalidFilenameException("Invalid sector directory name");
2083 v2s16 pos((s16)x, (s16)y);
2087 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2089 v2s16 p2d = getSectorPos(sectordir);
2091 if(blockfile.size() != 4){
2092 throw InvalidFilenameException("Invalid block filename");
2095 int r = sscanf(blockfile.c_str(), "%4x", &y);
2097 throw InvalidFilenameException("Invalid block filename");
2098 return v3s16(p2d.X, y, p2d.Y);
2102 #define ENABLE_SECTOR_SAVING 1
2103 #define ENABLE_SECTOR_LOADING 1
2104 #define ENABLE_BLOCK_SAVING 1
2105 #define ENABLE_BLOCK_LOADING 1
2107 void ServerMap::save(bool only_changed)
2109 DSTACK(__FUNCTION_NAME);
2110 if(m_map_saving_enabled == false)
2112 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2116 if(only_changed == false)
2117 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2120 saveMasterHeightmap();
2122 u32 sector_meta_count = 0;
2123 u32 block_count = 0;
2126 JMutexAutoLock lock(m_sector_mutex);
2128 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2129 for(; i.atEnd() == false; i++)
2131 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2132 assert(sector->getId() == MAPSECTOR_SERVER);
2134 if(ENABLE_SECTOR_SAVING)
2136 if(sector->differs_from_disk || only_changed == false)
2138 saveSectorMeta(sector);
2139 sector_meta_count++;
2142 if(ENABLE_BLOCK_SAVING)
2144 core::list<MapBlock*> blocks;
2145 sector->getBlocks(blocks);
2146 core::list<MapBlock*>::Iterator j;
2147 for(j=blocks.begin(); j!=blocks.end(); j++)
2149 MapBlock *block = *j;
2150 if(block->getChangedFlag() || only_changed == false)
2161 u32 deleted_count = 0;
2162 deleted_count = deleteUnusedSectors
2163 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2166 Only print if something happened or saved whole map
2168 if(only_changed == false || sector_meta_count != 0
2169 || block_count != 0 || deleted_count != 0)
2171 dstream<<DTIME<<"ServerMap: Written: "
2172 <<sector_meta_count<<" sector metadata files, "
2173 <<block_count<<" block files, "
2174 <<deleted_count<<" sectors unloaded from memory."
2179 void ServerMap::loadAll()
2181 DSTACK(__FUNCTION_NAME);
2182 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2184 loadMasterHeightmap();
2186 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2188 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2190 JMutexAutoLock lock(m_sector_mutex);
2193 s32 printed_counter = -100000;
2194 s32 count = list.size();
2196 std::vector<fs::DirListNode>::iterator i;
2197 for(i=list.begin(); i!=list.end(); i++)
2199 if(counter > printed_counter + 10)
2201 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2202 printed_counter = counter;
2206 MapSector *sector = NULL;
2208 // We want directories
2212 sector = loadSectorMeta(i->name);
2214 catch(InvalidFilenameException &e)
2216 // This catches unknown crap in directory
2219 if(ENABLE_BLOCK_LOADING)
2221 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2222 (m_savedir+"/sectors/"+i->name);
2223 std::vector<fs::DirListNode>::iterator i2;
2224 for(i2=list2.begin(); i2!=list2.end(); i2++)
2230 loadBlock(i->name, i2->name, sector);
2232 catch(InvalidFilenameException &e)
2234 // This catches unknown crap in directory
2239 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2242 void ServerMap::saveMasterHeightmap()
2244 DSTACK(__FUNCTION_NAME);
2245 createDir(m_savedir);
2247 std::string fullpath = m_savedir + "/master_heightmap";
2248 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2249 if(o.good() == false)
2250 throw FileNotGoodException("Cannot open master heightmap");
2252 // Format used for writing
2253 u8 version = SER_FMT_VER_HIGHEST;
2256 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2258 [0] u8 serialization version
2259 [1] X master heightmap
2261 u32 fullsize = 1 + hmdata.getSize();
2262 SharedBuffer<u8> data(fullsize);
2265 memcpy(&data[1], *hmdata, hmdata.getSize());
2267 o.write((const char*)*data, fullsize);
2270 m_heightmap->serialize(o, version);
2273 void ServerMap::loadMasterHeightmap()
2275 DSTACK(__FUNCTION_NAME);
2276 std::string fullpath = m_savedir + "/master_heightmap";
2277 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2278 if(is.good() == false)
2279 throw FileNotGoodException("Cannot open master heightmap");
2281 if(m_heightmap != NULL)
2284 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2287 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2289 DSTACK(__FUNCTION_NAME);
2290 // Format used for writing
2291 u8 version = SER_FMT_VER_HIGHEST;
2293 v2s16 pos = sector->getPos();
2294 createDir(m_savedir);
2295 createDir(m_savedir+"/sectors");
2296 std::string dir = getSectorDir(pos);
2299 std::string fullpath = dir + "/heightmap";
2300 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2301 if(o.good() == false)
2302 throw FileNotGoodException("Cannot open master heightmap");
2304 sector->serialize(o, version);
2306 sector->differs_from_disk = false;
2309 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2311 DSTACK(__FUNCTION_NAME);
2313 v2s16 p2d = getSectorPos(dirname);
2314 std::string dir = m_savedir + "/sectors/" + dirname;
2316 std::string fullpath = dir + "/heightmap";
2317 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2318 if(is.good() == false)
2319 throw FileNotGoodException("Cannot open sector heightmap");
2321 ServerMapSector *sector = ServerMapSector::deSerialize
2322 (is, this, p2d, &m_hwrapper, m_sectors);
2324 sector->differs_from_disk = false;
2329 bool ServerMap::loadSectorFull(v2s16 p2d)
2331 DSTACK(__FUNCTION_NAME);
2332 std::string sectorsubdir = getSectorSubDir(p2d);
2334 MapSector *sector = NULL;
2336 JMutexAutoLock lock(m_sector_mutex);
2339 sector = loadSectorMeta(sectorsubdir);
2341 catch(InvalidFilenameException &e)
2345 catch(FileNotGoodException &e)
2349 catch(std::exception &e)
2354 if(ENABLE_BLOCK_LOADING)
2356 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2357 (m_savedir+"/sectors/"+sectorsubdir);
2358 std::vector<fs::DirListNode>::iterator i2;
2359 for(i2=list2.begin(); i2!=list2.end(); i2++)
2365 loadBlock(sectorsubdir, i2->name, sector);
2367 catch(InvalidFilenameException &e)
2369 // This catches unknown crap in directory
2377 bool ServerMap::deFlushSector(v2s16 p2d)
2379 DSTACK(__FUNCTION_NAME);
2380 // See if it already exists in memory
2382 MapSector *sector = getSectorNoGenerate(p2d);
2385 catch(InvalidPositionException &e)
2388 Try to load the sector from disk.
2390 if(loadSectorFull(p2d) == true)
2399 void ServerMap::saveBlock(MapBlock *block)
2401 DSTACK(__FUNCTION_NAME);
2403 Dummy blocks are not written
2405 if(block->isDummy())
2407 /*v3s16 p = block->getPos();
2408 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2409 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2413 // Format used for writing
2414 u8 version = SER_FMT_VER_HIGHEST;
2416 v3s16 p3d = block->getPos();
2417 v2s16 p2d(p3d.X, p3d.Z);
2418 createDir(m_savedir);
2419 createDir(m_savedir+"/sectors");
2420 std::string dir = getSectorDir(p2d);
2423 // Block file is map/sectors/xxxxxxxx/xxxx
2425 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2426 std::string fullpath = dir + "/" + cc;
2427 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2428 if(o.good() == false)
2429 throw FileNotGoodException("Cannot open block data");
2432 [0] u8 serialization version
2435 o.write((char*)&version, 1);
2437 block->serialize(o, version);
2440 Versions up from 9 have block objects.
2444 block->serializeObjects(o, version);
2447 // We just wrote it to the disk
2448 block->resetChangedFlag();
2451 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2453 DSTACK(__FUNCTION_NAME);
2454 // Block file is map/sectors/xxxxxxxx/xxxx
2455 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2456 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2457 if(is.good() == false)
2458 throw FileNotGoodException("Cannot open block file");
2460 v3s16 p3d = getBlockPos(sectordir, blockfile);
2461 v2s16 p2d(p3d.X, p3d.Z);
2463 assert(sector->getPos() == p2d);
2465 u8 version = SER_FMT_VER_INVALID;
2466 is.read((char*)&version, 1);
2468 /*u32 block_size = MapBlock::serializedLength(version);
2469 SharedBuffer<u8> data(block_size);
2470 is.read((char*)*data, block_size);*/
2472 // This will always return a sector because we're the server
2473 //MapSector *sector = emergeSector(p2d);
2475 MapBlock *block = NULL;
2476 bool created_new = false;
2478 block = sector->getBlockNoCreate(p3d.Y);
2480 catch(InvalidPositionException &e)
2482 block = sector->createBlankBlockNoInsert(p3d.Y);
2486 block->deSerialize(is, version);
2489 Versions up from 9 have block objects.
2493 block->updateObjects(is, version, NULL);
2497 sector->insertBlock(block);
2500 Convert old formats to new and save
2503 // Save old format blocks in new format
2504 if(version < SER_FMT_VER_HIGHEST)
2509 // We just loaded it from the disk, so it's up-to-date.
2510 block->resetChangedFlag();
2513 // Gets from master heightmap
2514 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2516 assert(m_heightmap != NULL);
2524 corners[0] = m_heightmap->getGroundHeight
2525 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2526 corners[1] = m_heightmap->getGroundHeight
2527 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2528 corners[2] = m_heightmap->getGroundHeight
2529 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2530 corners[3] = m_heightmap->getGroundHeight
2531 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2534 void ServerMap::PrintInfo(std::ostream &out)
2543 ClientMap::ClientMap(
2545 video::SMaterial *materials,
2546 scene::ISceneNode* parent,
2547 scene::ISceneManager* mgr,
2551 scene::ISceneNode(parent, mgr, id),
2553 m_materials(materials),
2556 /*m_box = core::aabbox3d<f32>(0,0,0,
2557 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2558 /*m_box = core::aabbox3d<f32>(0,0,0,
2559 map->getSizeNodes().X * BS,
2560 map->getSizeNodes().Y * BS,
2561 map->getSizeNodes().Z * BS);*/
2562 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2563 BS*1000000,BS*1000000,BS*1000000);
2568 ClientMap::~ClientMap()
2570 JMutexAutoLock lock(mesh_mutex);
2579 MapSector * ClientMap::emergeSector(v2s16 p2d)
2581 DSTACK(__FUNCTION_NAME);
2582 // Check that it doesn't exist already
2584 return getSectorNoGenerate(p2d);
2586 catch(InvalidPositionException &e)
2590 // Create a sector with no heightmaps
2591 ClientMapSector *sector = new ClientMapSector(this, p2d);
2594 JMutexAutoLock lock(m_sector_mutex);
2595 m_sectors.insert(p2d, sector);
2601 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2603 DSTACK(__FUNCTION_NAME);
2604 ClientMapSector *sector = NULL;
2606 JMutexAutoLock lock(m_sector_mutex);
2608 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2612 sector = (ClientMapSector*)n->getValue();
2613 assert(sector->getId() == MAPSECTOR_CLIENT);
2617 sector = new ClientMapSector(this, p2d);
2619 JMutexAutoLock lock(m_sector_mutex);
2620 m_sectors.insert(p2d, sector);
2624 sector->deSerialize(is);
2627 void ClientMap::renderMap(video::IVideoDriver* driver,
2628 video::SMaterial *materials, s32 pass)
2630 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2631 DSTACK(__FUNCTION_NAME);
2633 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2636 Draw master heightmap mesh
2640 JMutexAutoLock lock(mesh_mutex);
2643 u32 c = mesh->getMeshBufferCount();
2645 for(u32 i=0; i<c; i++)
2647 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2648 const video::SMaterial& material = buf->getMaterial();
2649 video::IMaterialRenderer* rnd =
2650 driver->getMaterialRenderer(material.MaterialType);
2651 bool transparent = (rnd && rnd->isTransparent());
2652 // Render transparent on transparent pass and likewise.
2653 if(transparent == is_transparent_pass)
2655 driver->setMaterial(buf->getMaterial());
2656 driver->drawMeshBuffer(buf);
2664 Get time for measuring timeout.
2666 Measuring time is very useful for long delays when the
2667 machine is swapping a lot.
2669 int time1 = time(0);
2672 Collect all blocks that are in the view range
2674 Should not optimize more here as we want to auto-update
2675 all changed nodes in viewing range at the next step.
2678 s16 viewing_range_nodes;
2679 bool viewing_range_all;
2681 JMutexAutoLock lock(g_range_mutex);
2682 viewing_range_nodes = g_viewing_range_nodes;
2683 viewing_range_all = g_viewing_range_all;
2686 m_camera_mutex.Lock();
2687 v3f camera_position = m_camera_position;
2688 v3f camera_direction = m_camera_direction;
2689 m_camera_mutex.Unlock();
2692 Get all blocks and draw all visible ones
2695 v3s16 cam_pos_nodes(
2696 camera_position.X / BS,
2697 camera_position.Y / BS,
2698 camera_position.Z / BS);
2700 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2702 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2703 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2705 // Take a fair amount as we will be dropping more out later
2707 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2708 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2709 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2711 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2712 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2713 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2715 u32 vertex_count = 0;
2717 core::map<v2s16, MapSector*>::Iterator si;
2719 //NOTE: The sectors map should be locked but we're not doing it
2720 // because it'd cause too much delays
2722 si = m_sectors.getIterator();
2723 for(; si.atEnd() == false; si++)
2726 static int timecheck_counter = 0;
2727 timecheck_counter++;
2728 if(timecheck_counter > 50)
2730 int time2 = time(0);
2731 if(time2 > time1 + 4)
2733 dstream<<"ClientMap::renderMap(): "
2734 "Rendering takes ages, returning."
2741 MapSector *sector = si.getNode()->getValue();
2742 v2s16 sp = sector->getPos();
2744 if(viewing_range_all == false)
2746 if(sp.X < p_blocks_min.X
2747 || sp.X > p_blocks_max.X
2748 || sp.Y < p_blocks_min.Z
2749 || sp.Y > p_blocks_max.Z)
2753 core::list< MapBlock * > sectorblocks;
2754 sector->getBlocks(sectorblocks);
2760 core::list< MapBlock * >::Iterator i;
2761 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2763 MapBlock *block = *i;
2766 Compare block position to camera position, skip
2767 if not seen on display
2770 v3s16 blockpos_nodes = block->getPosRelative();
2772 // Block center position
2774 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2775 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2776 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2779 // Block position relative to camera
2780 v3f blockpos_relative = blockpos - camera_position;
2782 // Distance in camera direction (+=front, -=back)
2783 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2786 f32 d = blockpos_relative.getLength();
2788 if(viewing_range_all == false)
2790 // If block is far away, don't draw it
2791 if(d > viewing_range_nodes * BS)
2795 // Maximum radius of a block
2796 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2798 // If block is (nearly) touching the camera, don't
2799 // bother validating further (that is, render it anyway)
2800 if(d > block_max_radius * 1.5)
2802 // Cosine of the angle between the camera direction
2803 // and the block direction (camera_direction is an unit vector)
2804 f32 cosangle = dforward / d;
2806 // Compensate for the size of the block
2807 // (as the block has to be shown even if it's a bit off FOV)
2808 // This is an estimate.
2809 cosangle += block_max_radius / dforward;
2811 // If block is not in the field of view, skip it
2812 //if(cosangle < cos(FOV_ANGLE/2))
2813 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2818 Draw the faces of the block
2822 JMutexAutoLock lock(block->mesh_mutex);
2824 // Cancel if block has no mesh
2825 if(block->mesh == NULL)
2828 u32 c = block->mesh->getMeshBufferCount();
2830 for(u32 i=0; i<c; i++)
2832 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2833 const video::SMaterial& material = buf->getMaterial();
2834 video::IMaterialRenderer* rnd =
2835 driver->getMaterialRenderer(material.MaterialType);
2836 bool transparent = (rnd && rnd->isTransparent());
2837 // Render transparent on transparent pass and likewise.
2838 if(transparent == is_transparent_pass)
2840 driver->setMaterial(buf->getMaterial());
2841 driver->drawMeshBuffer(buf);
2842 vertex_count += buf->getVertexCount();
2846 } // foreach sectorblocks
2849 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2850 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2853 void ClientMap::updateMesh()
2856 DSTACK(__FUNCTION_NAME);
2859 Check what sectors don't draw anything useful at ground level
2860 and create a mesh of the rough heightmap at those positions.
2863 m_camera_mutex.Lock();
2864 v3f camera_position = m_camera_position;
2865 v3f camera_direction = m_camera_direction;
2866 m_camera_mutex.Unlock();
2868 v3s16 cam_pos_nodes(
2869 camera_position.X / BS,
2870 camera_position.Y / BS,
2871 camera_position.Z / BS);
2873 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2875 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2876 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2878 // Take a fair amount as we will be dropping more out later
2880 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2881 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2882 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2884 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2885 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2886 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2892 scene::SMesh *mesh_new = new scene::SMesh();
2893 //scene::IMeshBuffer *buf = NULL;
2894 scene::SMeshBuffer *buf = NULL;
2896 u8 material_in_use = 0;
2899 Loop through sectors
2902 for(core::map<v2s16, MapSector*>::Iterator
2903 si = m_sectors.getIterator();
2904 si.atEnd() == false; si++)
2906 MapSector *sector = si.getNode()->getValue();
2908 if(sector->getId() != MAPSECTOR_CLIENT)
2910 dstream<<"WARNING: Client has a non-client sector"
2915 ClientMapSector *cs = (ClientMapSector*)sector;
2917 v2s16 sp = sector->getPos();
2919 if(sp.X < p_blocks_min.X
2920 || sp.X > p_blocks_max.X
2921 || sp.Y < p_blocks_min.Z
2922 || sp.Y > p_blocks_max.Z)
2926 Get some ground level info
2938 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
2940 s16 cn_max = -32768;
2941 for(s16 i=0; i<4; i++)
2948 s16 cn_slope = cn_max - cn_min;
2951 Generate this part of the heightmap mesh
2955 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
2957 else if(cn_slope <= MAP_BLOCKSIZE)
2962 if(material != material_in_use || buf == NULL)
2964 // Try to get a meshbuffer associated with the material
2965 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
2966 (g_mesh_materials[material]);
2967 // If not found, create one
2970 // This is a "Standard MeshBuffer",
2971 // it's a typedeffed CMeshBuffer<video::S3DVertex>
2972 buf = new scene::SMeshBuffer();
2975 buf->Material = g_mesh_materials[material];
2977 //buf->setHardwareMappingHint(scene::EHM_STATIC);
2979 mesh_new->addMeshBuffer(buf);
2983 material_in_use = material;
2986 // Sector side width in floating-point units
2987 f32 sd = BS * MAP_BLOCKSIZE;
2988 // Sector position in global floating-point units
2989 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
2991 //video::SColor c(255,255,255,255);
2993 video::SColor c(255,cc,cc,cc);
2995 video::S3DVertex vertices[4] =
2997 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
2998 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
2999 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3000 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3002 u16 indices[] = {0,1,2,2,3,0};
3004 buf->append(vertices, 4, indices, 6);
3008 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3016 scene::SMesh *mesh_old = mesh;
3023 mesh_mutex.Unlock();
3025 if(mesh_old != NULL)
3027 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3029 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3030 (g_materials[MATERIAL_GRASS]);
3032 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3039 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3044 void ClientMap::PrintInfo(std::ostream &out)