2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
8 #include "jmutexautolock.h"
15 #define sleep_ms(x) Sleep(x)
18 #define sleep_ms(x) usleep(x*1000)
21 Map::Map(std::ostream &dout):
23 m_camera_position(0,0,0),
24 m_camera_direction(0,0,1),
29 m_sector_mutex.Init();
30 m_camera_mutex.Init();
31 assert(m_sector_mutex.IsInitialized());
32 assert(m_camera_mutex.IsInitialized());
34 // Get this so that the player can stay on it at first
35 //getSector(v2s16(0,0));
43 /*updater.setRun(false);
44 while(updater.IsRunning())
50 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
51 for(; i.atEnd() == false; i++)
53 MapSector *sector = i.getNode()->getValue();
58 /*bool Map::sectorExists(v2s16 p)
60 JMutexAutoLock lock(m_sector_mutex);
61 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
65 MapSector * Map::getSectorNoGenerate(v2s16 p)
67 JMutexAutoLock lock(m_sector_mutex);
69 if(m_sector_cache != NULL && p == m_sector_cache_p){
70 MapSector * sector = m_sector_cache;
71 // Reset inactivity timer
72 sector->usage_timer = 0.0;
76 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
77 // If sector doesn't exist, throw an exception
80 throw InvalidPositionException();
83 MapSector *sector = n->getValue();
85 // Cache the last result
87 m_sector_cache = sector;
89 //MapSector * ref(sector);
91 // Reset inactivity timer
92 sector->usage_timer = 0.0;
96 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
98 v2s16 p2d(p3d.X, p3d.Z);
99 MapSector * sector = getSectorNoGenerate(p2d);
101 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
106 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
108 dstream<<"Map::getBlock() with generate=true called"
110 v2s16 p2d(p3d.X, p3d.Z);
111 //MapSector * sector = getSector(p2d, generate);
112 MapSector * sector = getSectorNoGenerate(p2d);
115 throw InvalidPositionException();
117 return sector->getBlockNoCreate(p3d.Y);
120 f32 Map::getGroundHeight(v2s16 p, bool generate)
123 v2s16 sectorpos = getNodeSectorPos(p);
124 MapSector * sref = getSectorNoGenerate(sectorpos);
125 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
126 f32 y = sref->getGroundHeight(relpos);
129 catch(InvalidPositionException &e)
131 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
135 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
137 /*m_dout<<DTIME<<"Map::setGroundHeight(("
139 <<"), "<<y<<")"<<std::endl;*/
140 v2s16 sectorpos = getNodeSectorPos(p);
141 MapSector * sref = getSectorNoGenerate(sectorpos);
142 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
143 //sref->mutex.Lock();
144 sref->setGroundHeight(relpos, y);
145 //sref->mutex.Unlock();
148 bool Map::isNodeUnderground(v3s16 p)
150 v3s16 blockpos = getNodeBlockPos(p);
152 MapBlock * block = getBlockNoCreate(blockpos);
153 return block->getIsUnderground();
155 catch(InvalidPositionException &e)
162 //TODO: Remove: Not used.
164 Goes recursively through the neighbours of the node.
166 Alters only transparent nodes.
168 If the lighting of the neighbour is lower than the lighting of
169 the node was (before changing it to 0 at the step before), the
170 lighting of the neighbour is set to 0 and then the same stuff
171 repeats for the neighbour.
173 Some things are made strangely to make it as fast as possible.
175 Usage: (for clearing all possible spreaded light of a lamp)
176 NOTE: This is outdated
177 core::list<v3s16> light_sources;
178 core::map<v3s16, MapBlock*> modified_blocks;
179 u8 oldlight = node_at_pos.light;
180 node_at_pos.setLight(0);
181 unLightNeighbors(pos, oldlight, light_sources, modified_blocks);
183 void Map::unLightNeighbors(v3s16 pos, u8 oldlight,
184 core::map<v3s16, bool> & light_sources,
185 core::map<v3s16, MapBlock*> & modified_blocks)
188 v3s16(0,0,1), // back
190 v3s16(1,0,0), // right
191 v3s16(0,0,-1), // front
192 v3s16(0,-1,0), // bottom
193 v3s16(-1,0,0), // left
197 Initialize block cache
200 MapBlock *block = NULL;
201 // Cache this a bit, too
202 bool block_checked_in_modified = false;
204 // Loop through 6 neighbors
205 for(u16 i=0; i<6; i++){
206 // Get the position of the neighbor node
207 v3s16 n2pos = pos + dirs[i];
209 // Get the block where the node is located
210 v3s16 blockpos = getNodeBlockPos(n2pos);
212 // Only fetch a new block if the block position has changed
214 if(block == NULL || blockpos != blockpos_last)
216 block = getBlockNoCreate(blockpos);
217 blockpos_last = blockpos;
219 block_checked_in_modified = false;
220 //blockchangecount++;
223 catch(InvalidPositionException &e)
231 // Calculate relative position in block
232 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
233 // Get node straight from the block
234 MapNode n2 = block->getNode(relpos);
237 If the neighbor is dimmer than what was specified
238 as oldlight (the light of the previous node)
240 if(n2.getLight() < oldlight)
243 And the neighbor is transparent and it has some light
245 if(n2.light_propagates() && n2.getLight() != 0)
248 Set light to 0 and recurse.
250 u8 current_light = n2.getLight();
252 block->setNode(relpos, n2);
253 unLightNeighbors(n2pos, current_light,
254 light_sources, modified_blocks);
256 if(block_checked_in_modified == false)
258 // If the block is not found in modified_blocks, add.
259 if(modified_blocks.find(blockpos) == NULL)
261 modified_blocks.insert(blockpos, block);
263 block_checked_in_modified = true;
268 //light_sources.push_back(n2pos);
269 light_sources.insert(n2pos, true);
276 Goes recursively through the neighbours of the node.
278 Alters only transparent nodes.
280 If the lighting of the neighbour is lower than the lighting of
281 the node was (before changing it to 0 at the step before), the
282 lighting of the neighbour is set to 0 and then the same stuff
283 repeats for the neighbour.
285 The ending nodes of the routine are stored in light_sources.
286 This is useful when a light is removed. In such case, this
287 routine can be called for the light node and then again for
288 light_sources to re-light the area without the removed light.
290 values of from_nodes are lighting values.
292 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
293 core::map<v3s16, bool> & light_sources,
294 core::map<v3s16, MapBlock*> & modified_blocks)
297 v3s16(0,0,1), // back
299 v3s16(1,0,0), // right
300 v3s16(0,0,-1), // front
301 v3s16(0,-1,0), // bottom
302 v3s16(-1,0,0), // left
305 if(from_nodes.size() == 0)
308 u32 blockchangecount = 0;
310 core::map<v3s16, u8> unlighted_nodes;
311 core::map<v3s16, u8>::Iterator j;
312 j = from_nodes.getIterator();
315 Initialize block cache
318 MapBlock *block = NULL;
319 // Cache this a bit, too
320 bool block_checked_in_modified = false;
322 for(; j.atEnd() == false; j++)
324 v3s16 pos = j.getNode()->getKey();
325 v3s16 blockpos = getNodeBlockPos(pos);
327 // Only fetch a new block if the block position has changed
329 if(block == NULL || blockpos != blockpos_last){
330 block = getBlockNoCreate(blockpos);
331 blockpos_last = blockpos;
333 block_checked_in_modified = false;
337 catch(InvalidPositionException &e)
345 // Calculate relative position in block
346 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
348 // Get node straight from the block
349 MapNode n = block->getNode(relpos);
351 u8 oldlight = j.getNode()->getValue();
353 // Loop through 6 neighbors
354 for(u16 i=0; i<6; i++)
356 // Get the position of the neighbor node
357 v3s16 n2pos = pos + dirs[i];
359 // Get the block where the node is located
360 v3s16 blockpos = getNodeBlockPos(n2pos);
364 // Only fetch a new block if the block position has changed
366 if(block == NULL || blockpos != blockpos_last){
367 block = getBlockNoCreate(blockpos);
368 blockpos_last = blockpos;
370 block_checked_in_modified = false;
374 catch(InvalidPositionException &e)
379 // Calculate relative position in block
380 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
381 // Get node straight from the block
382 MapNode n2 = block->getNode(relpos);
384 bool changed = false;
386 //TODO: Optimize output by optimizing light_sources?
389 If the neighbor is dimmer than what was specified
390 as oldlight (the light of the previous node)
392 if(n2.getLight() < oldlight)
395 And the neighbor is transparent and it has some light
397 if(n2.light_propagates() && n2.getLight() != 0)
400 Set light to 0 and add to queue
403 u8 current_light = n2.getLight();
405 block->setNode(relpos, n2);
407 unlighted_nodes.insert(n2pos, current_light);
411 Remove from light_sources if it is there
412 NOTE: This doesn't happen nearly at all
414 /*if(light_sources.find(n2pos))
416 std::cout<<"Removed from light_sources"<<std::endl;
417 light_sources.remove(n2pos);
422 light_sources.insert(n2pos, true);
425 // Add to modified_blocks
426 if(changed == true && block_checked_in_modified == false)
428 // If the block is not found in modified_blocks, add.
429 if(modified_blocks.find(blockpos) == NULL)
431 modified_blocks.insert(blockpos, block);
433 block_checked_in_modified = true;
436 catch(InvalidPositionException &e)
443 /*dstream<<"unspreadLight(): Changed block "
444 <<blockchangecount<<" times"
445 <<" for "<<from_nodes.size()<<" nodes"
448 if(unlighted_nodes.size() > 0)
449 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
453 A single-node wrapper of the above
455 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
456 core::map<v3s16, bool> & light_sources,
457 core::map<v3s16, MapBlock*> & modified_blocks)
459 core::map<v3s16, u8> from_nodes;
460 from_nodes.insert(pos, lightwas);
462 unspreadLight(from_nodes, light_sources, modified_blocks);
466 Lights neighbors of from_nodes, collects all them and then
469 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
470 core::map<v3s16, MapBlock*> & modified_blocks)
472 const v3s16 dirs[6] = {
473 v3s16(0,0,1), // back
475 v3s16(1,0,0), // right
476 v3s16(0,0,-1), // front
477 v3s16(0,-1,0), // bottom
478 v3s16(-1,0,0), // left
481 if(from_nodes.size() == 0)
484 u32 blockchangecount = 0;
486 core::map<v3s16, bool> lighted_nodes;
487 core::map<v3s16, bool>::Iterator j;
488 j = from_nodes.getIterator();
491 Initialize block cache
494 MapBlock *block = NULL;
495 // Cache this a bit, too
496 bool block_checked_in_modified = false;
498 for(; j.atEnd() == false; j++)
499 //for(; j != from_nodes.end(); j++)
501 v3s16 pos = j.getNode()->getKey();
503 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
504 v3s16 blockpos = getNodeBlockPos(pos);
506 // Only fetch a new block if the block position has changed
508 if(block == NULL || blockpos != blockpos_last){
509 block = getBlockNoCreate(blockpos);
510 blockpos_last = blockpos;
512 block_checked_in_modified = false;
516 catch(InvalidPositionException &e)
524 // Calculate relative position in block
525 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
527 // Get node straight from the block
528 MapNode n = block->getNode(relpos);
530 u8 oldlight = n.getLight();
531 u8 newlight = diminish_light(oldlight);
533 // Loop through 6 neighbors
534 for(u16 i=0; i<6; i++){
535 // Get the position of the neighbor node
536 v3s16 n2pos = pos + dirs[i];
538 // Get the block where the node is located
539 v3s16 blockpos = getNodeBlockPos(n2pos);
543 // Only fetch a new block if the block position has changed
545 if(block == NULL || blockpos != blockpos_last){
546 block = getBlockNoCreate(blockpos);
547 blockpos_last = blockpos;
549 block_checked_in_modified = false;
553 catch(InvalidPositionException &e)
558 // Calculate relative position in block
559 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
560 // Get node straight from the block
561 MapNode n2 = block->getNode(relpos);
563 bool changed = false;
565 If the neighbor is brighter than the current node,
566 add to list (it will light up this node on its turn)
568 if(n2.getLight() > undiminish_light(oldlight))
570 lighted_nodes.insert(n2pos, true);
571 //lighted_nodes.push_back(n2pos);
575 If the neighbor is dimmer than how much light this node
576 would spread on it, add to list
578 if(n2.getLight() < newlight)
580 if(n2.light_propagates())
582 n2.setLight(newlight);
583 block->setNode(relpos, n2);
584 lighted_nodes.insert(n2pos, true);
585 //lighted_nodes.push_back(n2pos);
590 // Add to modified_blocks
591 if(changed == true && block_checked_in_modified == false)
593 // If the block is not found in modified_blocks, add.
594 if(modified_blocks.find(blockpos) == NULL)
596 modified_blocks.insert(blockpos, block);
598 block_checked_in_modified = true;
601 catch(InvalidPositionException &e)
608 /*dstream<<"spreadLight(): Changed block "
609 <<blockchangecount<<" times"
610 <<" for "<<from_nodes.size()<<" nodes"
613 if(lighted_nodes.size() > 0)
614 spreadLight(lighted_nodes, modified_blocks);
618 A single-node source variation of the above.
620 void Map::lightNeighbors(v3s16 pos,
621 core::map<v3s16, MapBlock*> & modified_blocks)
623 core::map<v3s16, bool> from_nodes;
624 from_nodes.insert(pos, true);
625 spreadLight(from_nodes, modified_blocks);
628 v3s16 Map::getBrightestNeighbour(v3s16 p)
631 v3s16(0,0,1), // back
633 v3s16(1,0,0), // right
634 v3s16(0,0,-1), // front
635 v3s16(0,-1,0), // bottom
636 v3s16(-1,0,0), // left
639 u8 brightest_light = 0;
640 v3s16 brightest_pos(0,0,0);
641 bool found_something = false;
643 // Loop through 6 neighbors
644 for(u16 i=0; i<6; i++){
645 // Get the position of the neighbor node
646 v3s16 n2pos = p + dirs[i];
651 catch(InvalidPositionException &e)
655 if(n2.getLight() > brightest_light || found_something == false){
656 brightest_light = n2.getLight();
657 brightest_pos = n2pos;
658 found_something = true;
662 if(found_something == false)
663 throw InvalidPositionException();
665 return brightest_pos;
669 Propagates sunlight down from a node.
670 Starting point gets sunlight.
672 Returns the lowest y value of where the sunlight went.
674 s16 Map::propagateSunlight(v3s16 start,
675 core::map<v3s16, MapBlock*> & modified_blocks)
680 v3s16 pos(start.X, y, start.Z);
682 v3s16 blockpos = getNodeBlockPos(pos);
685 block = getBlockNoCreate(blockpos);
687 catch(InvalidPositionException &e)
692 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
693 MapNode n = block->getNode(relpos);
695 if(n.sunlight_propagates())
697 n.setLight(LIGHT_SUN);
698 block->setNode(relpos, n);
700 modified_blocks.insert(blockpos, block);
709 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
710 core::map<v3s16, MapBlock*> & modified_blocks)
712 /*m_dout<<DTIME<<"Map::updateLighting(): "
713 <<a_blocks.getSize()<<" blocks... ";*/
717 u32 count_was = modified_blocks.size();
719 /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
720 for(; i != a_blocks.end(); i++)
722 MapBlock *block = *i;*/
724 core::map<v3s16, bool> light_sources;
726 core::map<v3s16, u8> unlight_from;
728 core::map<v3s16, MapBlock*>::Iterator i;
729 i = a_blocks.getIterator();
730 for(; i.atEnd() == false; i++)
732 MapBlock *block = i.getNode()->getValue();
736 // Don't bother with dummy blocks.
740 v3s16 pos = block->getPos();
741 modified_blocks.insert(pos, block);
744 Clear all light from block
746 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
747 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
748 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
753 MapNode n = block->getNode(v3s16(x,y,z));
754 u8 oldlight = n.getLight();
756 block->setNode(v3s16(x,y,z), n);
758 // Collect borders for unlighting
759 if(x==0 || x == MAP_BLOCKSIZE-1
760 || y==0 || y == MAP_BLOCKSIZE-1
761 || z==0 || z == MAP_BLOCKSIZE-1)
763 v3s16 p_map = p + v3s16(
766 MAP_BLOCKSIZE*pos.Z);
767 unlight_from.insert(p_map, oldlight);
770 catch(InvalidPositionException &e)
773 This would happen when dealing with a
777 dstream<<"updateLighting(): InvalidPositionException"
782 bool bottom_valid = block->propagateSunlight(light_sources);
784 // If bottom is valid, we're done.
788 /*dstream<<"Bottom for sunlight-propagated block ("
789 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
792 // Else get the block below and loop to it
796 block = getBlockNoCreate(pos);
798 catch(InvalidPositionException &e)
807 //TimeTaker timer("unspreadLight", g_device);
808 unspreadLight(unlight_from, light_sources, modified_blocks);
813 u32 diff = modified_blocks.size() - count_was;
814 count_was = modified_blocks.size();
815 dstream<<"unspreadLight modified "<<diff<<std::endl;
818 // TODO: Spread light from propagated sunlight?
819 // Yes, add it to light_sources... somehow.
820 // It has to be added at somewhere above, in the loop.
824 //TimeTaker timer("spreadLight", g_device);
825 spreadLight(light_sources, modified_blocks);
830 u32 diff = modified_blocks.size() - count_was;
831 count_was = modified_blocks.size();
832 dstream<<"spreadLight modified "<<diff<<std::endl;
835 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
839 This is called after changing a node from transparent to opaque.
840 The lighting value of the node should be left as-is after changing
841 other values. This sets the lighting value to 0.
843 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
844 core::map<v3s16, MapBlock*> &modified_blocks)*/
845 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
846 core::map<v3s16, MapBlock*> &modified_blocks)
849 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
850 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
852 u8 lightwas = getNode(p).getLight();
854 //core::list<v3s16> light_sources;
855 core::map<v3s16, bool> light_sources;
856 //MapNode n = getNode(p);
859 From this node to nodes underneath:
860 If lighting is sunlight (1.0), unlight neighbours and
865 bool node_under_sunlight = true;
867 v3s16 toppos = p + v3s16(0,1,0);
870 If there is a node at top and it doesn't have sunlight,
871 there has not been any sunlight going down.
873 Otherwise there probably is.
876 MapNode topnode = getNode(toppos);
878 if(topnode.getLight() != LIGHT_SUN)
879 node_under_sunlight = false;
881 catch(InvalidPositionException &e)
885 // Add the block of the added node to modified_blocks
886 v3s16 blockpos = getNodeBlockPos(p);
887 MapBlock * block = getBlockNoCreate(blockpos);
888 assert(block != NULL);
889 modified_blocks.insert(blockpos, block);
891 if(isValidPosition(p) == false)
894 // Unlight neighbours of node.
895 // This means setting light of all consequent dimmer nodes
897 // This also collects the nodes at the border which will spread
898 // light again into this.
899 unLightNeighbors(p, lightwas, light_sources, modified_blocks);
905 If node is under sunlight, take all sunlighted nodes under
906 it and clear light from them and from where the light has
909 if(node_under_sunlight)
913 //m_dout<<DTIME<<"y="<<y<<std::endl;
914 v3s16 n2pos(p.X, y, p.Z);
920 catch(InvalidPositionException &e)
925 if(n2.getLight() == LIGHT_SUN)
927 //m_dout<<DTIME<<"doing"<<std::endl;
928 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
938 Spread light from all nodes that might be capable of doing so
939 TODO: Convert to spreadLight
941 spreadLight(light_sources, modified_blocks);
946 void Map::removeNodeAndUpdate(v3s16 p,
947 core::map<v3s16, MapBlock*> &modified_blocks)
950 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
951 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
953 bool node_under_sunlight = true;
955 v3s16 toppos = p + v3s16(0,1,0);
958 If there is a node at top and it doesn't have sunlight,
959 there will be no sunlight going down.
962 MapNode topnode = getNode(toppos);
964 if(topnode.getLight() != LIGHT_SUN)
965 node_under_sunlight = false;
967 catch(InvalidPositionException &e)
972 Unlight neighbors (in case the node is a light source)
974 //core::list<v3s16> light_sources;
975 core::map<v3s16, bool> light_sources;
976 unLightNeighbors(p, getNode(p).getLight(),
977 light_sources, modified_blocks);
990 spreadLight(light_sources, modified_blocks);
992 // Add the block of the removed node to modified_blocks
993 v3s16 blockpos = getNodeBlockPos(p);
994 MapBlock * block = getBlockNoCreate(blockpos);
995 assert(block != NULL);
996 modified_blocks.insert(blockpos, block);
999 If the removed node was under sunlight, propagate the
1000 sunlight down from it and then light all neighbors
1001 of the propagated blocks.
1003 if(node_under_sunlight)
1005 s16 ybottom = propagateSunlight(p, modified_blocks);
1006 /*m_dout<<DTIME<<"Node was under sunlight. "
1007 "Propagating sunlight";
1008 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1010 for(; y >= ybottom; y--)
1012 v3s16 p2(p.X, y, p.Z);
1013 /*m_dout<<DTIME<<"lighting neighbors of node ("
1014 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1016 lightNeighbors(p2, modified_blocks);
1021 // Set the lighting of this node to 0
1023 MapNode n = getNode(p);
1027 catch(InvalidPositionException &e)
1033 // Get the brightest neighbour node and propagate light from it
1034 v3s16 n2p = getBrightestNeighbour(p);
1036 MapNode n2 = getNode(n2p);
1037 lightNeighbors(n2p, modified_blocks);
1039 catch(InvalidPositionException &e)
1044 void Map::updateMeshes(v3s16 blockpos)
1046 assert(mapType() == MAPTYPE_CLIENT);
1049 v3s16 p = blockpos + v3s16(0,0,0);
1050 MapBlock *b = getBlockNoCreate(p);
1053 catch(InvalidPositionException &e){}
1055 v3s16 p = blockpos + v3s16(-1,0,0);
1056 MapBlock *b = getBlockNoCreate(p);
1059 catch(InvalidPositionException &e){}
1061 v3s16 p = blockpos + v3s16(0,-1,0);
1062 MapBlock *b = getBlockNoCreate(p);
1065 catch(InvalidPositionException &e){}
1067 v3s16 p = blockpos + v3s16(0,0,-1);
1068 MapBlock *b = getBlockNoCreate(p);
1071 catch(InvalidPositionException &e){}
1075 Updates usage timers
1077 void Map::timerUpdate(float dtime)
1079 JMutexAutoLock lock(m_sector_mutex);
1081 core::map<v2s16, MapSector*>::Iterator si;
1083 si = m_sectors.getIterator();
1084 for(; si.atEnd() == false; si++)
1086 MapSector *sector = si.getNode()->getValue();
1087 sector->usage_timer += dtime;
1091 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1093 core::list<v2s16>::Iterator j;
1094 for(j=list.begin(); j!=list.end(); j++)
1096 MapSector *sector = m_sectors[*j];
1099 sector->deleteBlocks();
1104 If sector is in sector cache, remove it from there
1106 if(m_sector_cache == sector)
1108 m_sector_cache = NULL;
1111 Remove from map and delete
1113 m_sectors.remove(*j);
1119 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1120 core::list<v3s16> *deleted_blocks)
1122 JMutexAutoLock lock(m_sector_mutex);
1124 core::list<v2s16> sector_deletion_queue;
1125 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1126 for(; i.atEnd() == false; i++)
1128 MapSector *sector = i.getNode()->getValue();
1130 Delete sector from memory if it hasn't been used in a long time
1132 if(sector->usage_timer > timeout)
1134 sector_deletion_queue.push_back(i.getNode()->getKey());
1136 if(deleted_blocks != NULL)
1138 // Collect positions of blocks of sector
1139 MapSector *sector = i.getNode()->getValue();
1140 core::list<MapBlock*> blocks;
1141 sector->getBlocks(blocks);
1142 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1143 i != blocks.end(); i++)
1145 deleted_blocks->push_back((*i)->getPos());
1150 deleteSectors(sector_deletion_queue, only_blocks);
1151 return sector_deletion_queue.getSize();
1154 void Map::PrintInfo(std::ostream &out)
1163 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1167 m_savedir = savedir;
1168 m_map_saving_enabled = false;
1172 // If directory exists, check contents and load if possible
1173 if(fs::PathExists(m_savedir))
1175 // If directory is empty, it is safe to save into it.
1176 if(fs::GetDirListing(m_savedir).size() == 0)
1178 dstream<<DTIME<<"Server: Empty save directory is valid."
1180 m_map_saving_enabled = true;
1184 // Load master heightmap
1185 loadMasterHeightmap();
1187 // Load sector (0,0) and throw and exception on fail
1188 if(loadSectorFull(v2s16(0,0)) == false)
1189 throw LoadError("Failed to load sector (0,0)");
1191 dstream<<DTIME<<"Server: Successfully loaded master "
1192 "heightmap and sector (0,0) from "<<savedir<<
1193 ", assuming valid save directory."
1196 m_map_saving_enabled = true;
1197 // Map loaded, not creating new one
1201 // If directory doesn't exist, it is safe to save to it
1203 m_map_saving_enabled = true;
1206 catch(std::exception &e)
1208 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1209 <<", exception: "<<e.what()<<std::endl;
1210 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1211 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1214 dstream<<DTIME<<"Initializing new map."<<std::endl;
1216 // Create master heightmap
1217 ValueGenerator *maxgen =
1218 ValueGenerator::deSerialize(hmp.height_randmax);
1219 ValueGenerator *factorgen =
1220 ValueGenerator::deSerialize(hmp.height_randfactor);
1221 ValueGenerator *basegen =
1222 ValueGenerator::deSerialize(hmp.height_base);
1223 m_heightmap = new UnlimitedHeightmap
1224 (hmp.heightmap_blocksize, maxgen, factorgen, basegen);
1226 // Set map parameters
1229 // Create zero sector
1230 emergeSector(v2s16(0,0));
1232 // Initially write whole map
1236 ServerMap::~ServerMap()
1240 if(m_map_saving_enabled)
1243 // Save only changed parts
1245 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1249 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1252 catch(std::exception &e)
1254 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1255 <<", exception: "<<e.what()<<std::endl;
1258 if(m_heightmap != NULL)
1262 MapSector * ServerMap::emergeSector(v2s16 p2d)
1264 DSTACK("%s: p2d=(%d,%d)",
1267 // Check that it doesn't exist already
1269 return getSectorNoGenerate(p2d);
1271 catch(InvalidPositionException &e)
1276 Try to load the sector from disk.
1278 if(loadSectorFull(p2d) == true)
1280 return getSectorNoGenerate(p2d);
1284 If there is no master heightmap, throw.
1286 if(m_heightmap == NULL)
1288 throw InvalidPositionException("emergeSector(): no heightmap");
1292 Do not generate over-limit
1294 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1295 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1296 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1297 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1298 throw InvalidPositionException("emergeSector(): pos. over limit");
1301 Generate sector and heightmaps
1304 // Number of heightmaps in sector in each direction
1305 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1307 // Heightmap side width
1308 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1310 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1312 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1313 " heightmaps and objects"<<std::endl;*/
1315 // Loop through sub-heightmaps
1316 for(s16 y=0; y<hm_split; y++)
1317 for(s16 x=0; x<hm_split; x++)
1319 v2s16 p_in_sector = v2s16(x,y);
1320 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1322 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1323 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1324 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1325 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1328 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1329 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1332 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1334 sector->setHeightmap(p_in_sector, hm);
1336 //TODO: Make these values configurable
1337 hm->generateContinued(1.0, 0.2, corners);
1338 //hm->generateContinued(2.0, 0.2, corners);
1348 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1349 sector->setObjects(objects);
1351 v2s16 mhm_p = p2d * hm_split;
1353 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1354 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1355 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1356 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1359 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1360 float avgslope = 0.0;
1361 avgslope += fabs(avgheight - corners[0]);
1362 avgslope += fabs(avgheight - corners[1]);
1363 avgslope += fabs(avgheight - corners[2]);
1364 avgslope += fabs(avgheight - corners[3]);
1366 avgslope /= MAP_BLOCKSIZE;
1367 //dstream<<"avgslope="<<avgslope<<std::endl;
1369 float pitness = 0.0;
1371 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1374 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1377 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1380 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1384 pitness /= MAP_BLOCKSIZE;
1385 //dstream<<"pitness="<<pitness<<std::endl;
1388 Plant some trees if there is not much slope
1391 // Avgslope is the derivative of a hill
1392 float t = avgslope * avgslope;
1393 float a = MAP_BLOCKSIZE * 2 * m_params.plants_amount;
1396 tree_max = a / (t/0.03);
1399 u32 count = (rand()%(tree_max+1));
1400 //u32 count = tree_max;
1401 for(u32 i=0; i<count; i++)
1403 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1404 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1405 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1408 objects->insert(v3s16(x, y, z),
1409 SECTOR_OBJECT_TREE_1);
1413 // Pitness usually goes at around -0.5...0.5
1415 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1417 bush_max = (pitness*a*4);
1420 u32 count = (rand()%(bush_max+1));
1421 for(u32 i=0; i<count; i++)
1423 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1424 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1425 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1428 objects->insert(v3s16(x, y, z),
1429 SECTOR_OBJECT_BUSH_1);
1436 JMutexAutoLock lock(m_sector_mutex);
1437 m_sectors.insert(p2d, sector);
1442 MapBlock * ServerMap::emergeBlock(
1444 bool only_from_disk,
1445 core::map<v3s16, MapBlock*> &changed_blocks,
1446 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1449 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1451 p.X, p.Y, p.Z, only_from_disk);
1453 /*dstream<<"ServerMap::emergeBlock(): "
1454 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1455 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1456 v2s16 p2d(p.X, p.Z);
1459 This will create or load a sector if not found in memory.
1460 If block exists on disk, it will be loaded.
1462 NOTE: On old save formats, this will be slow, as it generates
1463 lighting on blocks for them.
1465 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1466 assert(sector->getId() == MAPSECTOR_SERVER);
1468 // Try to get a block from the sector
1469 MapBlock *block = NULL;
1470 bool not_on_disk = false;
1472 block = sector->getBlockNoCreate(block_y);
1473 if(block->isDummy() == true)
1478 catch(InvalidPositionException &e)
1484 If block was not found on disk and not going to generate a
1485 new one, make sure there is a dummy block in place.
1487 if(not_on_disk && only_from_disk)
1491 // Create dummy block
1492 block = new MapBlock(this, p, true);
1494 // Add block to sector
1495 sector->insertBlock(block);
1501 //dstream<<"Not found on disk, generating."<<std::endl;
1504 Do not generate over-limit
1506 if(blockpos_over_limit(p))
1507 throw InvalidPositionException("emergeBlock(): pos. over limit");
1512 Go on generating the block.
1514 TODO: If a dungeon gets generated so that it's side gets
1515 revealed to the outside air, the lighting should be
1520 If block doesn't exist, create one.
1521 If it exists, it is a dummy. In that case unDummify() it.
1525 block = sector->createBlankBlockNoInsert(block_y);
1529 // Remove the block so that nobody can get a half-generated one.
1530 sector->removeBlock(block);
1531 // Allocate the block to be a proper one.
1535 // Randomize a bit. This makes dungeons.
1536 bool low_block_is_empty = false;
1538 low_block_is_empty = true;
1540 // This is the basic material of what the visible flat ground
1542 u8 material = MATERIAL_GRASS;
1544 s32 lowest_ground_y = 32767;
1547 //sector->printHeightmaps();
1549 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1550 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1552 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1553 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1555 assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1557 s16 surface_y = surface_y_f;
1558 //avg_ground_y += surface_y;
1559 if(surface_y < lowest_ground_y)
1560 lowest_ground_y = surface_y;
1562 s32 surface_depth = 0;
1564 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1566 float min_slope = 0.45;
1567 float max_slope = 0.85;
1568 float min_slope_depth = 5.0;
1569 float max_slope_depth = 0;
1570 if(slope < min_slope)
1571 surface_depth = min_slope_depth;
1572 else if(slope > max_slope)
1573 surface_depth = max_slope_depth;
1575 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1577 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++){
1578 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1583 FIXME: If there are some man-made structures above the
1584 newly created block, they won't be taken into account.
1586 if(real_y > surface_y)
1587 n.setLight(LIGHT_SUN);
1591 // If node is very low
1592 if(real_y <= surface_y - 10){
1594 if(low_block_is_empty){
1598 n.d = MATERIAL_STONE;
1601 // If node is under surface level
1602 else if(real_y <= surface_y - surface_depth)
1603 n.d = MATERIAL_STONE;
1604 // If node is at or under heightmap y
1605 else if(real_y <= surface_y)
1607 // If node is over heightmap y
1609 // If under water level, it's water
1610 if(real_y < WATER_LEVEL)
1612 n.d = MATERIAL_WATER;
1613 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1619 block->setNode(v3s16(x0,y0,z0), n);
1624 Calculate is_underground
1626 // Probably underground if the highest part of block is under lowest
1628 bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1629 block->setIsUnderground(is_underground);
1635 if(is_underground && low_block_is_empty == false)
1637 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1638 for(s16 i=0; i<underground_level*3; i++)
1643 /*(rand()%(MAP_BLOCKSIZE-4))+2,
1644 (rand()%(MAP_BLOCKSIZE-4))+2,
1645 (rand()%(MAP_BLOCKSIZE-4))+2*/
1646 (rand()%(MAP_BLOCKSIZE-2))+1,
1647 (rand()%(MAP_BLOCKSIZE-2))+1,
1648 (rand()%(MAP_BLOCKSIZE-2))+1
1652 n.d = MATERIAL_MESE;
1655 block->setNode(cp, n);
1657 for(u16 i=0; i<26; i++)
1660 block->setNode(cp+g_26dirs[i], n);
1667 Create a few rats in empty blocks underground
1669 if(is_underground && low_block_is_empty == true)
1671 //for(u16 i=0; i<2; i++)
1674 RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1675 block->addObject(obj);
1682 Add some objects to the block for testing.
1684 /*if(p == v3s16(0,0,0))
1686 //TestObject *obj = new TestObject(NULL, -1, v3f(BS*8,BS*8,BS*8));
1687 Test2Object *obj = new Test2Object(NULL, -1, v3f(BS*8,BS*15,BS*8));
1688 block->addObject(obj);
1693 v3s16 pos(8, 11, 8);
1694 SignObject *obj = new SignObject(NULL, -1, intToFloat(pos));
1695 obj->setText("Moicka");
1697 block->addObject(obj);
1701 v3s16 pos(8, 11, 8);
1702 RatObject *obj = new RatObject(NULL, -1, intToFloat(pos));
1703 block->addObject(obj);
1708 Add block to sector.
1710 sector->insertBlock(block);
1712 // An y-wise container if changed blocks
1713 core::map<s16, MapBlock*> changed_blocks_sector;
1716 Check if any sector's objects can be placed now.
1719 core::map<v3s16, u8> *objects = sector->getObjects();
1720 core::list<v3s16> objects_to_remove;
1721 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1722 i.atEnd() == false; i++)
1724 v3s16 p = i.getNode()->getKey();
1725 u8 d = i.getNode()->getValue();
1727 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1732 if(d == SECTOR_OBJECT_TEST)
1734 if(sector->isValidArea(p + v3s16(0,0,0),
1735 p + v3s16(0,0,0), &changed_blocks_sector))
1738 n.d = MATERIAL_LIGHT;
1739 sector->setNode(p, n);
1740 objects_to_remove.push_back(p);
1743 else if(d == SECTOR_OBJECT_TREE_1)
1745 v3s16 p_min = p + v3s16(-1,0,-1);
1746 v3s16 p_max = p + v3s16(1,4,1);
1747 if(sector->isValidArea(p_min, p_max,
1748 &changed_blocks_sector))
1751 n.d = MATERIAL_TREE;
1752 sector->setNode(p+v3s16(0,0,0), n);
1753 sector->setNode(p+v3s16(0,1,0), n);
1754 sector->setNode(p+v3s16(0,2,0), n);
1755 sector->setNode(p+v3s16(0,3,0), n);
1757 n.d = MATERIAL_LEAVES;
1759 sector->setNode(p+v3s16(0,4,0), n);
1761 sector->setNode(p+v3s16(-1,4,0), n);
1762 sector->setNode(p+v3s16(1,4,0), n);
1763 sector->setNode(p+v3s16(0,4,-1), n);
1764 sector->setNode(p+v3s16(0,4,1), n);
1765 sector->setNode(p+v3s16(1,4,1), n);
1766 sector->setNode(p+v3s16(-1,4,1), n);
1767 sector->setNode(p+v3s16(-1,4,-1), n);
1768 sector->setNode(p+v3s16(1,4,-1), n);
1770 sector->setNode(p+v3s16(-1,3,0), n);
1771 sector->setNode(p+v3s16(1,3,0), n);
1772 sector->setNode(p+v3s16(0,3,-1), n);
1773 sector->setNode(p+v3s16(0,3,1), n);
1774 sector->setNode(p+v3s16(1,3,1), n);
1775 sector->setNode(p+v3s16(-1,3,1), n);
1776 sector->setNode(p+v3s16(-1,3,-1), n);
1777 sector->setNode(p+v3s16(1,3,-1), n);
1779 objects_to_remove.push_back(p);
1781 // Lighting has to be recalculated for this one.
1782 sector->getBlocksInArea(p_min, p_max,
1783 lighting_invalidated_blocks);
1786 else if(d == SECTOR_OBJECT_BUSH_1)
1788 if(sector->isValidArea(p + v3s16(0,0,0),
1789 p + v3s16(0,0,0), &changed_blocks_sector))
1792 n.d = MATERIAL_LEAVES;
1793 sector->setNode(p+v3s16(0,0,0), n);
1795 objects_to_remove.push_back(p);
1800 dstream<<"ServerMap::emergeBlock(): "
1801 "Invalid heightmap object"
1806 catch(InvalidPositionException &e)
1808 dstream<<"WARNING: "<<__FUNCTION_NAME
1809 <<": while inserting object "<<(int)d
1810 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1811 <<" InvalidPositionException.what()="
1812 <<e.what()<<std::endl;
1813 // This is not too fatal and seems to happen sometimes.
1818 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
1819 i != objects_to_remove.end(); i++)
1821 objects->remove(*i);
1824 for(core::map<s16, MapBlock*>::Iterator
1825 i = changed_blocks_sector.getIterator();
1826 i.atEnd() == false; i++)
1828 MapBlock *block = i.getNode()->getValue();
1830 changed_blocks.insert(block->getPos(), block);
1836 void ServerMap::createDir(std::string path)
1838 if(fs::CreateDir(path) == false)
1840 m_dout<<DTIME<<"ServerMap: Failed to create directory "
1841 <<"\""<<path<<"\""<<std::endl;
1842 throw BaseException("ServerMap failed to create directory");
1846 std::string ServerMap::getSectorSubDir(v2s16 pos)
1849 snprintf(cc, 9, "%.4x%.4x",
1850 (unsigned int)pos.X&0xffff,
1851 (unsigned int)pos.Y&0xffff);
1853 return std::string(cc);
1856 std::string ServerMap::getSectorDir(v2s16 pos)
1858 return m_savedir + "/sectors/" + getSectorSubDir(pos);
1861 v2s16 ServerMap::getSectorPos(std::string dirname)
1863 if(dirname.size() != 8)
1864 throw InvalidFilenameException("Invalid sector directory name");
1866 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
1868 throw InvalidFilenameException("Invalid sector directory name");
1869 v2s16 pos((s16)x, (s16)y);
1873 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
1875 v2s16 p2d = getSectorPos(sectordir);
1877 if(blockfile.size() != 4){
1878 throw InvalidFilenameException("Invalid block filename");
1881 int r = sscanf(blockfile.c_str(), "%4x", &y);
1883 throw InvalidFilenameException("Invalid block filename");
1884 return v3s16(p2d.X, y, p2d.Y);
1888 #define ENABLE_SECTOR_SAVING 1
1889 #define ENABLE_SECTOR_LOADING 1
1890 #define ENABLE_BLOCK_SAVING 1
1891 #define ENABLE_BLOCK_LOADING 1
1893 void ServerMap::save(bool only_changed)
1895 DSTACK(__FUNCTION_NAME);
1896 if(m_map_saving_enabled == false)
1898 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
1902 if(only_changed == false)
1903 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
1906 saveMasterHeightmap();
1908 u32 sector_meta_count = 0;
1909 u32 block_count = 0;
1912 JMutexAutoLock lock(m_sector_mutex);
1914 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1915 for(; i.atEnd() == false; i++)
1917 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
1918 assert(sector->getId() == MAPSECTOR_SERVER);
1920 if(ENABLE_SECTOR_SAVING)
1922 if(sector->differs_from_disk || only_changed == false)
1924 saveSectorMeta(sector);
1925 sector_meta_count++;
1928 if(ENABLE_BLOCK_SAVING)
1930 core::list<MapBlock*> blocks;
1931 sector->getBlocks(blocks);
1932 core::list<MapBlock*>::Iterator j;
1933 for(j=blocks.begin(); j!=blocks.end(); j++)
1935 MapBlock *block = *j;
1936 if(block->getChangedFlag() || only_changed == false)
1947 u32 deleted_count = 0;
1948 deleted_count = deleteUnusedSectors
1949 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
1952 Only print if something happened or saved whole map
1954 if(only_changed == false || sector_meta_count != 0
1955 || block_count != 0 || deleted_count != 0)
1957 dstream<<DTIME<<"ServerMap: Written: "
1958 <<sector_meta_count<<" sector metadata files, "
1959 <<block_count<<" block files, "
1960 <<deleted_count<<" sectors unloaded from memory."
1965 void ServerMap::loadAll()
1967 DSTACK(__FUNCTION_NAME);
1968 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
1970 loadMasterHeightmap();
1972 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
1974 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
1976 JMutexAutoLock lock(m_sector_mutex);
1979 s32 printed_counter = -100000;
1980 s32 count = list.size();
1982 std::vector<fs::DirListNode>::iterator i;
1983 for(i=list.begin(); i!=list.end(); i++)
1985 if(counter > printed_counter + 10)
1987 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
1988 printed_counter = counter;
1992 MapSector *sector = NULL;
1994 // We want directories
1998 sector = loadSectorMeta(i->name);
2000 catch(InvalidFilenameException &e)
2002 // This catches unknown crap in directory
2005 if(ENABLE_BLOCK_LOADING)
2007 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2008 (m_savedir+"/sectors/"+i->name);
2009 std::vector<fs::DirListNode>::iterator i2;
2010 for(i2=list2.begin(); i2!=list2.end(); i2++)
2016 loadBlock(i->name, i2->name, sector);
2018 catch(InvalidFilenameException &e)
2020 // This catches unknown crap in directory
2025 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2028 void ServerMap::saveMasterHeightmap()
2030 DSTACK(__FUNCTION_NAME);
2031 createDir(m_savedir);
2033 std::string fullpath = m_savedir + "/master_heightmap";
2034 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2035 if(o.good() == false)
2036 throw FileNotGoodException("Cannot open master heightmap");
2038 // Format used for writing
2039 u8 version = SER_FMT_VER_HIGHEST;
2042 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2044 [0] u8 serialization version
2045 [1] X master heightmap
2047 u32 fullsize = 1 + hmdata.getSize();
2048 SharedBuffer<u8> data(fullsize);
2051 memcpy(&data[1], *hmdata, hmdata.getSize());
2053 o.write((const char*)*data, fullsize);
2056 m_heightmap->serialize(o, version);
2059 void ServerMap::loadMasterHeightmap()
2061 DSTACK(__FUNCTION_NAME);
2062 std::string fullpath = m_savedir + "/master_heightmap";
2063 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2064 if(is.good() == false)
2065 throw FileNotGoodException("Cannot open master heightmap");
2067 if(m_heightmap != NULL)
2070 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2073 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2075 DSTACK(__FUNCTION_NAME);
2076 // Format used for writing
2077 u8 version = SER_FMT_VER_HIGHEST;
2079 v2s16 pos = sector->getPos();
2080 createDir(m_savedir);
2081 createDir(m_savedir+"/sectors");
2082 std::string dir = getSectorDir(pos);
2085 std::string fullpath = dir + "/heightmap";
2086 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2087 if(o.good() == false)
2088 throw FileNotGoodException("Cannot open master heightmap");
2090 sector->serialize(o, version);
2092 sector->differs_from_disk = false;
2095 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2097 DSTACK(__FUNCTION_NAME);
2099 v2s16 p2d = getSectorPos(dirname);
2100 std::string dir = m_savedir + "/sectors/" + dirname;
2102 std::string fullpath = dir + "/heightmap";
2103 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2104 if(is.good() == false)
2105 throw FileNotGoodException("Cannot open sector heightmap");
2107 ServerMapSector *sector = ServerMapSector::deSerialize
2108 (is, this, p2d, &m_hwrapper, m_sectors);
2110 sector->differs_from_disk = false;
2115 bool ServerMap::loadSectorFull(v2s16 p2d)
2117 DSTACK(__FUNCTION_NAME);
2118 std::string sectorsubdir = getSectorSubDir(p2d);
2120 MapSector *sector = NULL;
2122 JMutexAutoLock lock(m_sector_mutex);
2125 sector = loadSectorMeta(sectorsubdir);
2127 catch(InvalidFilenameException &e)
2131 catch(FileNotGoodException &e)
2135 catch(std::exception &e)
2140 if(ENABLE_BLOCK_LOADING)
2142 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2143 (m_savedir+"/sectors/"+sectorsubdir);
2144 std::vector<fs::DirListNode>::iterator i2;
2145 for(i2=list2.begin(); i2!=list2.end(); i2++)
2151 loadBlock(sectorsubdir, i2->name, sector);
2153 catch(InvalidFilenameException &e)
2155 // This catches unknown crap in directory
2163 bool ServerMap::deFlushSector(v2s16 p2d)
2165 DSTACK(__FUNCTION_NAME);
2166 // See if it already exists in memory
2168 MapSector *sector = getSectorNoGenerate(p2d);
2171 catch(InvalidPositionException &e)
2174 Try to load the sector from disk.
2176 if(loadSectorFull(p2d) == true)
2185 void ServerMap::saveBlock(MapBlock *block)
2187 DSTACK(__FUNCTION_NAME);
2189 Dummy blocks are not written
2191 if(block->isDummy())
2193 /*v3s16 p = block->getPos();
2194 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2195 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2199 // Format used for writing
2200 u8 version = SER_FMT_VER_HIGHEST;
2202 v3s16 p3d = block->getPos();
2203 v2s16 p2d(p3d.X, p3d.Z);
2204 createDir(m_savedir);
2205 createDir(m_savedir+"/sectors");
2206 std::string dir = getSectorDir(p2d);
2209 // Block file is map/sectors/xxxxxxxx/xxxx
2211 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2212 std::string fullpath = dir + "/" + cc;
2213 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2214 if(o.good() == false)
2215 throw FileNotGoodException("Cannot open block data");
2218 [0] u8 serialization version
2221 o.write((char*)&version, 1);
2223 block->serialize(o, version);
2226 Versions up from 9 have block objects.
2230 block->serializeObjects(o, version);
2233 // We just wrote it to the disk
2234 block->resetChangedFlag();
2237 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2239 DSTACK(__FUNCTION_NAME);
2240 // Block file is map/sectors/xxxxxxxx/xxxx
2241 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2242 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2243 if(is.good() == false)
2244 throw FileNotGoodException("Cannot open block file");
2246 v3s16 p3d = getBlockPos(sectordir, blockfile);
2247 v2s16 p2d(p3d.X, p3d.Z);
2249 assert(sector->getPos() == p2d);
2251 u8 version = SER_FMT_VER_INVALID;
2252 is.read((char*)&version, 1);
2254 /*u32 block_size = MapBlock::serializedLength(version);
2255 SharedBuffer<u8> data(block_size);
2256 is.read((char*)*data, block_size);*/
2258 // This will always return a sector because we're the server
2259 //MapSector *sector = emergeSector(p2d);
2261 MapBlock *block = NULL;
2262 bool created_new = false;
2264 block = sector->getBlockNoCreate(p3d.Y);
2266 catch(InvalidPositionException &e)
2268 block = sector->createBlankBlockNoInsert(p3d.Y);
2272 block->deSerialize(is, version);
2275 Versions up from 9 have block objects.
2279 block->updateObjects(is, version, NULL);
2283 sector->insertBlock(block);
2286 Convert old formats to new and save
2289 if(version == 0 || version == 1)
2291 dstream<<"Block ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
2292 " is in old format. Updating lighting and saving"
2293 " modified blocks in new format."<<std::endl;
2295 // Old version has zero lighting, update it
2296 core::map<v3s16, MapBlock*> blocks_changed;
2297 blocks_changed.insert(block->getPos(), block);
2298 core::map<v3s16, MapBlock*> modified_blocks;
2299 updateLighting(blocks_changed, modified_blocks);
2304 // Save modified blocks
2305 core::map<v3s16, MapBlock * >::Iterator i = modified_blocks.getIterator();
2306 for(; i.atEnd() == false; i++)
2308 MapBlock *b2 = i.getNode()->getValue();
2312 // Save blocks in new format
2313 else if(version < SER_FMT_VER_HIGHEST)
2318 // We just loaded it from the disk, so it's up-to-date.
2319 block->resetChangedFlag();
2322 // Gets from master heightmap
2323 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2325 assert(m_heightmap != NULL);
2333 corners[0] = m_heightmap->getGroundHeight
2334 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2335 corners[1] = m_heightmap->getGroundHeight
2336 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2337 corners[2] = m_heightmap->getGroundHeight
2338 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2339 corners[3] = m_heightmap->getGroundHeight
2340 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2343 void ServerMap::PrintInfo(std::ostream &out)
2352 ClientMap::ClientMap(
2354 video::SMaterial *materials,
2355 scene::ISceneNode* parent,
2356 scene::ISceneManager* mgr,
2360 scene::ISceneNode(parent, mgr, id),
2362 m_materials(materials),
2365 /*m_box = core::aabbox3d<f32>(0,0,0,
2366 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2367 /*m_box = core::aabbox3d<f32>(0,0,0,
2368 map->getSizeNodes().X * BS,
2369 map->getSizeNodes().Y * BS,
2370 map->getSizeNodes().Z * BS);*/
2371 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2372 BS*1000000,BS*1000000,BS*1000000);
2377 ClientMap::~ClientMap()
2379 JMutexAutoLock lock(mesh_mutex);
2388 MapSector * ClientMap::emergeSector(v2s16 p2d)
2390 DSTACK(__FUNCTION_NAME);
2391 // Check that it doesn't exist already
2393 return getSectorNoGenerate(p2d);
2395 catch(InvalidPositionException &e)
2399 // Create a sector with no heightmaps
2400 ClientMapSector *sector = new ClientMapSector(this, p2d);
2403 JMutexAutoLock lock(m_sector_mutex);
2404 m_sectors.insert(p2d, sector);
2410 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2412 DSTACK(__FUNCTION_NAME);
2413 ClientMapSector *sector = NULL;
2415 JMutexAutoLock lock(m_sector_mutex);
2417 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2421 sector = (ClientMapSector*)n->getValue();
2422 assert(sector->getId() == MAPSECTOR_CLIENT);
2426 sector = new ClientMapSector(this, p2d);
2428 JMutexAutoLock lock(m_sector_mutex);
2429 m_sectors.insert(p2d, sector);
2433 sector->deSerialize(is);
2436 void ClientMap::renderMap(video::IVideoDriver* driver,
2437 video::SMaterial *materials, s32 pass)
2439 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2440 DSTACK(__FUNCTION_NAME);
2442 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2445 Draw master heightmap mesh
2449 JMutexAutoLock lock(mesh_mutex);
2452 u32 c = mesh->getMeshBufferCount();
2454 for(u32 i=0; i<c; i++)
2456 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2457 const video::SMaterial& material = buf->getMaterial();
2458 video::IMaterialRenderer* rnd =
2459 driver->getMaterialRenderer(material.MaterialType);
2460 bool transparent = (rnd && rnd->isTransparent());
2461 // Render transparent on transparent pass and likewise.
2462 if(transparent == is_transparent_pass)
2464 driver->setMaterial(buf->getMaterial());
2465 driver->drawMeshBuffer(buf);
2473 Get time for measuring timeout.
2475 Measuring time is very useful for long delays when the
2476 machine is swapping a lot.
2478 int time1 = time(0);
2481 Collect all blocks that are in the view range
2483 Should not optimize more here as we want to auto-update
2484 all changed nodes in viewing range at the next step.
2487 s16 viewing_range_nodes;
2488 bool viewing_range_all;
2490 JMutexAutoLock lock(g_range_mutex);
2491 viewing_range_nodes = g_viewing_range_nodes;
2492 viewing_range_all = g_viewing_range_all;
2495 m_camera_mutex.Lock();
2496 v3f camera_position = m_camera_position;
2497 v3f camera_direction = m_camera_direction;
2498 m_camera_mutex.Unlock();
2501 Get all blocks and draw all visible ones
2504 v3s16 cam_pos_nodes(
2505 camera_position.X / BS,
2506 camera_position.Y / BS,
2507 camera_position.Z / BS);
2509 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2511 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2512 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2514 // Take a fair amount as we will be dropping more out later
2516 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2517 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2518 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2520 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2521 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2522 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2524 u32 vertex_count = 0;
2526 core::map<v2s16, MapSector*>::Iterator si;
2528 //NOTE: The sectors map should be locked but we're not doing it
2529 // because it'd cause too much delays
2531 si = m_sectors.getIterator();
2532 for(; si.atEnd() == false; si++)
2535 static int timecheck_counter = 0;
2536 timecheck_counter++;
2537 if(timecheck_counter > 50)
2539 int time2 = time(0);
2540 if(time2 > time1 + 4)
2542 dstream<<"ClientMap::renderMap(): "
2543 "Rendering takes ages, returning."
2550 MapSector *sector = si.getNode()->getValue();
2551 v2s16 sp = sector->getPos();
2553 if(viewing_range_all == false)
2555 if(sp.X < p_blocks_min.X
2556 || sp.X > p_blocks_max.X
2557 || sp.Y < p_blocks_min.Z
2558 || sp.Y > p_blocks_max.Z)
2562 core::list< MapBlock * > sectorblocks;
2563 sector->getBlocks(sectorblocks);
2569 core::list< MapBlock * >::Iterator i;
2570 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2572 MapBlock *block = *i;
2575 Compare block position to camera position, skip
2576 if not seen on display
2579 v3s16 blockpos_nodes = block->getPosRelative();
2581 // Block center position
2583 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2584 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2585 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2588 // Block position relative to camera
2589 v3f blockpos_relative = blockpos - camera_position;
2591 // Distance in camera direction (+=front, -=back)
2592 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2595 f32 d = blockpos_relative.getLength();
2597 if(viewing_range_all == false)
2599 // If block is far away, don't draw it
2600 if(d > viewing_range_nodes * BS)
2604 // Maximum radius of a block
2605 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2607 // If block is (nearly) touching the camera, don't
2608 // bother validating further (that is, render it anyway)
2609 if(d > block_max_radius * 1.5)
2611 // Cosine of the angle between the camera direction
2612 // and the block direction (camera_direction is an unit vector)
2613 f32 cosangle = dforward / d;
2615 // Compensate for the size of the block
2616 // (as the block has to be shown even if it's a bit off FOV)
2617 // This is an estimate.
2618 cosangle += block_max_radius / dforward;
2620 // If block is not in the field of view, skip it
2621 //if(cosangle < cos(FOV_ANGLE/2))
2622 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2627 Draw the faces of the block
2631 JMutexAutoLock lock(block->mesh_mutex);
2633 // Cancel if block has no mesh
2634 if(block->mesh == NULL)
2637 u32 c = block->mesh->getMeshBufferCount();
2639 for(u32 i=0; i<c; i++)
2641 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2642 const video::SMaterial& material = buf->getMaterial();
2643 video::IMaterialRenderer* rnd =
2644 driver->getMaterialRenderer(material.MaterialType);
2645 bool transparent = (rnd && rnd->isTransparent());
2646 // Render transparent on transparent pass and likewise.
2647 if(transparent == is_transparent_pass)
2649 driver->setMaterial(buf->getMaterial());
2650 driver->drawMeshBuffer(buf);
2651 vertex_count += buf->getVertexCount();
2655 } // foreach sectorblocks
2658 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2659 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2662 void ClientMap::updateMesh()
2665 DSTACK(__FUNCTION_NAME);
2668 Check what sectors don't draw anything useful at ground level
2669 and create a mesh of the rough heightmap at those positions.
2672 m_camera_mutex.Lock();
2673 v3f camera_position = m_camera_position;
2674 v3f camera_direction = m_camera_direction;
2675 m_camera_mutex.Unlock();
2677 v3s16 cam_pos_nodes(
2678 camera_position.X / BS,
2679 camera_position.Y / BS,
2680 camera_position.Z / BS);
2682 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2684 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2685 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2687 // Take a fair amount as we will be dropping more out later
2689 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2690 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2691 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2693 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2694 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2695 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2701 scene::SMesh *mesh_new = new scene::SMesh();
2702 //scene::IMeshBuffer *buf = NULL;
2703 scene::SMeshBuffer *buf = NULL;
2705 u8 material_in_use = 0;
2708 Loop through sectors
2711 for(core::map<v2s16, MapSector*>::Iterator
2712 si = m_sectors.getIterator();
2713 si.atEnd() == false; si++)
2715 MapSector *sector = si.getNode()->getValue();
2717 if(sector->getId() != MAPSECTOR_CLIENT)
2719 dstream<<"WARNING: Client has a non-client sector"
2724 ClientMapSector *cs = (ClientMapSector*)sector;
2726 v2s16 sp = sector->getPos();
2728 if(sp.X < p_blocks_min.X
2729 || sp.X > p_blocks_max.X
2730 || sp.Y < p_blocks_min.Z
2731 || sp.Y > p_blocks_max.Z)
2735 Get some ground level info
2747 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
2749 s16 cn_max = -32768;
2750 for(s16 i=0; i<4; i++)
2757 s16 cn_slope = cn_max - cn_min;
2760 Generate this part of the heightmap mesh
2764 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
2766 else if(cn_slope <= MAP_BLOCKSIZE)
2771 if(material != material_in_use || buf == NULL)
2773 // Try to get a meshbuffer associated with the material
2774 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
2775 (g_mesh_materials[material]);
2776 // If not found, create one
2779 // This is a "Standard MeshBuffer",
2780 // it's a typedeffed CMeshBuffer<video::S3DVertex>
2781 buf = new scene::SMeshBuffer();
2784 buf->Material = g_mesh_materials[material];
2786 //buf->setHardwareMappingHint(scene::EHM_STATIC);
2788 mesh_new->addMeshBuffer(buf);
2792 material_in_use = material;
2795 // Sector side width in floating-point units
2796 f32 sd = BS * MAP_BLOCKSIZE;
2797 // Sector position in global floating-point units
2798 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
2800 //video::SColor c(255,255,255,255);
2802 video::SColor c(255,cc,cc,cc);
2804 video::S3DVertex vertices[4] =
2806 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
2807 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
2808 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
2809 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
2811 u16 indices[] = {0,1,2,2,3,0};
2813 buf->append(vertices, 4, indices, 6);
2817 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
2825 scene::SMesh *mesh_old = mesh;
2832 mesh_mutex.Unlock();
2834 if(mesh_old != NULL)
2836 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
2838 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
2839 (g_materials[MATERIAL_GRASS]);
2841 dstream<<"grass buf refcount="<<buf->getReferenceCount()
2848 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
2853 void ClientMap::PrintInfo(std::ostream &out)