3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "jmutexautolock.h"
31 #define sleep_ms(x) Sleep(x)
34 #define sleep_ms(x) usleep(x*1000)
37 MapBlockPointerCache::MapBlockPointerCache(Map *map)
40 m_map->m_blockcachelock.cacheCreated();
42 m_from_cache_count = 0;
46 MapBlockPointerCache::~MapBlockPointerCache()
48 m_map->m_blockcachelock.cacheRemoved();
50 /*dstream<<"MapBlockPointerCache:"
51 <<" from_cache_count="<<m_from_cache_count
52 <<" from_map_count="<<m_from_map_count
56 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
58 core::map<v3s16, MapBlock*>::Node *n = NULL;
68 // Throws InvalidPositionException if not found
69 MapBlock *b = m_map->getBlockNoCreate(p);
78 Map::Map(std::ostream &dout):
80 m_camera_position(0,0,0),
81 m_camera_direction(0,0,1),
86 m_sector_mutex.Init();
87 m_camera_mutex.Init();
88 assert(m_sector_mutex.IsInitialized());
89 assert(m_camera_mutex.IsInitialized());
91 // Get this so that the player can stay on it at first
92 //getSector(v2s16(0,0));
100 /*updater.setRun(false);
101 while(updater.IsRunning())
107 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
108 for(; i.atEnd() == false; i++)
110 MapSector *sector = i.getNode()->getValue();
115 /*bool Map::sectorExists(v2s16 p)
117 JMutexAutoLock lock(m_sector_mutex);
118 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
122 MapSector * Map::getSectorNoGenerate(v2s16 p)
124 JMutexAutoLock lock(m_sector_mutex);
126 if(m_sector_cache != NULL && p == m_sector_cache_p){
127 MapSector * sector = m_sector_cache;
128 // Reset inactivity timer
129 sector->usage_timer = 0.0;
133 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
134 // If sector doesn't exist, throw an exception
137 throw InvalidPositionException();
140 MapSector *sector = n->getValue();
142 // Cache the last result
143 m_sector_cache_p = p;
144 m_sector_cache = sector;
146 //MapSector * ref(sector);
148 // Reset inactivity timer
149 sector->usage_timer = 0.0;
153 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
155 v2s16 p2d(p3d.X, p3d.Z);
156 MapSector * sector = getSectorNoGenerate(p2d);
158 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
163 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
165 dstream<<"Map::getBlock() with generate=true called"
167 v2s16 p2d(p3d.X, p3d.Z);
168 //MapSector * sector = getSector(p2d, generate);
169 MapSector * sector = getSectorNoGenerate(p2d);
172 throw InvalidPositionException();
174 return sector->getBlockNoCreate(p3d.Y);
177 f32 Map::getGroundHeight(v2s16 p, bool generate)
180 v2s16 sectorpos = getNodeSectorPos(p);
181 MapSector * sref = getSectorNoGenerate(sectorpos);
182 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
183 f32 y = sref->getGroundHeight(relpos);
186 catch(InvalidPositionException &e)
188 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
192 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
194 /*m_dout<<DTIME<<"Map::setGroundHeight(("
196 <<"), "<<y<<")"<<std::endl;*/
197 v2s16 sectorpos = getNodeSectorPos(p);
198 MapSector * sref = getSectorNoGenerate(sectorpos);
199 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
200 //sref->mutex.Lock();
201 sref->setGroundHeight(relpos, y);
202 //sref->mutex.Unlock();
205 bool Map::isNodeUnderground(v3s16 p)
207 v3s16 blockpos = getNodeBlockPos(p);
209 MapBlock * block = getBlockNoCreate(blockpos);
210 return block->getIsUnderground();
212 catch(InvalidPositionException &e)
219 void Map::interpolate(v3s16 block,
220 core::map<v3s16, MapBlock*> & modified_blocks)
222 const v3s16 dirs[6] = {
223 v3s16(0,0,1), // back
225 v3s16(1,0,0), // right
226 v3s16(0,0,-1), // front
227 v3s16(0,-1,0), // bottom
228 v3s16(-1,0,0), // left
231 if(from_nodes.size() == 0)
234 u32 blockchangecount = 0;
236 core::map<v3s16, bool> lighted_nodes;
237 core::map<v3s16, bool>::Iterator j;
238 j = from_nodes.getIterator();
241 Initialize block cache
244 MapBlock *block = NULL;
245 // Cache this a bit, too
246 bool block_checked_in_modified = false;
248 for(; j.atEnd() == false; j++)
249 //for(; j != from_nodes.end(); j++)
251 v3s16 pos = j.getNode()->getKey();
253 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
254 v3s16 blockpos = getNodeBlockPos(pos);
256 // Only fetch a new block if the block position has changed
258 if(block == NULL || blockpos != blockpos_last){
259 block = getBlockNoCreate(blockpos);
260 blockpos_last = blockpos;
262 block_checked_in_modified = false;
266 catch(InvalidPositionException &e)
274 // Calculate relative position in block
275 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
277 // Get node straight from the block
278 MapNode n = block->getNode(relpos);
280 u8 oldlight = n.getLight();
281 u8 newlight = diminish_light(oldlight);
283 // Loop through 6 neighbors
284 for(u16 i=0; i<6; i++){
285 // Get the position of the neighbor node
286 v3s16 n2pos = pos + dirs[i];
288 // Get the block where the node is located
289 v3s16 blockpos = getNodeBlockPos(n2pos);
293 // Only fetch a new block if the block position has changed
295 if(block == NULL || blockpos != blockpos_last){
296 block = getBlockNoCreate(blockpos);
297 blockpos_last = blockpos;
299 block_checked_in_modified = false;
303 catch(InvalidPositionException &e)
308 // Calculate relative position in block
309 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
310 // Get node straight from the block
311 MapNode n2 = block->getNode(relpos);
313 bool changed = false;
315 If the neighbor is brighter than the current node,
316 add to list (it will light up this node on its turn)
318 if(n2.getLight() > undiminish_light(oldlight))
320 lighted_nodes.insert(n2pos, true);
321 //lighted_nodes.push_back(n2pos);
325 If the neighbor is dimmer than how much light this node
326 would spread on it, add to list
328 if(n2.getLight() < newlight)
330 if(n2.light_propagates())
332 n2.setLight(newlight);
333 block->setNode(relpos, n2);
334 lighted_nodes.insert(n2pos, true);
335 //lighted_nodes.push_back(n2pos);
340 // Add to modified_blocks
341 if(changed == true && block_checked_in_modified == false)
343 // If the block is not found in modified_blocks, add.
344 if(modified_blocks.find(blockpos) == NULL)
346 modified_blocks.insert(blockpos, block);
348 block_checked_in_modified = true;
351 catch(InvalidPositionException &e)
358 /*dstream<<"spreadLight(): Changed block "
359 <<blockchangecount<<" times"
360 <<" for "<<from_nodes.size()<<" nodes"
363 if(lighted_nodes.size() > 0)
364 spreadLight(lighted_nodes, modified_blocks);
369 Goes recursively through the neighbours of the node.
371 Alters only transparent nodes.
373 If the lighting of the neighbour is lower than the lighting of
374 the node was (before changing it to 0 at the step before), the
375 lighting of the neighbour is set to 0 and then the same stuff
376 repeats for the neighbour.
378 The ending nodes of the routine are stored in light_sources.
379 This is useful when a light is removed. In such case, this
380 routine can be called for the light node and then again for
381 light_sources to re-light the area without the removed light.
383 values of from_nodes are lighting values.
385 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
386 core::map<v3s16, bool> & light_sources,
387 core::map<v3s16, MapBlock*> & modified_blocks)
390 v3s16(0,0,1), // back
392 v3s16(1,0,0), // right
393 v3s16(0,0,-1), // front
394 v3s16(0,-1,0), // bottom
395 v3s16(-1,0,0), // left
398 if(from_nodes.size() == 0)
401 u32 blockchangecount = 0;
403 core::map<v3s16, u8> unlighted_nodes;
404 core::map<v3s16, u8>::Iterator j;
405 j = from_nodes.getIterator();
408 Initialize block cache
411 MapBlock *block = NULL;
412 // Cache this a bit, too
413 bool block_checked_in_modified = false;
415 for(; j.atEnd() == false; j++)
417 v3s16 pos = j.getNode()->getKey();
418 v3s16 blockpos = getNodeBlockPos(pos);
420 // Only fetch a new block if the block position has changed
422 if(block == NULL || blockpos != blockpos_last){
423 block = getBlockNoCreate(blockpos);
424 blockpos_last = blockpos;
426 block_checked_in_modified = false;
430 catch(InvalidPositionException &e)
438 // Calculate relative position in block
439 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
441 // Get node straight from the block
442 MapNode n = block->getNode(relpos);
444 u8 oldlight = j.getNode()->getValue();
446 // Loop through 6 neighbors
447 for(u16 i=0; i<6; i++)
449 // Get the position of the neighbor node
450 v3s16 n2pos = pos + dirs[i];
452 // Get the block where the node is located
453 v3s16 blockpos = getNodeBlockPos(n2pos);
457 // Only fetch a new block if the block position has changed
459 if(block == NULL || blockpos != blockpos_last){
460 block = getBlockNoCreate(blockpos);
461 blockpos_last = blockpos;
463 block_checked_in_modified = false;
467 catch(InvalidPositionException &e)
472 // Calculate relative position in block
473 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
474 // Get node straight from the block
475 MapNode n2 = block->getNode(relpos);
477 bool changed = false;
479 //TODO: Optimize output by optimizing light_sources?
482 If the neighbor is dimmer than what was specified
483 as oldlight (the light of the previous node)
485 if(n2.getLight() < oldlight)
488 And the neighbor is transparent and it has some light
490 if(n2.light_propagates() && n2.getLight() != 0)
493 Set light to 0 and add to queue
496 u8 current_light = n2.getLight();
498 block->setNode(relpos, n2);
500 unlighted_nodes.insert(n2pos, current_light);
504 Remove from light_sources if it is there
505 NOTE: This doesn't happen nearly at all
507 /*if(light_sources.find(n2pos))
509 std::cout<<"Removed from light_sources"<<std::endl;
510 light_sources.remove(n2pos);
515 if(light_sources.find(n2pos) != NULL)
516 light_sources.remove(n2pos);*/
519 light_sources.insert(n2pos, true);
522 // Add to modified_blocks
523 if(changed == true && block_checked_in_modified == false)
525 // If the block is not found in modified_blocks, add.
526 if(modified_blocks.find(blockpos) == NULL)
528 modified_blocks.insert(blockpos, block);
530 block_checked_in_modified = true;
533 catch(InvalidPositionException &e)
540 /*dstream<<"unspreadLight(): Changed block "
541 <<blockchangecount<<" times"
542 <<" for "<<from_nodes.size()<<" nodes"
545 if(unlighted_nodes.size() > 0)
546 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
550 A single-node wrapper of the above
552 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
553 core::map<v3s16, bool> & light_sources,
554 core::map<v3s16, MapBlock*> & modified_blocks)
556 core::map<v3s16, u8> from_nodes;
557 from_nodes.insert(pos, lightwas);
559 unspreadLight(from_nodes, light_sources, modified_blocks);
563 Lights neighbors of from_nodes, collects all them and then
566 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
567 core::map<v3s16, MapBlock*> & modified_blocks)
569 const v3s16 dirs[6] = {
570 v3s16(0,0,1), // back
572 v3s16(1,0,0), // right
573 v3s16(0,0,-1), // front
574 v3s16(0,-1,0), // bottom
575 v3s16(-1,0,0), // left
578 if(from_nodes.size() == 0)
581 u32 blockchangecount = 0;
583 core::map<v3s16, bool> lighted_nodes;
584 core::map<v3s16, bool>::Iterator j;
585 j = from_nodes.getIterator();
588 Initialize block cache
591 MapBlock *block = NULL;
592 // Cache this a bit, too
593 bool block_checked_in_modified = false;
595 for(; j.atEnd() == false; j++)
596 //for(; j != from_nodes.end(); j++)
598 v3s16 pos = j.getNode()->getKey();
600 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
601 v3s16 blockpos = getNodeBlockPos(pos);
603 // Only fetch a new block if the block position has changed
605 if(block == NULL || blockpos != blockpos_last){
606 block = getBlockNoCreate(blockpos);
607 blockpos_last = blockpos;
609 block_checked_in_modified = false;
613 catch(InvalidPositionException &e)
621 // Calculate relative position in block
622 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
624 // Get node straight from the block
625 MapNode n = block->getNode(relpos);
627 u8 oldlight = n.getLight();
628 u8 newlight = diminish_light(oldlight);
630 // Loop through 6 neighbors
631 for(u16 i=0; i<6; i++){
632 // Get the position of the neighbor node
633 v3s16 n2pos = pos + dirs[i];
635 // Get the block where the node is located
636 v3s16 blockpos = getNodeBlockPos(n2pos);
640 // Only fetch a new block if the block position has changed
642 if(block == NULL || blockpos != blockpos_last){
643 block = getBlockNoCreate(blockpos);
644 blockpos_last = blockpos;
646 block_checked_in_modified = false;
650 catch(InvalidPositionException &e)
655 // Calculate relative position in block
656 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
657 // Get node straight from the block
658 MapNode n2 = block->getNode(relpos);
660 bool changed = false;
662 If the neighbor is brighter than the current node,
663 add to list (it will light up this node on its turn)
665 if(n2.getLight() > undiminish_light(oldlight))
667 lighted_nodes.insert(n2pos, true);
668 //lighted_nodes.push_back(n2pos);
672 If the neighbor is dimmer than how much light this node
673 would spread on it, add to list
675 if(n2.getLight() < newlight)
677 if(n2.light_propagates())
679 n2.setLight(newlight);
680 block->setNode(relpos, n2);
681 lighted_nodes.insert(n2pos, true);
682 //lighted_nodes.push_back(n2pos);
687 // Add to modified_blocks
688 if(changed == true && block_checked_in_modified == false)
690 // If the block is not found in modified_blocks, add.
691 if(modified_blocks.find(blockpos) == NULL)
693 modified_blocks.insert(blockpos, block);
695 block_checked_in_modified = true;
698 catch(InvalidPositionException &e)
705 /*dstream<<"spreadLight(): Changed block "
706 <<blockchangecount<<" times"
707 <<" for "<<from_nodes.size()<<" nodes"
710 if(lighted_nodes.size() > 0)
711 spreadLight(lighted_nodes, modified_blocks);
715 A single-node source variation of the above.
717 void Map::lightNeighbors(v3s16 pos,
718 core::map<v3s16, MapBlock*> & modified_blocks)
720 core::map<v3s16, bool> from_nodes;
721 from_nodes.insert(pos, true);
722 spreadLight(from_nodes, modified_blocks);
725 v3s16 Map::getBrightestNeighbour(v3s16 p)
728 v3s16(0,0,1), // back
730 v3s16(1,0,0), // right
731 v3s16(0,0,-1), // front
732 v3s16(0,-1,0), // bottom
733 v3s16(-1,0,0), // left
736 u8 brightest_light = 0;
737 v3s16 brightest_pos(0,0,0);
738 bool found_something = false;
740 // Loop through 6 neighbors
741 for(u16 i=0; i<6; i++){
742 // Get the position of the neighbor node
743 v3s16 n2pos = p + dirs[i];
748 catch(InvalidPositionException &e)
752 if(n2.getLight() > brightest_light || found_something == false){
753 brightest_light = n2.getLight();
754 brightest_pos = n2pos;
755 found_something = true;
759 if(found_something == false)
760 throw InvalidPositionException();
762 return brightest_pos;
766 Propagates sunlight down from a node.
767 Starting point gets sunlight.
769 Returns the lowest y value of where the sunlight went.
771 s16 Map::propagateSunlight(v3s16 start,
772 core::map<v3s16, MapBlock*> & modified_blocks)
777 v3s16 pos(start.X, y, start.Z);
779 v3s16 blockpos = getNodeBlockPos(pos);
782 block = getBlockNoCreate(blockpos);
784 catch(InvalidPositionException &e)
789 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
790 MapNode n = block->getNode(relpos);
792 if(n.sunlight_propagates())
794 n.setLight(LIGHT_SUN);
795 block->setNode(relpos, n);
797 modified_blocks.insert(blockpos, block);
806 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
807 core::map<v3s16, MapBlock*> & modified_blocks)
809 /*m_dout<<DTIME<<"Map::updateLighting(): "
810 <<a_blocks.getSize()<<" blocks... ";*/
814 u32 count_was = modified_blocks.size();
816 /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
817 for(; i != a_blocks.end(); i++)
819 MapBlock *block = *i;*/
821 core::map<v3s16, bool> light_sources;
823 core::map<v3s16, u8> unlight_from;
825 core::map<v3s16, MapBlock*>::Iterator i;
826 i = a_blocks.getIterator();
827 for(; i.atEnd() == false; i++)
829 MapBlock *block = i.getNode()->getValue();
833 // Don't bother with dummy blocks.
837 v3s16 pos = block->getPos();
838 modified_blocks.insert(pos, block);
841 Clear all light from block
843 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
844 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
845 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
850 MapNode n = block->getNode(v3s16(x,y,z));
851 u8 oldlight = n.getLight();
853 block->setNode(v3s16(x,y,z), n);
855 // Collect borders for unlighting
856 if(x==0 || x == MAP_BLOCKSIZE-1
857 || y==0 || y == MAP_BLOCKSIZE-1
858 || z==0 || z == MAP_BLOCKSIZE-1)
860 v3s16 p_map = p + v3s16(
863 MAP_BLOCKSIZE*pos.Z);
864 unlight_from.insert(p_map, oldlight);
867 catch(InvalidPositionException &e)
870 This would happen when dealing with a
874 dstream<<"updateLighting(): InvalidPositionException"
879 bool bottom_valid = block->propagateSunlight(light_sources);
881 // If bottom is valid, we're done.
885 /*dstream<<"Bottom for sunlight-propagated block ("
886 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
889 // Else get the block below and loop to it
893 block = getBlockNoCreate(pos);
895 catch(InvalidPositionException &e)
904 //TimeTaker timer("unspreadLight", g_device);
905 unspreadLight(unlight_from, light_sources, modified_blocks);
910 u32 diff = modified_blocks.size() - count_was;
911 count_was = modified_blocks.size();
912 dstream<<"unspreadLight modified "<<diff<<std::endl;
915 // TODO: Spread light from propagated sunlight?
916 // Yes, add it to light_sources... somehow.
917 // It has to be added at somewhere above, in the loop.
919 // NOTE: This actually works quite fine without it
920 // - Find out why it works
923 //TimeTaker timer("spreadLight", g_device);
924 spreadLight(light_sources, modified_blocks);
929 u32 diff = modified_blocks.size() - count_was;
930 count_was = modified_blocks.size();
931 dstream<<"spreadLight modified "<<diff<<std::endl;
934 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
938 This is called after changing a node from transparent to opaque.
939 The lighting value of the node should be left as-is after changing
940 other values. This sets the lighting value to 0.
942 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
943 core::map<v3s16, MapBlock*> &modified_blocks)*/
944 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
945 core::map<v3s16, MapBlock*> &modified_blocks)
948 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
949 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
951 u8 lightwas = getNode(p).getLight();
953 //core::list<v3s16> light_sources;
954 core::map<v3s16, bool> light_sources;
955 //MapNode n = getNode(p);
958 From this node to nodes underneath:
959 If lighting is sunlight (1.0), unlight neighbours and
964 bool node_under_sunlight = true;
966 v3s16 toppos = p + v3s16(0,1,0);
969 If there is a node at top and it doesn't have sunlight,
970 there has not been any sunlight going down.
972 Otherwise there probably is.
975 MapNode topnode = getNode(toppos);
977 if(topnode.getLight() != LIGHT_SUN)
978 node_under_sunlight = false;
980 catch(InvalidPositionException &e)
984 // Add the block of the added node to modified_blocks
985 v3s16 blockpos = getNodeBlockPos(p);
986 MapBlock * block = getBlockNoCreate(blockpos);
987 assert(block != NULL);
988 modified_blocks.insert(blockpos, block);
990 if(isValidPosition(p) == false)
993 // Unlight neighbours of node.
994 // This means setting light of all consequent dimmer nodes
996 // This also collects the nodes at the border which will spread
997 // light again into this.
998 unLightNeighbors(p, lightwas, light_sources, modified_blocks);
1004 If node is under sunlight, take all sunlighted nodes under
1005 it and clear light from them and from where the light has
1008 if(node_under_sunlight)
1012 //m_dout<<DTIME<<"y="<<y<<std::endl;
1013 v3s16 n2pos(p.X, y, p.Z);
1017 n2 = getNode(n2pos);
1019 catch(InvalidPositionException &e)
1024 if(n2.getLight() == LIGHT_SUN)
1026 //m_dout<<DTIME<<"doing"<<std::endl;
1027 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
1037 Spread light from all nodes that might be capable of doing so
1038 TODO: Convert to spreadLight
1040 spreadLight(light_sources, modified_blocks);
1045 void Map::removeNodeAndUpdate(v3s16 p,
1046 core::map<v3s16, MapBlock*> &modified_blocks)
1048 /*PrintInfo(m_dout);
1049 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1050 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1052 bool node_under_sunlight = true;
1054 v3s16 toppos = p + v3s16(0,1,0);
1056 // Node will be replaced with this
1057 u8 replace_material = CONTENT_AIR;
1059 // NOTE: Water is now managed elsewhere
1063 Find out with what material the node will be replaced.
1064 It will be replaced with the mostly seen buildable_to.
1068 v3s16(0,0,1), // back
1069 v3s16(0,1,0), // top
1070 v3s16(1,0,0), // right
1071 v3s16(0,0,-1), // front
1072 v3s16(0,-1,0), // bottom
1073 v3s16(-1,0,0), // left
1076 core::map<u8, u16> neighbor_rankings;
1078 for(u32 i=0; i<sizeof(dirs)/sizeof(dirs[0]); i++)
1081 MapNode n2 = getNode(p + dirs[i]);
1083 if(material_buildable_to(n2.d))
1085 if(neighbor_rankings.find(n2.d) == NULL)
1086 neighbor_rankings[n2.d] = 1;
1088 neighbor_rankings[n2.d]
1089 = neighbor_rankings[n2.d] + 1;
1092 catch(InvalidPositionException &e)
1097 u16 highest_ranking = 0;
1099 for(core::map<u8, u16>::Iterator
1100 i = neighbor_rankings.getIterator();
1101 i.atEnd() == false; i++)
1103 u8 m = i.getNode()->getKey();
1104 u8 c = i.getNode()->getValue();
1106 c > highest_ranking ||
1107 // Prefer something else than air
1108 (c >= highest_ranking && m != CONTENT_AIR)
1112 replace_material = m;
1113 highest_ranking = c;
1121 If there is a node at top and it doesn't have sunlight,
1122 there will be no sunlight going down.
1125 MapNode topnode = getNode(toppos);
1127 if(topnode.getLight() != LIGHT_SUN)
1128 node_under_sunlight = false;
1130 catch(InvalidPositionException &e)
1135 Unlight neighbors (in case the node is a light source)
1137 //core::list<v3s16> light_sources;
1138 core::map<v3s16, bool> light_sources;
1139 unLightNeighbors(p, getNode(p).getLight(),
1140 light_sources, modified_blocks);
1146 n.d = replace_material;
1151 Recalculate lighting
1153 spreadLight(light_sources, modified_blocks);
1155 // Add the block of the removed node to modified_blocks
1156 v3s16 blockpos = getNodeBlockPos(p);
1157 MapBlock * block = getBlockNoCreate(blockpos);
1158 assert(block != NULL);
1159 modified_blocks.insert(blockpos, block);
1162 If the removed node was under sunlight, propagate the
1163 sunlight down from it and then light all neighbors
1164 of the propagated blocks.
1166 if(node_under_sunlight)
1168 s16 ybottom = propagateSunlight(p, modified_blocks);
1169 /*m_dout<<DTIME<<"Node was under sunlight. "
1170 "Propagating sunlight";
1171 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1173 for(; y >= ybottom; y--)
1175 v3s16 p2(p.X, y, p.Z);
1176 /*m_dout<<DTIME<<"lighting neighbors of node ("
1177 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1179 lightNeighbors(p2, modified_blocks);
1184 // Set the lighting of this node to 0
1186 MapNode n = getNode(p);
1190 catch(InvalidPositionException &e)
1196 // Get the brightest neighbour node and propagate light from it
1197 v3s16 n2p = getBrightestNeighbour(p);
1199 MapNode n2 = getNode(n2p);
1200 lightNeighbors(n2p, modified_blocks);
1202 catch(InvalidPositionException &e)
1207 void Map::updateMeshes(v3s16 blockpos)
1209 assert(mapType() == MAPTYPE_CLIENT);
1212 v3s16 p = blockpos + v3s16(0,0,0);
1213 MapBlock *b = getBlockNoCreate(p);
1216 catch(InvalidPositionException &e){}
1218 v3s16 p = blockpos + v3s16(-1,0,0);
1219 MapBlock *b = getBlockNoCreate(p);
1222 catch(InvalidPositionException &e){}
1224 v3s16 p = blockpos + v3s16(0,-1,0);
1225 MapBlock *b = getBlockNoCreate(p);
1228 catch(InvalidPositionException &e){}
1230 v3s16 p = blockpos + v3s16(0,0,-1);
1231 MapBlock *b = getBlockNoCreate(p);
1234 catch(InvalidPositionException &e){}
1238 Updates usage timers
1240 void Map::timerUpdate(float dtime)
1242 JMutexAutoLock lock(m_sector_mutex);
1244 core::map<v2s16, MapSector*>::Iterator si;
1246 si = m_sectors.getIterator();
1247 for(; si.atEnd() == false; si++)
1249 MapSector *sector = si.getNode()->getValue();
1250 sector->usage_timer += dtime;
1254 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1257 Wait for caches to be removed before continuing.
1259 This disables the existence of caches while locked
1261 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1263 core::list<v2s16>::Iterator j;
1264 for(j=list.begin(); j!=list.end(); j++)
1266 MapSector *sector = m_sectors[*j];
1269 sector->deleteBlocks();
1274 If sector is in sector cache, remove it from there
1276 if(m_sector_cache == sector)
1278 m_sector_cache = NULL;
1281 Remove from map and delete
1283 m_sectors.remove(*j);
1289 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1290 core::list<v3s16> *deleted_blocks)
1292 JMutexAutoLock lock(m_sector_mutex);
1294 core::list<v2s16> sector_deletion_queue;
1295 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1296 for(; i.atEnd() == false; i++)
1298 MapSector *sector = i.getNode()->getValue();
1300 Delete sector from memory if it hasn't been used in a long time
1302 if(sector->usage_timer > timeout)
1304 sector_deletion_queue.push_back(i.getNode()->getKey());
1306 if(deleted_blocks != NULL)
1308 // Collect positions of blocks of sector
1309 MapSector *sector = i.getNode()->getValue();
1310 core::list<MapBlock*> blocks;
1311 sector->getBlocks(blocks);
1312 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1313 i != blocks.end(); i++)
1315 deleted_blocks->push_back((*i)->getPos());
1320 deleteSectors(sector_deletion_queue, only_blocks);
1321 return sector_deletion_queue.getSize();
1324 void Map::PrintInfo(std::ostream &out)
1333 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1337 m_savedir = savedir;
1338 m_map_saving_enabled = false;
1342 // If directory exists, check contents and load if possible
1343 if(fs::PathExists(m_savedir))
1345 // If directory is empty, it is safe to save into it.
1346 if(fs::GetDirListing(m_savedir).size() == 0)
1348 dstream<<DTIME<<"Server: Empty save directory is valid."
1350 m_map_saving_enabled = true;
1354 // Load master heightmap
1355 loadMasterHeightmap();
1357 // Load sector (0,0) and throw and exception on fail
1358 if(loadSectorFull(v2s16(0,0)) == false)
1359 throw LoadError("Failed to load sector (0,0)");
1361 dstream<<DTIME<<"Server: Successfully loaded master "
1362 "heightmap and sector (0,0) from "<<savedir<<
1363 ", assuming valid save directory."
1366 m_map_saving_enabled = true;
1367 // Map loaded, not creating new one
1371 // If directory doesn't exist, it is safe to save to it
1373 m_map_saving_enabled = true;
1376 catch(std::exception &e)
1378 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1379 <<", exception: "<<e.what()<<std::endl;
1380 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1381 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1384 dstream<<DTIME<<"Initializing new map."<<std::endl;
1386 // Create master heightmap
1387 ValueGenerator *maxgen =
1388 ValueGenerator::deSerialize(hmp.randmax);
1389 ValueGenerator *factorgen =
1390 ValueGenerator::deSerialize(hmp.randfactor);
1391 ValueGenerator *basegen =
1392 ValueGenerator::deSerialize(hmp.base);
1393 m_heightmap = new UnlimitedHeightmap
1394 (hmp.blocksize, maxgen, factorgen, basegen);
1396 // Set map parameters
1399 // Create zero sector
1400 emergeSector(v2s16(0,0));
1402 // Initially write whole map
1406 ServerMap::~ServerMap()
1410 if(m_map_saving_enabled)
1413 // Save only changed parts
1415 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1419 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1422 catch(std::exception &e)
1424 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1425 <<", exception: "<<e.what()<<std::endl;
1428 if(m_heightmap != NULL)
1432 MapSector * ServerMap::emergeSector(v2s16 p2d)
1434 DSTACK("%s: p2d=(%d,%d)",
1437 // Check that it doesn't exist already
1439 return getSectorNoGenerate(p2d);
1441 catch(InvalidPositionException &e)
1446 Try to load the sector from disk.
1448 if(loadSectorFull(p2d) == true)
1450 return getSectorNoGenerate(p2d);
1454 If there is no master heightmap, throw.
1456 if(m_heightmap == NULL)
1458 throw InvalidPositionException("emergeSector(): no heightmap");
1462 Do not generate over-limit
1464 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1465 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1466 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1467 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1468 throw InvalidPositionException("emergeSector(): pos. over limit");
1471 Generate sector and heightmaps
1474 // Number of heightmaps in sector in each direction
1475 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1477 // Heightmap side width
1478 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1480 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1482 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1483 " heightmaps and objects"<<std::endl;*/
1485 // Loop through sub-heightmaps
1486 for(s16 y=0; y<hm_split; y++)
1487 for(s16 x=0; x<hm_split; x++)
1489 v2s16 p_in_sector = v2s16(x,y);
1490 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1492 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1493 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1494 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1495 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1498 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1499 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1502 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1504 sector->setHeightmap(p_in_sector, hm);
1506 //TODO: Make these values configurable
1507 hm->generateContinued(1.0, 0.2, corners);
1508 //hm->generateContinued(2.0, 0.2, corners);
1518 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1519 sector->setObjects(objects);
1521 v2s16 mhm_p = p2d * hm_split;
1523 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1524 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1525 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1526 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1529 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1530 float avgslope = 0.0;
1531 avgslope += fabs(avgheight - corners[0]);
1532 avgslope += fabs(avgheight - corners[1]);
1533 avgslope += fabs(avgheight - corners[2]);
1534 avgslope += fabs(avgheight - corners[3]);
1536 avgslope /= MAP_BLOCKSIZE;
1537 //dstream<<"avgslope="<<avgslope<<std::endl;
1539 float pitness = 0.0;
1541 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1544 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1547 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1550 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1554 pitness /= MAP_BLOCKSIZE;
1555 //dstream<<"pitness="<<pitness<<std::endl;
1558 Plant some trees if there is not much slope
1561 // Avgslope is the derivative of a hill
1562 float t = avgslope * avgslope;
1563 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1566 tree_max = a / (t/0.03);
1569 u32 count = (rand()%(tree_max+1));
1570 //u32 count = tree_max;
1571 for(u32 i=0; i<count; i++)
1573 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1574 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1575 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1578 objects->insert(v3s16(x, y, z),
1579 SECTOR_OBJECT_TREE_1);
1583 Plant some bushes if sector is pit-like
1586 // Pitness usually goes at around -0.5...0.5
1588 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1590 bush_max = (pitness*a*4);
1593 u32 count = (rand()%(bush_max+1));
1594 for(u32 i=0; i<count; i++)
1596 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1597 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1598 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1601 objects->insert(v3s16(x, y, z),
1602 SECTOR_OBJECT_BUSH_1);
1606 Add ravine (randomly)
1608 if(m_params.ravines_amount != 0)
1610 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1613 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1614 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1617 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1618 objects->insert(v3s16(x, y, z),
1619 SECTOR_OBJECT_RAVINE);
1626 JMutexAutoLock lock(m_sector_mutex);
1627 m_sectors.insert(p2d, sector);
1632 MapBlock * ServerMap::emergeBlock(
1634 bool only_from_disk,
1635 core::map<v3s16, MapBlock*> &changed_blocks,
1636 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1639 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1641 p.X, p.Y, p.Z, only_from_disk);
1643 /*dstream<<"ServerMap::emergeBlock(): "
1644 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1645 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1646 v2s16 p2d(p.X, p.Z);
1649 This will create or load a sector if not found in memory.
1650 If block exists on disk, it will be loaded.
1652 NOTE: On old save formats, this will be slow, as it generates
1653 lighting on blocks for them.
1655 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1656 assert(sector->getId() == MAPSECTOR_SERVER);
1658 // Try to get a block from the sector
1659 MapBlock *block = NULL;
1660 bool not_on_disk = false;
1662 block = sector->getBlockNoCreate(block_y);
1663 if(block->isDummy() == true)
1668 catch(InvalidPositionException &e)
1674 If block was not found on disk and not going to generate a
1675 new one, make sure there is a dummy block in place.
1677 if(not_on_disk && only_from_disk)
1681 // Create dummy block
1682 block = new MapBlock(this, p, true);
1684 // Add block to sector
1685 sector->insertBlock(block);
1691 //dstream<<"Not found on disk, generating."<<std::endl;
1694 Do not generate over-limit
1696 if(blockpos_over_limit(p))
1697 throw InvalidPositionException("emergeBlock(): pos. over limit");
1702 Go on generating the block.
1704 TODO: If a dungeon gets generated so that it's side gets
1705 revealed to the outside air, the lighting should be
1710 If block doesn't exist, create one.
1711 If it exists, it is a dummy. In that case unDummify() it.
1715 block = sector->createBlankBlockNoInsert(block_y);
1719 // Remove the block so that nobody can get a half-generated one.
1720 sector->removeBlock(block);
1721 // Allocate the block to be a proper one.
1725 // Randomize a bit. This makes dungeons.
1726 /*bool low_block_is_empty = false;
1728 low_block_is_empty = true;*/
1731 //const s32 ued = 8;
1732 bool underground_emptiness[ued*ued*ued];
1733 for(s32 i=0; i<ued*ued*ued; i++)
1735 underground_emptiness[i] = ((rand() % 5) == 0);
1740 This is a messy hack to sort the emptiness a bit
1742 for(s32 j=0; j<2; j++)
1743 for(s32 y0=0; y0<ued; y0++)
1744 for(s32 z0=0; z0<ued; z0++)
1745 for(s32 x0=0; x0<ued; x0++)
1748 bool &e0 = underground_emptiness[
1749 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1750 +ued*(y0*ued/MAP_BLOCKSIZE)
1751 +(x0*ued/MAP_BLOCKSIZE)];
1754 v3s16(0,0,1), // back
1755 v3s16(1,0,0), // right
1756 v3s16(0,0,-1), // front
1757 v3s16(-1,0,0), // left
1758 /*v3s16(0,1,0), // top
1759 v3s16(0,-1,0), // bottom*/
1761 for(s32 i=0; i<4; i++)
1763 v3s16 p1 = p0 + dirs[i];
1764 if(isInArea(p1, ued) == false)
1766 bool &e1 = underground_emptiness[
1767 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1768 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1769 +(p1.X*ued/MAP_BLOCKSIZE)];
1774 v3s16(0,1,0), // top
1775 v3s16(0,-1,0), // bottom
1776 /*v3s16(0,0,1), // back
1777 v3s16(1,0,0), // right
1778 v3s16(0,0,-1), // front
1779 v3s16(-1,0,0), // left*/
1781 for(s32 i=0; i<2; i++)
1783 v3s16 p2 = p1 + dirs[i];
1786 if(isInArea(p2, ued) == false)
1788 bool &e2 = underground_emptiness[
1789 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1790 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1791 +(p2.X*ued/MAP_BLOCKSIZE)];
1806 // This is the basic material of what the visible flat ground
1808 u8 material = CONTENT_GRASS;
1810 u8 water_material = CONTENT_WATER;
1811 if(g_settings.getBool("endless_water"))
1812 water_material = CONTENT_OCEAN;
1814 s32 lowest_ground_y = 32767;
1815 s32 highest_ground_y = -32768;
1818 //sector->printHeightmaps();
1820 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1821 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1823 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1825 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1826 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1827 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1829 dstream<<"WARNING: Surface height not found in sector "
1830 "for block that is being emerged"<<std::endl;
1834 s16 surface_y = surface_y_f;
1835 //avg_ground_y += surface_y;
1836 if(surface_y < lowest_ground_y)
1837 lowest_ground_y = surface_y;
1838 if(surface_y > highest_ground_y)
1839 highest_ground_y = surface_y;
1841 s32 surface_depth = 0;
1843 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1845 //float min_slope = 0.45;
1846 //float max_slope = 0.85;
1847 float min_slope = 0.70;
1848 float max_slope = 1.20;
1849 float min_slope_depth = 4.0;
1850 //float min_slope_depth = 5.0;
1851 float max_slope_depth = 0;
1852 if(slope < min_slope)
1853 surface_depth = min_slope_depth;
1854 else if(slope > max_slope)
1855 surface_depth = max_slope_depth;
1857 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1859 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1861 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1866 NOTE: If there are some man-made structures above the
1867 newly created block, they won't be taken into account.
1869 if(real_y > surface_y)
1870 n.setLight(LIGHT_SUN);
1876 // If node is very low
1877 /*if(real_y <= surface_y - 7)
1880 if(underground_emptiness[
1881 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1882 +ued*(y0*ued/MAP_BLOCKSIZE)
1883 +(x0*ued/MAP_BLOCKSIZE)])
1889 n.d = CONTENT_STONE;
1892 // If node is under surface level
1893 else if(real_y <= surface_y - surface_depth)
1894 n.d = CONTENT_STONE;
1896 if(real_y <= surface_y - surface_depth)
1899 if(underground_emptiness[
1900 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1901 +ued*(y0*ued/MAP_BLOCKSIZE)
1902 +(x0*ued/MAP_BLOCKSIZE)])
1908 n.d = CONTENT_STONE;
1911 // If node is at or under heightmap y
1912 else if(real_y <= surface_y)
1914 // If under water level, it's mud
1915 if(real_y < WATER_LEVEL)
1917 // Only the topmost node is grass
1918 else if(real_y <= surface_y - 1)
1920 // Else it's the main material
1924 // If node is over heightmap y
1926 // If under water level, it's water
1927 if(real_y < WATER_LEVEL)
1929 n.d = water_material;
1930 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1936 block->setNode(v3s16(x0,y0,z0), n);
1941 Calculate is_underground
1943 // Probably underground if the highest part of block is under lowest
1945 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1946 block->setIsUnderground(is_underground);
1949 Force lighting update if some part of block is underground
1950 This is needed because of caves.
1953 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1954 if(some_part_underground)
1955 //if(is_underground)
1957 lighting_invalidated_blocks[block->getPos()] = block;
1966 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1967 for(s16 i=0; i<underground_level*3; i++)
1972 (rand()%(MAP_BLOCKSIZE-2))+1,
1973 (rand()%(MAP_BLOCKSIZE-2))+1,
1974 (rand()%(MAP_BLOCKSIZE-2))+1
1980 if(is_ground_content(block->getNode(cp).d))
1982 block->setNode(cp, n);
1984 for(u16 i=0; i<26; i++)
1986 if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1988 block->setNode(cp+g_26dirs[i], n);
1995 Create a few rats in empty blocks underground
1999 //for(u16 i=0; i<2; i++)
2002 (rand()%(MAP_BLOCKSIZE-2))+1,
2003 (rand()%(MAP_BLOCKSIZE-2))+1,
2004 (rand()%(MAP_BLOCKSIZE-2))+1
2007 // Check that the place is empty
2008 //if(!is_ground_content(block->getNode(cp).d))
2011 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2012 block->addObject(obj);
2018 Add block to sector.
2020 sector->insertBlock(block);
2023 Do some interpolation for dungeons
2028 TimeTaker timer("interpolation", g_device);
2030 MapVoxelManipulator vmanip(this);
2032 v3s16 relpos = block->getPosRelative();
2034 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
2035 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
2036 /*vmanip.interpolate(VoxelArea(relpos,
2037 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
2039 core::map<v3s16, MapBlock*> modified_blocks;
2040 vmanip.blitBack(modified_blocks);
2041 dstream<<"blitBack modified "<<modified_blocks.size()
2042 <<" blocks"<<std::endl;
2044 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
2045 for(core::map<v3s16, MapBlock*>::Iterator
2046 i = modified_blocks.getIterator();
2047 i.atEnd() == false; i++)
2049 MapBlock *block = i.getNode()->getValue();
2051 changed_blocks.insert(block->getPos(), block);
2052 //lighting_invalidated_blocks.insert(block->getPos(), block);
2062 // An y-wise container of changed blocks
2063 core::map<s16, MapBlock*> changed_blocks_sector;
2066 Check if any sector's objects can be placed now.
2069 core::map<v3s16, u8> *objects = sector->getObjects();
2070 core::list<v3s16> objects_to_remove;
2071 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2072 i.atEnd() == false; i++)
2074 v3s16 p = i.getNode()->getKey();
2076 u8 d = i.getNode()->getValue();
2078 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2083 if(d == SECTOR_OBJECT_TEST)
2085 if(sector->isValidArea(p + v3s16(0,0,0),
2086 p + v3s16(0,0,0), &changed_blocks_sector))
2089 n.d = CONTENT_LIGHT;
2090 sector->setNode(p, n);
2091 objects_to_remove.push_back(p);
2094 else if(d == SECTOR_OBJECT_TREE_1)
2096 v3s16 p_min = p + v3s16(-1,0,-1);
2097 v3s16 p_max = p + v3s16(1,4,1);
2098 if(sector->isValidArea(p_min, p_max,
2099 &changed_blocks_sector))
2103 sector->setNode(p+v3s16(0,0,0), n);
2104 sector->setNode(p+v3s16(0,1,0), n);
2105 sector->setNode(p+v3s16(0,2,0), n);
2106 sector->setNode(p+v3s16(0,3,0), n);
2108 n.d = CONTENT_LEAVES;
2110 sector->setNode(p+v3s16(0,4,0), n);
2112 sector->setNode(p+v3s16(-1,4,0), n);
2113 sector->setNode(p+v3s16(1,4,0), n);
2114 sector->setNode(p+v3s16(0,4,-1), n);
2115 sector->setNode(p+v3s16(0,4,1), n);
2116 sector->setNode(p+v3s16(1,4,1), n);
2117 sector->setNode(p+v3s16(-1,4,1), n);
2118 sector->setNode(p+v3s16(-1,4,-1), n);
2119 sector->setNode(p+v3s16(1,4,-1), n);
2121 sector->setNode(p+v3s16(-1,3,0), n);
2122 sector->setNode(p+v3s16(1,3,0), n);
2123 sector->setNode(p+v3s16(0,3,-1), n);
2124 sector->setNode(p+v3s16(0,3,1), n);
2125 sector->setNode(p+v3s16(1,3,1), n);
2126 sector->setNode(p+v3s16(-1,3,1), n);
2127 sector->setNode(p+v3s16(-1,3,-1), n);
2128 sector->setNode(p+v3s16(1,3,-1), n);
2130 objects_to_remove.push_back(p);
2132 // Lighting has to be recalculated for this one.
2133 sector->getBlocksInArea(p_min, p_max,
2134 lighting_invalidated_blocks);
2137 else if(d == SECTOR_OBJECT_BUSH_1)
2139 if(sector->isValidArea(p + v3s16(0,0,0),
2140 p + v3s16(0,0,0), &changed_blocks_sector))
2143 n.d = CONTENT_LEAVES;
2144 sector->setNode(p+v3s16(0,0,0), n);
2146 objects_to_remove.push_back(p);
2149 else if(d == SECTOR_OBJECT_RAVINE)
2152 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2153 v3s16 p_max = p + v3s16(6,6,6);
2154 if(sector->isValidArea(p_min, p_max,
2155 &changed_blocks_sector))
2158 n.d = CONTENT_STONE;
2161 s16 depth = maxdepth + (rand()%10);
2163 s16 minz = -6 - (-2);
2165 for(s16 x=-6; x<=6; x++)
2167 z += -1 + (rand()%3);
2172 for(s16 y=depth+(rand()%2); y<=6; y++)
2174 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2177 v3s16 p2 = p + v3s16(x,y,z-2);
2178 if(is_ground_content(sector->getNode(p2).d))
2179 sector->setNode(p2, n);
2182 v3s16 p2 = p + v3s16(x,y,z-1);
2183 if(is_ground_content(sector->getNode(p2).d))
2184 sector->setNode(p2, n2);
2187 v3s16 p2 = p + v3s16(x,y,z+0);
2188 if(is_ground_content(sector->getNode(p2).d))
2189 sector->setNode(p2, n2);
2192 v3s16 p2 = p + v3s16(x,y,z+1);
2193 if(is_ground_content(sector->getNode(p2).d))
2194 sector->setNode(p2, n);
2197 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2198 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2202 objects_to_remove.push_back(p);
2204 // Lighting has to be recalculated for this one.
2205 sector->getBlocksInArea(p_min, p_max,
2206 lighting_invalidated_blocks);
2211 dstream<<"ServerMap::emergeBlock(): "
2212 "Invalid heightmap object"
2217 catch(InvalidPositionException &e)
2219 dstream<<"WARNING: "<<__FUNCTION_NAME
2220 <<": while inserting object "<<(int)d
2221 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2222 <<" InvalidPositionException.what()="
2223 <<e.what()<<std::endl;
2224 // This is not too fatal and seems to happen sometimes.
2229 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2230 i != objects_to_remove.end(); i++)
2232 objects->remove(*i);
2235 for(core::map<s16, MapBlock*>::Iterator
2236 i = changed_blocks_sector.getIterator();
2237 i.atEnd() == false; i++)
2239 MapBlock *block = i.getNode()->getValue();
2241 changed_blocks.insert(block->getPos(), block);
2247 void ServerMap::createDir(std::string path)
2249 if(fs::CreateDir(path) == false)
2251 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2252 <<"\""<<path<<"\""<<std::endl;
2253 throw BaseException("ServerMap failed to create directory");
2257 std::string ServerMap::getSectorSubDir(v2s16 pos)
2260 snprintf(cc, 9, "%.4x%.4x",
2261 (unsigned int)pos.X&0xffff,
2262 (unsigned int)pos.Y&0xffff);
2264 return std::string(cc);
2267 std::string ServerMap::getSectorDir(v2s16 pos)
2269 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2272 v2s16 ServerMap::getSectorPos(std::string dirname)
2274 if(dirname.size() != 8)
2275 throw InvalidFilenameException("Invalid sector directory name");
2277 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2279 throw InvalidFilenameException("Invalid sector directory name");
2280 v2s16 pos((s16)x, (s16)y);
2284 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2286 v2s16 p2d = getSectorPos(sectordir);
2288 if(blockfile.size() != 4){
2289 throw InvalidFilenameException("Invalid block filename");
2292 int r = sscanf(blockfile.c_str(), "%4x", &y);
2294 throw InvalidFilenameException("Invalid block filename");
2295 return v3s16(p2d.X, y, p2d.Y);
2299 #define ENABLE_SECTOR_SAVING 1
2300 #define ENABLE_SECTOR_LOADING 1
2301 #define ENABLE_BLOCK_SAVING 1
2302 #define ENABLE_BLOCK_LOADING 1
2304 void ServerMap::save(bool only_changed)
2306 DSTACK(__FUNCTION_NAME);
2307 if(m_map_saving_enabled == false)
2309 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2313 if(only_changed == false)
2314 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2317 saveMasterHeightmap();
2319 u32 sector_meta_count = 0;
2320 u32 block_count = 0;
2323 JMutexAutoLock lock(m_sector_mutex);
2325 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2326 for(; i.atEnd() == false; i++)
2328 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2329 assert(sector->getId() == MAPSECTOR_SERVER);
2331 if(ENABLE_SECTOR_SAVING)
2333 if(sector->differs_from_disk || only_changed == false)
2335 saveSectorMeta(sector);
2336 sector_meta_count++;
2339 if(ENABLE_BLOCK_SAVING)
2341 core::list<MapBlock*> blocks;
2342 sector->getBlocks(blocks);
2343 core::list<MapBlock*>::Iterator j;
2344 for(j=blocks.begin(); j!=blocks.end(); j++)
2346 MapBlock *block = *j;
2347 if(block->getChangedFlag() || only_changed == false)
2358 u32 deleted_count = 0;
2359 deleted_count = deleteUnusedSectors
2360 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2363 Only print if something happened or saved whole map
2365 if(only_changed == false || sector_meta_count != 0
2366 || block_count != 0 || deleted_count != 0)
2368 dstream<<DTIME<<"ServerMap: Written: "
2369 <<sector_meta_count<<" sector metadata files, "
2370 <<block_count<<" block files, "
2371 <<deleted_count<<" sectors unloaded from memory."
2376 void ServerMap::loadAll()
2378 DSTACK(__FUNCTION_NAME);
2379 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2381 loadMasterHeightmap();
2383 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2385 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2387 JMutexAutoLock lock(m_sector_mutex);
2390 s32 printed_counter = -100000;
2391 s32 count = list.size();
2393 std::vector<fs::DirListNode>::iterator i;
2394 for(i=list.begin(); i!=list.end(); i++)
2396 if(counter > printed_counter + 10)
2398 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2399 printed_counter = counter;
2403 MapSector *sector = NULL;
2405 // We want directories
2409 sector = loadSectorMeta(i->name);
2411 catch(InvalidFilenameException &e)
2413 // This catches unknown crap in directory
2416 if(ENABLE_BLOCK_LOADING)
2418 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2419 (m_savedir+"/sectors/"+i->name);
2420 std::vector<fs::DirListNode>::iterator i2;
2421 for(i2=list2.begin(); i2!=list2.end(); i2++)
2427 loadBlock(i->name, i2->name, sector);
2429 catch(InvalidFilenameException &e)
2431 // This catches unknown crap in directory
2436 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2439 void ServerMap::saveMasterHeightmap()
2441 DSTACK(__FUNCTION_NAME);
2442 createDir(m_savedir);
2444 std::string fullpath = m_savedir + "/master_heightmap";
2445 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2446 if(o.good() == false)
2447 throw FileNotGoodException("Cannot open master heightmap");
2449 // Format used for writing
2450 u8 version = SER_FMT_VER_HIGHEST;
2453 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2455 [0] u8 serialization version
2456 [1] X master heightmap
2458 u32 fullsize = 1 + hmdata.getSize();
2459 SharedBuffer<u8> data(fullsize);
2462 memcpy(&data[1], *hmdata, hmdata.getSize());
2464 o.write((const char*)*data, fullsize);
2467 m_heightmap->serialize(o, version);
2470 void ServerMap::loadMasterHeightmap()
2472 DSTACK(__FUNCTION_NAME);
2473 std::string fullpath = m_savedir + "/master_heightmap";
2474 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2475 if(is.good() == false)
2476 throw FileNotGoodException("Cannot open master heightmap");
2478 if(m_heightmap != NULL)
2481 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2484 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2486 DSTACK(__FUNCTION_NAME);
2487 // Format used for writing
2488 u8 version = SER_FMT_VER_HIGHEST;
2490 v2s16 pos = sector->getPos();
2491 createDir(m_savedir);
2492 createDir(m_savedir+"/sectors");
2493 std::string dir = getSectorDir(pos);
2496 std::string fullpath = dir + "/heightmap";
2497 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2498 if(o.good() == false)
2499 throw FileNotGoodException("Cannot open master heightmap");
2501 sector->serialize(o, version);
2503 sector->differs_from_disk = false;
2506 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2508 DSTACK(__FUNCTION_NAME);
2510 v2s16 p2d = getSectorPos(dirname);
2511 std::string dir = m_savedir + "/sectors/" + dirname;
2513 std::string fullpath = dir + "/heightmap";
2514 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2515 if(is.good() == false)
2516 throw FileNotGoodException("Cannot open sector heightmap");
2518 ServerMapSector *sector = ServerMapSector::deSerialize
2519 (is, this, p2d, &m_hwrapper, m_sectors);
2521 sector->differs_from_disk = false;
2526 bool ServerMap::loadSectorFull(v2s16 p2d)
2528 DSTACK(__FUNCTION_NAME);
2529 std::string sectorsubdir = getSectorSubDir(p2d);
2531 MapSector *sector = NULL;
2533 JMutexAutoLock lock(m_sector_mutex);
2536 sector = loadSectorMeta(sectorsubdir);
2538 catch(InvalidFilenameException &e)
2542 catch(FileNotGoodException &e)
2546 catch(std::exception &e)
2551 if(ENABLE_BLOCK_LOADING)
2553 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2554 (m_savedir+"/sectors/"+sectorsubdir);
2555 std::vector<fs::DirListNode>::iterator i2;
2556 for(i2=list2.begin(); i2!=list2.end(); i2++)
2562 loadBlock(sectorsubdir, i2->name, sector);
2564 catch(InvalidFilenameException &e)
2566 // This catches unknown crap in directory
2574 bool ServerMap::deFlushSector(v2s16 p2d)
2576 DSTACK(__FUNCTION_NAME);
2577 // See if it already exists in memory
2579 MapSector *sector = getSectorNoGenerate(p2d);
2582 catch(InvalidPositionException &e)
2585 Try to load the sector from disk.
2587 if(loadSectorFull(p2d) == true)
2596 void ServerMap::saveBlock(MapBlock *block)
2598 DSTACK(__FUNCTION_NAME);
2600 Dummy blocks are not written
2602 if(block->isDummy())
2604 /*v3s16 p = block->getPos();
2605 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2606 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2610 // Format used for writing
2611 u8 version = SER_FMT_VER_HIGHEST;
2613 v3s16 p3d = block->getPos();
2614 v2s16 p2d(p3d.X, p3d.Z);
2615 createDir(m_savedir);
2616 createDir(m_savedir+"/sectors");
2617 std::string dir = getSectorDir(p2d);
2620 // Block file is map/sectors/xxxxxxxx/xxxx
2622 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2623 std::string fullpath = dir + "/" + cc;
2624 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2625 if(o.good() == false)
2626 throw FileNotGoodException("Cannot open block data");
2629 [0] u8 serialization version
2632 o.write((char*)&version, 1);
2634 block->serialize(o, version);
2637 Versions up from 9 have block objects.
2641 block->serializeObjects(o, version);
2644 // We just wrote it to the disk
2645 block->resetChangedFlag();
2648 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2650 DSTACK(__FUNCTION_NAME);
2654 // Block file is map/sectors/xxxxxxxx/xxxx
2655 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2656 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2657 if(is.good() == false)
2658 throw FileNotGoodException("Cannot open block file");
2660 v3s16 p3d = getBlockPos(sectordir, blockfile);
2661 v2s16 p2d(p3d.X, p3d.Z);
2663 assert(sector->getPos() == p2d);
2665 u8 version = SER_FMT_VER_INVALID;
2666 is.read((char*)&version, 1);
2668 /*u32 block_size = MapBlock::serializedLength(version);
2669 SharedBuffer<u8> data(block_size);
2670 is.read((char*)*data, block_size);*/
2672 // This will always return a sector because we're the server
2673 //MapSector *sector = emergeSector(p2d);
2675 MapBlock *block = NULL;
2676 bool created_new = false;
2678 block = sector->getBlockNoCreate(p3d.Y);
2680 catch(InvalidPositionException &e)
2682 block = sector->createBlankBlockNoInsert(p3d.Y);
2686 // deserialize block data
2687 block->deSerialize(is, version);
2690 Versions up from 9 have block objects.
2694 block->updateObjects(is, version, NULL);
2698 sector->insertBlock(block);
2701 Convert old formats to new and save
2704 // Save old format blocks in new format
2705 if(version < SER_FMT_VER_HIGHEST)
2710 // We just loaded it from the disk, so it's up-to-date.
2711 block->resetChangedFlag();
2714 catch(SerializationError &e)
2716 dstream<<"WARNING: Invalid block data on disk "
2717 "(SerializationError). Ignoring."
2722 // Gets from master heightmap
2723 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2725 assert(m_heightmap != NULL);
2733 corners[0] = m_heightmap->getGroundHeight
2734 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2735 corners[1] = m_heightmap->getGroundHeight
2736 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2737 corners[2] = m_heightmap->getGroundHeight
2738 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2739 corners[3] = m_heightmap->getGroundHeight
2740 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2743 void ServerMap::PrintInfo(std::ostream &out)
2752 ClientMap::ClientMap(
2754 video::SMaterial *materials,
2755 scene::ISceneNode* parent,
2756 scene::ISceneManager* mgr,
2760 scene::ISceneNode(parent, mgr, id),
2762 m_materials(materials),
2765 /*m_box = core::aabbox3d<f32>(0,0,0,
2766 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2767 /*m_box = core::aabbox3d<f32>(0,0,0,
2768 map->getSizeNodes().X * BS,
2769 map->getSizeNodes().Y * BS,
2770 map->getSizeNodes().Z * BS);*/
2771 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2772 BS*1000000,BS*1000000,BS*1000000);
2777 ClientMap::~ClientMap()
2779 JMutexAutoLock lock(mesh_mutex);
2788 MapSector * ClientMap::emergeSector(v2s16 p2d)
2790 DSTACK(__FUNCTION_NAME);
2791 // Check that it doesn't exist already
2793 return getSectorNoGenerate(p2d);
2795 catch(InvalidPositionException &e)
2799 // Create a sector with no heightmaps
2800 ClientMapSector *sector = new ClientMapSector(this, p2d);
2803 JMutexAutoLock lock(m_sector_mutex);
2804 m_sectors.insert(p2d, sector);
2810 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2812 DSTACK(__FUNCTION_NAME);
2813 ClientMapSector *sector = NULL;
2815 JMutexAutoLock lock(m_sector_mutex);
2817 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2821 sector = (ClientMapSector*)n->getValue();
2822 assert(sector->getId() == MAPSECTOR_CLIENT);
2826 sector = new ClientMapSector(this, p2d);
2828 JMutexAutoLock lock(m_sector_mutex);
2829 m_sectors.insert(p2d, sector);
2833 sector->deSerialize(is);
2836 void ClientMap::renderMap(video::IVideoDriver* driver,
2837 video::SMaterial *materials, s32 pass)
2839 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2840 DSTACK(__FUNCTION_NAME);
2842 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2845 Get time for measuring timeout.
2847 Measuring time is very useful for long delays when the
2848 machine is swapping a lot.
2850 int time1 = time(0);
2853 Collect all blocks that are in the view range
2855 Should not optimize more here as we want to auto-update
2856 all changed nodes in viewing range at the next step.
2859 s16 viewing_range_nodes;
2860 bool viewing_range_all;
2862 JMutexAutoLock lock(g_range_mutex);
2863 viewing_range_nodes = g_viewing_range_nodes;
2864 viewing_range_all = g_viewing_range_all;
2867 m_camera_mutex.Lock();
2868 v3f camera_position = m_camera_position;
2869 v3f camera_direction = m_camera_direction;
2870 m_camera_mutex.Unlock();
2873 Get all blocks and draw all visible ones
2876 v3s16 cam_pos_nodes(
2877 camera_position.X / BS,
2878 camera_position.Y / BS,
2879 camera_position.Z / BS);
2881 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2883 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2884 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2886 // Take a fair amount as we will be dropping more out later
2888 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2889 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2890 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2892 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2893 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2894 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2896 u32 vertex_count = 0;
2898 core::map<v2s16, MapSector*>::Iterator si;
2900 //NOTE: The sectors map should be locked but we're not doing it
2901 // because it'd cause too much delays
2903 si = m_sectors.getIterator();
2904 for(; si.atEnd() == false; si++)
2907 static int timecheck_counter = 0;
2908 timecheck_counter++;
2909 if(timecheck_counter > 50)
2911 int time2 = time(0);
2912 if(time2 > time1 + 4)
2914 dstream<<"ClientMap::renderMap(): "
2915 "Rendering takes ages, returning."
2922 MapSector *sector = si.getNode()->getValue();
2923 v2s16 sp = sector->getPos();
2925 if(viewing_range_all == false)
2927 if(sp.X < p_blocks_min.X
2928 || sp.X > p_blocks_max.X
2929 || sp.Y < p_blocks_min.Z
2930 || sp.Y > p_blocks_max.Z)
2934 core::list< MapBlock * > sectorblocks;
2935 sector->getBlocks(sectorblocks);
2941 core::list< MapBlock * >::Iterator i;
2942 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2944 MapBlock *block = *i;
2947 Compare block position to camera position, skip
2948 if not seen on display
2951 v3s16 blockpos_nodes = block->getPosRelative();
2953 // Block center position
2955 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2956 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2957 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2960 // Block position relative to camera
2961 v3f blockpos_relative = blockpos - camera_position;
2963 // Distance in camera direction (+=front, -=back)
2964 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2967 f32 d = blockpos_relative.getLength();
2969 if(viewing_range_all == false)
2971 // If block is far away, don't draw it
2972 if(d > viewing_range_nodes * BS)
2976 // Maximum radius of a block
2977 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2979 // If block is (nearly) touching the camera, don't
2980 // bother validating further (that is, render it anyway)
2981 if(d > block_max_radius * 1.5)
2983 // Cosine of the angle between the camera direction
2984 // and the block direction (camera_direction is an unit vector)
2985 f32 cosangle = dforward / d;
2987 // Compensate for the size of the block
2988 // (as the block has to be shown even if it's a bit off FOV)
2989 // This is an estimate.
2990 cosangle += block_max_radius / dforward;
2992 // If block is not in the field of view, skip it
2993 //if(cosangle < cos(FOV_ANGLE/2))
2994 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2999 Draw the faces of the block
3003 JMutexAutoLock lock(block->mesh_mutex);
3005 // Cancel if block has no mesh
3006 if(block->mesh == NULL)
3009 u32 c = block->mesh->getMeshBufferCount();
3011 for(u32 i=0; i<c; i++)
3013 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
3014 const video::SMaterial& material = buf->getMaterial();
3015 video::IMaterialRenderer* rnd =
3016 driver->getMaterialRenderer(material.MaterialType);
3017 bool transparent = (rnd && rnd->isTransparent());
3018 // Render transparent on transparent pass and likewise.
3019 if(transparent == is_transparent_pass)
3021 driver->setMaterial(buf->getMaterial());
3022 driver->drawMeshBuffer(buf);
3023 vertex_count += buf->getVertexCount();
3027 } // foreach sectorblocks
3030 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3031 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3034 void ClientMap::updateMesh()
3037 DSTACK(__FUNCTION_NAME);
3040 Check what sectors don't draw anything useful at ground level
3041 and create a mesh of the rough heightmap at those positions.
3044 m_camera_mutex.Lock();
3045 v3f camera_position = m_camera_position;
3046 v3f camera_direction = m_camera_direction;
3047 m_camera_mutex.Unlock();
3049 v3s16 cam_pos_nodes(
3050 camera_position.X / BS,
3051 camera_position.Y / BS,
3052 camera_position.Z / BS);
3054 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
3056 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3057 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3059 // Take a fair amount as we will be dropping more out later
3061 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3062 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3063 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3065 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3066 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3067 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3073 scene::SMesh *mesh_new = new scene::SMesh();
3074 //scene::IMeshBuffer *buf = NULL;
3075 scene::SMeshBuffer *buf = NULL;
3077 u8 material_in_use = 0;
3080 Loop through sectors
3083 for(core::map<v2s16, MapSector*>::Iterator
3084 si = m_sectors.getIterator();
3085 si.atEnd() == false; si++)
3087 MapSector *sector = si.getNode()->getValue();
3089 if(sector->getId() != MAPSECTOR_CLIENT)
3091 dstream<<"WARNING: Client has a non-client sector"
3096 ClientMapSector *cs = (ClientMapSector*)sector;
3098 v2s16 sp = sector->getPos();
3100 if(sp.X < p_blocks_min.X
3101 || sp.X > p_blocks_max.X
3102 || sp.Y < p_blocks_min.Z
3103 || sp.Y > p_blocks_max.Z)
3107 Get some ground level info
3119 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
3121 s16 cn_max = -32768;
3122 for(s16 i=0; i<4; i++)
3129 s16 cn_slope = cn_max - cn_min;
3132 Generate this part of the heightmap mesh
3136 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
3138 else if(cn_slope <= MAP_BLOCKSIZE)
3143 if(material != material_in_use || buf == NULL)
3145 // Try to get a meshbuffer associated with the material
3146 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
3147 (g_mesh_materials[material]);
3148 // If not found, create one
3151 // This is a "Standard MeshBuffer",
3152 // it's a typedeffed CMeshBuffer<video::S3DVertex>
3153 buf = new scene::SMeshBuffer();
3156 buf->Material = g_mesh_materials[material];
3158 //buf->setHardwareMappingHint(scene::EHM_STATIC);
3160 mesh_new->addMeshBuffer(buf);
3164 material_in_use = material;
3167 // Sector side width in floating-point units
3168 f32 sd = BS * MAP_BLOCKSIZE;
3169 // Sector position in global floating-point units
3170 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
3172 //video::SColor c(255,255,255,255);
3174 video::SColor c(255,cc,cc,cc);
3176 video::S3DVertex vertices[4] =
3178 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
3179 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
3180 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3181 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3183 u16 indices[] = {0,1,2,2,3,0};
3185 buf->append(vertices, 4, indices, 6);
3189 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3197 scene::SMesh *mesh_old = mesh;
3204 mesh_mutex.Unlock();
3206 if(mesh_old != NULL)
3208 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3210 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3211 (g_materials[CONTENT_GRASS]);
3213 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3220 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3225 void ClientMap::PrintInfo(std::ostream &out)
3235 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3240 MapVoxelManipulator::~MapVoxelManipulator()
3242 dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3247 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3249 TimeTaker timer1("emerge", g_device, &emerge_time);
3251 // Units of these are MapBlocks
3252 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3253 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3255 VoxelArea block_area_nodes
3256 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3258 addArea(block_area_nodes);
3260 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3261 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3262 for(s32 x=p_min.X; x<=p_max.X; x++)
3265 core::map<v3s16, bool>::Node *n;
3266 n = m_loaded_blocks.find(p);
3270 bool block_data_inexistent = false;
3273 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3275 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3276 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3279 dstream<<std::endl;*/
3281 MapBlock *block = m_map->getBlockNoCreate(p);
3282 if(block->isDummy())
3283 block_data_inexistent = true;
3285 block->copyTo(*this);
3287 catch(InvalidPositionException &e)
3289 block_data_inexistent = true;
3292 if(block_data_inexistent)
3294 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3295 // Fill with VOXELFLAG_INEXISTENT
3296 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3297 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3299 s32 i = m_area.index(a.MinEdge.X,y,z);
3300 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3304 m_loaded_blocks.insert(p, true);
3307 //dstream<<"emerge done"<<std::endl;
3312 void MapVoxelManipulator::emerge(VoxelArea a)
3314 TimeTaker timer1("emerge", g_device, &emerge_time);
3316 v3s16 size = a.getExtent();
3318 VoxelArea padded = a;
3319 padded.pad(m_area.getExtent() / 4);
3322 for(s16 z=0; z<size.Z; z++)
3323 for(s16 y=0; y<size.Y; y++)
3324 for(s16 x=0; x<size.X; x++)
3327 s32 i = m_area.index(a.MinEdge + p);
3328 // Don't touch nodes that have already been loaded
3329 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3333 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
3334 MapNode n = m_map->getNode(a.MinEdge + p);
3338 catch(InvalidPositionException &e)
3340 m_flags[i] = VOXELFLAG_INEXISTENT;
3348 TODO: Add an option to only update eg. water and air nodes.
3349 This will make it interfere less with important stuff if
3352 void MapVoxelManipulator::blitBack
3353 (core::map<v3s16, MapBlock*> & modified_blocks)
3355 if(m_area.getExtent() == v3s16(0,0,0))
3358 TimeTaker timer1("blitBack", g_device);
3361 Initialize block cache
3363 v3s16 blockpos_last;
3364 MapBlock *block = NULL;
3365 bool block_checked_in_modified = false;
3367 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3368 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3369 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3373 u8 f = m_flags[m_area.index(p)];
3374 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3377 MapNode &n = m_data[m_area.index(p)];
3379 v3s16 blockpos = getNodeBlockPos(p);
3384 if(block == NULL || blockpos != blockpos_last){
3385 block = m_map->getBlockNoCreate(blockpos);
3386 blockpos_last = blockpos;
3387 block_checked_in_modified = false;
3390 // Calculate relative position in block
3391 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3393 // Don't continue if nothing has changed here
3394 if(block->getNode(relpos) == n)
3397 //m_map->setNode(m_area.MinEdge + p, n);
3398 block->setNode(relpos, n);
3401 Make sure block is in modified_blocks
3403 if(block_checked_in_modified == false)
3405 modified_blocks[blockpos] = block;
3406 block_checked_in_modified = true;
3409 catch(InvalidPositionException &e)