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.
22 #include "jmutexautolock.h"
30 MapBlockPointerCache::MapBlockPointerCache(Map *map)
33 m_map->m_blockcachelock.cacheCreated();
35 m_from_cache_count = 0;
39 MapBlockPointerCache::~MapBlockPointerCache()
41 m_map->m_blockcachelock.cacheRemoved();
43 /*dstream<<"MapBlockPointerCache:"
44 <<" from_cache_count="<<m_from_cache_count
45 <<" from_map_count="<<m_from_map_count
49 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
51 core::map<v3s16, MapBlock*>::Node *n = NULL;
61 // Throws InvalidPositionException if not found
62 MapBlock *b = m_map->getBlockNoCreate(p);
72 Map::Map(std::ostream &dout):
74 m_camera_position(0,0,0),
75 m_camera_direction(0,0,1),
80 m_sector_mutex.Init();
81 m_camera_mutex.Init();
82 assert(m_sector_mutex.IsInitialized());
83 assert(m_camera_mutex.IsInitialized());
85 // Get this so that the player can stay on it at first
86 //getSector(v2s16(0,0));
94 /*updater.setRun(false);
95 while(updater.IsRunning())
101 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
102 for(; i.atEnd() == false; i++)
104 MapSector *sector = i.getNode()->getValue();
109 MapSector * Map::getSectorNoGenerate(v2s16 p)
111 JMutexAutoLock lock(m_sector_mutex);
113 if(m_sector_cache != NULL && p == m_sector_cache_p){
114 MapSector * sector = m_sector_cache;
115 // Reset inactivity timer
116 sector->usage_timer = 0.0;
120 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
121 // If sector doesn't exist, throw an exception
124 throw InvalidPositionException();
127 MapSector *sector = n->getValue();
129 // Cache the last result
130 m_sector_cache_p = p;
131 m_sector_cache = sector;
133 //MapSector * ref(sector);
135 // Reset inactivity timer
136 sector->usage_timer = 0.0;
140 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
142 v2s16 p2d(p3d.X, p3d.Z);
143 MapSector * sector = getSectorNoGenerate(p2d);
145 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
150 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
154 v2s16 p2d(p3d.X, p3d.Z);
155 MapSector * sector = getSectorNoGenerate(p2d);
156 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
159 catch(InvalidPositionException &e)
165 f32 Map::getGroundHeight(v2s16 p, bool generate)
168 v2s16 sectorpos = getNodeSectorPos(p);
169 MapSector * sref = getSectorNoGenerate(sectorpos);
170 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
171 f32 y = sref->getGroundHeight(relpos);
174 catch(InvalidPositionException &e)
176 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
180 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
182 /*m_dout<<DTIME<<"Map::setGroundHeight(("
184 <<"), "<<y<<")"<<std::endl;*/
185 v2s16 sectorpos = getNodeSectorPos(p);
186 MapSector * sref = getSectorNoGenerate(sectorpos);
187 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
188 //sref->mutex.Lock();
189 sref->setGroundHeight(relpos, y);
190 //sref->mutex.Unlock();
193 bool Map::isNodeUnderground(v3s16 p)
195 v3s16 blockpos = getNodeBlockPos(p);
197 MapBlock * block = getBlockNoCreate(blockpos);
198 return block->getIsUnderground();
200 catch(InvalidPositionException &e)
207 Goes recursively through the neighbours of the node.
209 Alters only transparent nodes.
211 If the lighting of the neighbour is lower than the lighting of
212 the node was (before changing it to 0 at the step before), the
213 lighting of the neighbour is set to 0 and then the same stuff
214 repeats for the neighbour.
216 The ending nodes of the routine are stored in light_sources.
217 This is useful when a light is removed. In such case, this
218 routine can be called for the light node and then again for
219 light_sources to re-light the area without the removed light.
221 values of from_nodes are lighting values.
223 void Map::unspreadLight(enum LightBank bank,
224 core::map<v3s16, u8> & from_nodes,
225 core::map<v3s16, bool> & light_sources,
226 core::map<v3s16, MapBlock*> & modified_blocks)
229 v3s16(0,0,1), // back
231 v3s16(1,0,0), // right
232 v3s16(0,0,-1), // front
233 v3s16(0,-1,0), // bottom
234 v3s16(-1,0,0), // left
237 if(from_nodes.size() == 0)
240 u32 blockchangecount = 0;
242 core::map<v3s16, u8> unlighted_nodes;
243 core::map<v3s16, u8>::Iterator j;
244 j = from_nodes.getIterator();
247 Initialize block cache
250 MapBlock *block = NULL;
251 // Cache this a bit, too
252 bool block_checked_in_modified = false;
254 for(; j.atEnd() == false; j++)
256 v3s16 pos = j.getNode()->getKey();
257 v3s16 blockpos = getNodeBlockPos(pos);
259 // Only fetch a new block if the block position has changed
261 if(block == NULL || blockpos != blockpos_last){
262 block = getBlockNoCreate(blockpos);
263 blockpos_last = blockpos;
265 block_checked_in_modified = false;
269 catch(InvalidPositionException &e)
277 // Calculate relative position in block
278 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
280 // Get node straight from the block
281 MapNode n = block->getNode(relpos);
283 u8 oldlight = j.getNode()->getValue();
285 // Loop through 6 neighbors
286 for(u16 i=0; i<6; i++)
288 // Get the position of the neighbor node
289 v3s16 n2pos = pos + dirs[i];
291 // Get the block where the node is located
292 v3s16 blockpos = getNodeBlockPos(n2pos);
296 // Only fetch a new block if the block position has changed
298 if(block == NULL || blockpos != blockpos_last){
299 block = getBlockNoCreate(blockpos);
300 blockpos_last = blockpos;
302 block_checked_in_modified = false;
306 catch(InvalidPositionException &e)
311 // Calculate relative position in block
312 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
313 // Get node straight from the block
314 MapNode n2 = block->getNode(relpos);
316 bool changed = false;
318 //TODO: Optimize output by optimizing light_sources?
321 If the neighbor is dimmer than what was specified
322 as oldlight (the light of the previous node)
324 if(n2.getLight(bank) < oldlight)
327 And the neighbor is transparent and it has some light
329 if(n2.light_propagates() && n2.getLight(bank) != 0)
332 Set light to 0 and add to queue
335 u8 current_light = n2.getLight(bank);
336 n2.setLight(bank, 0);
337 block->setNode(relpos, n2);
339 unlighted_nodes.insert(n2pos, current_light);
343 Remove from light_sources if it is there
344 NOTE: This doesn't happen nearly at all
346 /*if(light_sources.find(n2pos))
348 std::cout<<"Removed from light_sources"<<std::endl;
349 light_sources.remove(n2pos);
354 if(light_sources.find(n2pos) != NULL)
355 light_sources.remove(n2pos);*/
358 light_sources.insert(n2pos, true);
361 // Add to modified_blocks
362 if(changed == true && block_checked_in_modified == false)
364 // If the block is not found in modified_blocks, add.
365 if(modified_blocks.find(blockpos) == NULL)
367 modified_blocks.insert(blockpos, block);
369 block_checked_in_modified = true;
372 catch(InvalidPositionException &e)
379 /*dstream<<"unspreadLight(): Changed block "
380 <<blockchangecount<<" times"
381 <<" for "<<from_nodes.size()<<" nodes"
384 if(unlighted_nodes.size() > 0)
385 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
389 A single-node wrapper of the above
391 void Map::unLightNeighbors(enum LightBank bank,
392 v3s16 pos, u8 lightwas,
393 core::map<v3s16, bool> & light_sources,
394 core::map<v3s16, MapBlock*> & modified_blocks)
396 core::map<v3s16, u8> from_nodes;
397 from_nodes.insert(pos, lightwas);
399 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
403 Lights neighbors of from_nodes, collects all them and then
406 void Map::spreadLight(enum LightBank bank,
407 core::map<v3s16, bool> & from_nodes,
408 core::map<v3s16, MapBlock*> & modified_blocks)
410 const v3s16 dirs[6] = {
411 v3s16(0,0,1), // back
413 v3s16(1,0,0), // right
414 v3s16(0,0,-1), // front
415 v3s16(0,-1,0), // bottom
416 v3s16(-1,0,0), // left
419 if(from_nodes.size() == 0)
422 u32 blockchangecount = 0;
424 core::map<v3s16, bool> lighted_nodes;
425 core::map<v3s16, bool>::Iterator j;
426 j = from_nodes.getIterator();
429 Initialize block cache
432 MapBlock *block = NULL;
433 // Cache this a bit, too
434 bool block_checked_in_modified = false;
436 for(; j.atEnd() == false; j++)
437 //for(; j != from_nodes.end(); j++)
439 v3s16 pos = j.getNode()->getKey();
441 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
442 v3s16 blockpos = getNodeBlockPos(pos);
444 // Only fetch a new block if the block position has changed
446 if(block == NULL || blockpos != blockpos_last){
447 block = getBlockNoCreate(blockpos);
448 blockpos_last = blockpos;
450 block_checked_in_modified = false;
454 catch(InvalidPositionException &e)
462 // Calculate relative position in block
463 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
465 // Get node straight from the block
466 MapNode n = block->getNode(relpos);
468 u8 oldlight = n.getLight(bank);
469 u8 newlight = diminish_light(oldlight);
471 // Loop through 6 neighbors
472 for(u16 i=0; i<6; i++){
473 // Get the position of the neighbor node
474 v3s16 n2pos = pos + dirs[i];
476 // Get the block where the node is located
477 v3s16 blockpos = getNodeBlockPos(n2pos);
481 // Only fetch a new block if the block position has changed
483 if(block == NULL || blockpos != blockpos_last){
484 block = getBlockNoCreate(blockpos);
485 blockpos_last = blockpos;
487 block_checked_in_modified = false;
491 catch(InvalidPositionException &e)
496 // Calculate relative position in block
497 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
498 // Get node straight from the block
499 MapNode n2 = block->getNode(relpos);
501 bool changed = false;
503 If the neighbor is brighter than the current node,
504 add to list (it will light up this node on its turn)
506 if(n2.getLight(bank) > undiminish_light(oldlight))
508 lighted_nodes.insert(n2pos, true);
509 //lighted_nodes.push_back(n2pos);
513 If the neighbor is dimmer than how much light this node
514 would spread on it, add to list
516 if(n2.getLight(bank) < newlight)
518 if(n2.light_propagates())
520 n2.setLight(bank, newlight);
521 block->setNode(relpos, n2);
522 lighted_nodes.insert(n2pos, true);
523 //lighted_nodes.push_back(n2pos);
528 // Add to modified_blocks
529 if(changed == true && block_checked_in_modified == false)
531 // If the block is not found in modified_blocks, add.
532 if(modified_blocks.find(blockpos) == NULL)
534 modified_blocks.insert(blockpos, block);
536 block_checked_in_modified = true;
539 catch(InvalidPositionException &e)
546 /*dstream<<"spreadLight(): Changed block "
547 <<blockchangecount<<" times"
548 <<" for "<<from_nodes.size()<<" nodes"
551 if(lighted_nodes.size() > 0)
552 spreadLight(bank, lighted_nodes, modified_blocks);
556 A single-node source variation of the above.
558 void Map::lightNeighbors(enum LightBank bank,
560 core::map<v3s16, MapBlock*> & modified_blocks)
562 core::map<v3s16, bool> from_nodes;
563 from_nodes.insert(pos, true);
564 spreadLight(bank, from_nodes, modified_blocks);
567 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
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 u8 brightest_light = 0;
579 v3s16 brightest_pos(0,0,0);
580 bool found_something = false;
582 // Loop through 6 neighbors
583 for(u16 i=0; i<6; i++){
584 // Get the position of the neighbor node
585 v3s16 n2pos = p + dirs[i];
590 catch(InvalidPositionException &e)
594 if(n2.getLight(bank) > brightest_light || found_something == false){
595 brightest_light = n2.getLight(bank);
596 brightest_pos = n2pos;
597 found_something = true;
601 if(found_something == false)
602 throw InvalidPositionException();
604 return brightest_pos;
608 Propagates sunlight down from a node.
609 Starting point gets sunlight.
611 Returns the lowest y value of where the sunlight went.
613 s16 Map::propagateSunlight(v3s16 start,
614 core::map<v3s16, MapBlock*> & modified_blocks)
619 v3s16 pos(start.X, y, start.Z);
621 v3s16 blockpos = getNodeBlockPos(pos);
624 block = getBlockNoCreate(blockpos);
626 catch(InvalidPositionException &e)
631 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
632 MapNode n = block->getNode(relpos);
634 if(n.sunlight_propagates())
636 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
637 block->setNode(relpos, n);
639 modified_blocks.insert(blockpos, block);
648 void Map::updateLighting(enum LightBank bank,
649 core::map<v3s16, MapBlock*> & a_blocks,
650 core::map<v3s16, MapBlock*> & modified_blocks)
652 /*m_dout<<DTIME<<"Map::updateLighting(): "
653 <<a_blocks.getSize()<<" blocks... ";*/
657 u32 count_was = modified_blocks.size();
659 core::map<v3s16, bool> light_sources;
661 core::map<v3s16, u8> unlight_from;
663 core::map<v3s16, MapBlock*>::Iterator i;
664 i = a_blocks.getIterator();
665 for(; i.atEnd() == false; i++)
667 MapBlock *block = i.getNode()->getValue();
671 // Don't bother with dummy blocks.
675 v3s16 pos = block->getPos();
676 modified_blocks.insert(pos, block);
679 Clear all light from block
681 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
682 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
683 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
688 MapNode n = block->getNode(v3s16(x,y,z));
689 u8 oldlight = n.getLight(bank);
691 block->setNode(v3s16(x,y,z), n);
693 // Collect borders for unlighting
694 if(x==0 || x == MAP_BLOCKSIZE-1
695 || y==0 || y == MAP_BLOCKSIZE-1
696 || z==0 || z == MAP_BLOCKSIZE-1)
698 v3s16 p_map = p + v3s16(
701 MAP_BLOCKSIZE*pos.Z);
702 unlight_from.insert(p_map, oldlight);
705 catch(InvalidPositionException &e)
708 This would happen when dealing with a
712 dstream<<"updateLighting(): InvalidPositionException"
717 if(bank == LIGHTBANK_DAY)
719 bool bottom_valid = block->propagateSunlight(light_sources);
721 // If bottom is valid, we're done.
725 else if(bank == LIGHTBANK_NIGHT)
734 /*dstream<<"Bottom for sunlight-propagated block ("
735 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
738 // Else get the block below and loop to it
742 block = getBlockNoCreate(pos);
744 catch(InvalidPositionException &e)
753 //TimeTaker timer("unspreadLight");
754 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
759 u32 diff = modified_blocks.size() - count_was;
760 count_was = modified_blocks.size();
761 dstream<<"unspreadLight modified "<<diff<<std::endl;
764 // TODO: Spread light from propagated sunlight?
765 // Yes, add it to light_sources... somehow.
766 // It has to be added at somewhere above, in the loop.
768 // NOTE: This actually works fine without doing so
769 // - Find out why it works
772 //TimeTaker timer("spreadLight");
773 spreadLight(bank, light_sources, modified_blocks);
778 u32 diff = modified_blocks.size() - count_was;
779 count_was = modified_blocks.size();
780 dstream<<"spreadLight modified "<<diff<<std::endl;
783 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
786 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
787 core::map<v3s16, MapBlock*> & modified_blocks)
789 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
790 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
793 Update information about whether day and night light differ
795 for(core::map<v3s16, MapBlock*>::Iterator
796 i = modified_blocks.getIterator();
797 i.atEnd() == false; i++)
799 MapBlock *block = i.getNode()->getValue();
800 block->updateDayNightDiff();
805 This is called after changing a node from transparent to opaque.
806 The lighting value of the node should be left as-is after changing
807 other values. This sets the lighting value to 0.
809 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
810 core::map<v3s16, MapBlock*> &modified_blocks)*/
811 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
812 core::map<v3s16, MapBlock*> &modified_blocks)
815 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
816 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
819 From this node to nodes underneath:
820 If lighting is sunlight (1.0), unlight neighbours and
825 v3s16 toppos = p + v3s16(0,1,0);
826 v3s16 bottompos = p + v3s16(0,-1,0);
828 bool node_under_sunlight = true;
829 core::map<v3s16, bool> light_sources;
832 If there is a node at top and it doesn't have sunlight,
833 there has not been any sunlight going down.
835 Otherwise there probably is.
838 MapNode topnode = getNode(toppos);
840 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
841 node_under_sunlight = false;
843 catch(InvalidPositionException &e)
847 if(n.d != CONTENT_TORCH)
850 If there is grass below, change it to mud
853 MapNode bottomnode = getNode(bottompos);
855 if(bottomnode.d == CONTENT_GRASS
856 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
858 bottomnode.d = CONTENT_MUD;
859 setNode(bottompos, bottomnode);
862 catch(InvalidPositionException &e)
867 enum LightBank banks[] =
872 for(s32 i=0; i<2; i++)
874 enum LightBank bank = banks[i];
876 u8 lightwas = getNode(p).getLight(bank);
878 // Add the block of the added node to modified_blocks
879 v3s16 blockpos = getNodeBlockPos(p);
880 MapBlock * block = getBlockNoCreate(blockpos);
881 assert(block != NULL);
882 modified_blocks.insert(blockpos, block);
884 if(isValidPosition(p) == false)
887 // Unlight neighbours of node.
888 // This means setting light of all consequent dimmer nodes
890 // This also collects the nodes at the border which will spread
891 // light again into this.
892 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
900 If node is under sunlight, take all sunlighted nodes under
901 it and clear light from them and from where the light has
903 TODO: This could be optimized by mass-unlighting instead
906 if(node_under_sunlight)
910 //m_dout<<DTIME<<"y="<<y<<std::endl;
911 v3s16 n2pos(p.X, y, p.Z);
917 catch(InvalidPositionException &e)
922 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
924 //m_dout<<DTIME<<"doing"<<std::endl;
925 unLightNeighbors(LIGHTBANK_DAY,
926 n2pos, n2.getLight(LIGHTBANK_DAY),
927 light_sources, modified_blocks);
928 n2.setLight(LIGHTBANK_DAY, 0);
936 for(s32 i=0; i<2; i++)
938 enum LightBank bank = banks[i];
941 Spread light from all nodes that might be capable of doing so
942 TODO: Convert to spreadLight
944 spreadLight(bank, light_sources, modified_blocks);
948 Update information about whether day and night light differ
950 for(core::map<v3s16, MapBlock*>::Iterator
951 i = modified_blocks.getIterator();
952 i.atEnd() == false; i++)
954 MapBlock *block = i.getNode()->getValue();
955 block->updateDayNightDiff();
961 void Map::removeNodeAndUpdate(v3s16 p,
962 core::map<v3s16, MapBlock*> &modified_blocks)
965 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
966 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
968 bool node_under_sunlight = true;
970 v3s16 toppos = p + v3s16(0,1,0);
972 // Node will be replaced with this
973 u8 replace_material = CONTENT_AIR;
976 If there is a node at top and it doesn't have sunlight,
977 there will be no sunlight going down.
980 MapNode topnode = getNode(toppos);
982 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
983 node_under_sunlight = false;
985 catch(InvalidPositionException &e)
989 core::map<v3s16, bool> light_sources;
991 enum LightBank banks[] =
996 for(s32 i=0; i<2; i++)
998 enum LightBank bank = banks[i];
1001 Unlight neighbors (in case the node is a light source)
1003 unLightNeighbors(bank, p,
1004 getNode(p).getLight(bank),
1005 light_sources, modified_blocks);
1010 This also clears the lighting.
1014 n.d = replace_material;
1017 for(s32 i=0; i<2; i++)
1019 enum LightBank bank = banks[i];
1022 Recalculate lighting
1024 spreadLight(bank, light_sources, modified_blocks);
1027 // Add the block of the removed node to modified_blocks
1028 v3s16 blockpos = getNodeBlockPos(p);
1029 MapBlock * block = getBlockNoCreate(blockpos);
1030 assert(block != NULL);
1031 modified_blocks.insert(blockpos, block);
1034 If the removed node was under sunlight, propagate the
1035 sunlight down from it and then light all neighbors
1036 of the propagated blocks.
1038 if(node_under_sunlight)
1040 s16 ybottom = propagateSunlight(p, modified_blocks);
1041 /*m_dout<<DTIME<<"Node was under sunlight. "
1042 "Propagating sunlight";
1043 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1045 for(; y >= ybottom; y--)
1047 v3s16 p2(p.X, y, p.Z);
1048 /*m_dout<<DTIME<<"lighting neighbors of node ("
1049 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1051 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1056 // Set the lighting of this node to 0
1057 // TODO: Is this needed? Lighting is cleared up there already.
1059 MapNode n = getNode(p);
1060 n.setLight(LIGHTBANK_DAY, 0);
1063 catch(InvalidPositionException &e)
1069 for(s32 i=0; i<2; i++)
1071 enum LightBank bank = banks[i];
1073 // Get the brightest neighbour node and propagate light from it
1074 v3s16 n2p = getBrightestNeighbour(bank, p);
1076 MapNode n2 = getNode(n2p);
1077 lightNeighbors(bank, n2p, modified_blocks);
1079 catch(InvalidPositionException &e)
1085 Update information about whether day and night light differ
1087 for(core::map<v3s16, MapBlock*>::Iterator
1088 i = modified_blocks.getIterator();
1089 i.atEnd() == false; i++)
1091 MapBlock *block = i.getNode()->getValue();
1092 block->updateDayNightDiff();
1097 void Map::expireMeshes(bool only_daynight_diffed)
1099 TimeTaker timer("expireMeshes()");
1101 core::map<v2s16, MapSector*>::Iterator si;
1102 si = m_sectors.getIterator();
1103 for(; si.atEnd() == false; si++)
1105 MapSector *sector = si.getNode()->getValue();
1107 core::list< MapBlock * > sectorblocks;
1108 sector->getBlocks(sectorblocks);
1110 core::list< MapBlock * >::Iterator i;
1111 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1113 MapBlock *block = *i;
1115 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1121 JMutexAutoLock lock(block->mesh_mutex);
1122 if(block->mesh != NULL)
1124 /*block->mesh->drop();
1125 block->mesh = NULL;*/
1126 block->setMeshExpired(true);
1133 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1135 assert(mapType() == MAPTYPE_CLIENT);
1138 v3s16 p = blockpos + v3s16(0,0,0);
1139 MapBlock *b = getBlockNoCreate(p);
1140 b->updateMesh(daynight_ratio);
1142 catch(InvalidPositionException &e){}
1145 v3s16 p = blockpos + v3s16(-1,0,0);
1146 MapBlock *b = getBlockNoCreate(p);
1147 b->updateMesh(daynight_ratio);
1149 catch(InvalidPositionException &e){}
1151 v3s16 p = blockpos + v3s16(0,-1,0);
1152 MapBlock *b = getBlockNoCreate(p);
1153 b->updateMesh(daynight_ratio);
1155 catch(InvalidPositionException &e){}
1157 v3s16 p = blockpos + v3s16(0,0,-1);
1158 MapBlock *b = getBlockNoCreate(p);
1159 b->updateMesh(daynight_ratio);
1161 catch(InvalidPositionException &e){}
1164 v3s16 p = blockpos + v3s16(1,0,0);
1165 MapBlock *b = getBlockNoCreate(p);
1166 b->updateMesh(daynight_ratio);
1168 catch(InvalidPositionException &e){}
1170 v3s16 p = blockpos + v3s16(0,1,0);
1171 MapBlock *b = getBlockNoCreate(p);
1172 b->updateMesh(daynight_ratio);
1174 catch(InvalidPositionException &e){}
1176 v3s16 p = blockpos + v3s16(0,0,1);
1177 MapBlock *b = getBlockNoCreate(p);
1178 b->updateMesh(daynight_ratio);
1180 catch(InvalidPositionException &e){}*/
1185 bool Map::dayNightDiffed(v3s16 blockpos)
1188 v3s16 p = blockpos + v3s16(0,0,0);
1189 MapBlock *b = getBlockNoCreate(p);
1190 if(b->dayNightDiffed())
1193 catch(InvalidPositionException &e){}
1196 v3s16 p = blockpos + v3s16(-1,0,0);
1197 MapBlock *b = getBlockNoCreate(p);
1198 if(b->dayNightDiffed())
1201 catch(InvalidPositionException &e){}
1203 v3s16 p = blockpos + v3s16(0,-1,0);
1204 MapBlock *b = getBlockNoCreate(p);
1205 if(b->dayNightDiffed())
1208 catch(InvalidPositionException &e){}
1210 v3s16 p = blockpos + v3s16(0,0,-1);
1211 MapBlock *b = getBlockNoCreate(p);
1212 if(b->dayNightDiffed())
1215 catch(InvalidPositionException &e){}
1218 v3s16 p = blockpos + v3s16(1,0,0);
1219 MapBlock *b = getBlockNoCreate(p);
1220 if(b->dayNightDiffed())
1223 catch(InvalidPositionException &e){}
1225 v3s16 p = blockpos + v3s16(0,1,0);
1226 MapBlock *b = getBlockNoCreate(p);
1227 if(b->dayNightDiffed())
1230 catch(InvalidPositionException &e){}
1232 v3s16 p = blockpos + v3s16(0,0,1);
1233 MapBlock *b = getBlockNoCreate(p);
1234 if(b->dayNightDiffed())
1237 catch(InvalidPositionException &e){}
1243 Updates usage timers
1245 void Map::timerUpdate(float dtime)
1247 JMutexAutoLock lock(m_sector_mutex);
1249 core::map<v2s16, MapSector*>::Iterator si;
1251 si = m_sectors.getIterator();
1252 for(; si.atEnd() == false; si++)
1254 MapSector *sector = si.getNode()->getValue();
1255 sector->usage_timer += dtime;
1259 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1262 Wait for caches to be removed before continuing.
1264 This disables the existence of caches while locked
1266 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1268 core::list<v2s16>::Iterator j;
1269 for(j=list.begin(); j!=list.end(); j++)
1271 MapSector *sector = m_sectors[*j];
1274 sector->deleteBlocks();
1279 If sector is in sector cache, remove it from there
1281 if(m_sector_cache == sector)
1283 m_sector_cache = NULL;
1286 Remove from map and delete
1288 m_sectors.remove(*j);
1294 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1295 core::list<v3s16> *deleted_blocks)
1297 JMutexAutoLock lock(m_sector_mutex);
1299 core::list<v2s16> sector_deletion_queue;
1300 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1301 for(; i.atEnd() == false; i++)
1303 MapSector *sector = i.getNode()->getValue();
1305 Delete sector from memory if it hasn't been used in a long time
1307 if(sector->usage_timer > timeout)
1309 sector_deletion_queue.push_back(i.getNode()->getKey());
1311 if(deleted_blocks != NULL)
1313 // Collect positions of blocks of sector
1314 MapSector *sector = i.getNode()->getValue();
1315 core::list<MapBlock*> blocks;
1316 sector->getBlocks(blocks);
1317 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1318 i != blocks.end(); i++)
1320 deleted_blocks->push_back((*i)->getPos());
1325 deleteSectors(sector_deletion_queue, only_blocks);
1326 return sector_deletion_queue.getSize();
1329 void Map::PrintInfo(std::ostream &out)
1338 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1342 m_savedir = savedir;
1343 m_map_saving_enabled = false;
1347 // If directory exists, check contents and load if possible
1348 if(fs::PathExists(m_savedir))
1350 // If directory is empty, it is safe to save into it.
1351 if(fs::GetDirListing(m_savedir).size() == 0)
1353 dstream<<DTIME<<"Server: Empty save directory is valid."
1355 m_map_saving_enabled = true;
1359 // Load master heightmap
1360 loadMasterHeightmap();
1362 // Load sector (0,0) and throw and exception on fail
1363 if(loadSectorFull(v2s16(0,0)) == false)
1364 throw LoadError("Failed to load sector (0,0)");
1366 dstream<<DTIME<<"Server: Successfully loaded master "
1367 "heightmap and sector (0,0) from "<<savedir<<
1368 ", assuming valid save directory."
1371 m_map_saving_enabled = true;
1372 // Map loaded, not creating new one
1376 // If directory doesn't exist, it is safe to save to it
1378 m_map_saving_enabled = true;
1381 catch(std::exception &e)
1383 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1384 <<", exception: "<<e.what()<<std::endl;
1385 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1386 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1389 dstream<<DTIME<<"Initializing new map."<<std::endl;
1391 // Create master heightmap
1392 ValueGenerator *maxgen =
1393 ValueGenerator::deSerialize(hmp.randmax);
1394 ValueGenerator *factorgen =
1395 ValueGenerator::deSerialize(hmp.randfactor);
1396 ValueGenerator *basegen =
1397 ValueGenerator::deSerialize(hmp.base);
1398 m_heightmap = new UnlimitedHeightmap
1399 (hmp.blocksize, maxgen, factorgen, basegen);
1401 // Set map parameters
1404 // Create zero sector
1405 emergeSector(v2s16(0,0));
1407 // Initially write whole map
1411 ServerMap::~ServerMap()
1415 if(m_map_saving_enabled)
1418 // Save only changed parts
1420 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1424 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1427 catch(std::exception &e)
1429 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1430 <<", exception: "<<e.what()<<std::endl;
1433 if(m_heightmap != NULL)
1437 MapSector * ServerMap::emergeSector(v2s16 p2d)
1439 DSTACK("%s: p2d=(%d,%d)",
1442 // Check that it doesn't exist already
1444 return getSectorNoGenerate(p2d);
1446 catch(InvalidPositionException &e)
1451 Try to load the sector from disk.
1453 if(loadSectorFull(p2d) == true)
1455 return getSectorNoGenerate(p2d);
1459 If there is no master heightmap, throw.
1461 if(m_heightmap == NULL)
1463 throw InvalidPositionException("emergeSector(): no heightmap");
1467 Do not generate over-limit
1469 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1470 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1471 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1472 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1473 throw InvalidPositionException("emergeSector(): pos. over limit");
1476 Generate sector and heightmaps
1479 // Number of heightmaps in sector in each direction
1480 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1482 // Heightmap side width
1483 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1485 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1487 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1488 " heightmaps and objects"<<std::endl;*/
1490 // Loop through sub-heightmaps
1491 for(s16 y=0; y<hm_split; y++)
1492 for(s16 x=0; x<hm_split; x++)
1494 v2s16 p_in_sector = v2s16(x,y);
1495 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1497 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1498 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1499 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1500 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1503 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1504 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1507 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1509 sector->setHeightmap(p_in_sector, hm);
1511 //TODO: Make these values configurable
1512 //hm->generateContinued(0.0, 0.0, corners);
1513 hm->generateContinued(0.5, 0.2, corners);
1514 //hm->generateContinued(1.0, 0.2, corners);
1515 //hm->generateContinued(2.0, 0.2, corners);
1525 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1526 sector->setObjects(objects);
1528 v2s16 mhm_p = p2d * hm_split;
1530 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1531 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1532 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1533 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1536 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1537 float avgslope = 0.0;
1538 avgslope += fabs(avgheight - corners[0]);
1539 avgslope += fabs(avgheight - corners[1]);
1540 avgslope += fabs(avgheight - corners[2]);
1541 avgslope += fabs(avgheight - corners[3]);
1543 avgslope /= MAP_BLOCKSIZE;
1544 //dstream<<"avgslope="<<avgslope<<std::endl;
1546 float pitness = 0.0;
1548 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1551 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1554 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1557 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1561 pitness /= MAP_BLOCKSIZE;
1562 //dstream<<"pitness="<<pitness<<std::endl;
1565 Plant some trees if there is not much slope
1568 // Avgslope is the derivative of a hill
1569 float t = avgslope * avgslope;
1570 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1573 tree_max = a / (t/0.03);
1576 u32 count = (rand()%(tree_max+1));
1577 //u32 count = tree_max;
1578 for(u32 i=0; i<count; i++)
1580 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1581 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1582 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1585 objects->insert(v3s16(x, y, z),
1586 SECTOR_OBJECT_TREE_1);
1590 Plant some bushes if sector is pit-like
1593 // Pitness usually goes at around -0.5...0.5
1595 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1597 bush_max = (pitness*a*4);
1600 u32 count = (rand()%(bush_max+1));
1601 for(u32 i=0; i<count; i++)
1603 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1604 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1605 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1608 objects->insert(v3s16(x, y, z),
1609 SECTOR_OBJECT_BUSH_1);
1613 Add ravine (randomly)
1615 if(m_params.ravines_amount != 0)
1617 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1620 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1621 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1624 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1625 objects->insert(v3s16(x, y, z),
1626 SECTOR_OBJECT_RAVINE);
1633 JMutexAutoLock lock(m_sector_mutex);
1634 m_sectors.insert(p2d, sector);
1639 MapBlock * ServerMap::emergeBlock(
1641 bool only_from_disk,
1642 core::map<v3s16, MapBlock*> &changed_blocks,
1643 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1646 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1648 p.X, p.Y, p.Z, only_from_disk);
1650 /*dstream<<"ServerMap::emergeBlock(): "
1651 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1652 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1653 v2s16 p2d(p.X, p.Z);
1656 This will create or load a sector if not found in memory.
1657 If block exists on disk, it will be loaded.
1659 NOTE: On old save formats, this will be slow, as it generates
1660 lighting on blocks for them.
1662 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1663 assert(sector->getId() == MAPSECTOR_SERVER);
1665 // Try to get a block from the sector
1666 MapBlock *block = NULL;
1667 bool not_on_disk = false;
1669 block = sector->getBlockNoCreate(block_y);
1670 if(block->isDummy() == true)
1675 catch(InvalidPositionException &e)
1681 If block was not found on disk and not going to generate a
1682 new one, make sure there is a dummy block in place.
1684 if(not_on_disk && only_from_disk)
1688 // Create dummy block
1689 block = new MapBlock(this, p, true);
1691 // Add block to sector
1692 sector->insertBlock(block);
1698 //dstream<<"Not found on disk, generating."<<std::endl;
1699 //TimeTaker("emergeBlock()", g_irrlicht);
1702 Do not generate over-limit
1704 if(blockpos_over_limit(p))
1705 throw InvalidPositionException("emergeBlock(): pos. over limit");
1710 Go on generating the block.
1712 TODO: If a dungeon gets generated so that it's side gets
1713 revealed to the outside air, the lighting should be
1718 If block doesn't exist, create one.
1719 If it exists, it is a dummy. In that case unDummify() it.
1723 block = sector->createBlankBlockNoInsert(block_y);
1727 // Remove the block so that nobody can get a half-generated one.
1728 sector->removeBlock(block);
1729 // Allocate the block to be a proper one.
1735 Initialize dungeon making by creating a random table
1737 const s32 ued_max = 5;
1738 const s32 ued_min = 3;
1739 bool underground_emptiness[ued_max*ued_max*ued_max];
1740 s32 ued = (rand()%(ued_max-ued_min+1))+1;
1741 //s32 ued = ued_max;
1742 for(s32 i=0; i<ued*ued*ued; i++)
1744 underground_emptiness[i] = ((rand() % 5) == 0);
1748 This is a messy hack to sort the emptiness a bit
1750 // Iterator through a few times
1751 for(s32 j=0; j<2; j++)
1752 for(s32 y0=0; y0<ued; y0++)
1753 for(s32 z0=0; z0<ued; z0++)
1754 for(s32 x0=0; x0<ued; x0++)
1757 bool &e0 = underground_emptiness[
1758 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1759 +ued*(y0*ued/MAP_BLOCKSIZE)
1760 +(x0*ued/MAP_BLOCKSIZE)];
1763 v3s16(0,0,1), // back
1764 v3s16(1,0,0), // right
1765 v3s16(0,0,-1), // front
1766 v3s16(-1,0,0), // left
1767 /*v3s16(0,1,0), // top
1768 v3s16(0,-1,0), // bottom*/
1771 for(s32 i=0; i<4; i++)
1773 v3s16 p1 = p0 + dirs[i];
1774 if(isInArea(p1, ued) == false)
1776 bool &e1 = underground_emptiness[
1777 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1778 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1779 +(p1.X*ued/MAP_BLOCKSIZE)];
1784 v3s16(0,1,0), // top
1785 v3s16(0,-1,0), // bottom
1786 /*v3s16(0,0,1), // back
1787 v3s16(1,0,0), // right
1788 v3s16(0,0,-1), // front
1789 v3s16(-1,0,0), // left*/
1791 for(s32 i=0; i<2; i++)
1793 v3s16 p2 = p1 + dirs[i];
1796 if(isInArea(p2, ued) == false)
1798 bool &e2 = underground_emptiness[
1799 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1800 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1801 +(p2.X*ued/MAP_BLOCKSIZE)];
1817 Create dungeon making table
1819 const s32 ued = MAP_BLOCKSIZE;
1820 bool underground_emptiness[ued*ued*ued];
1821 for(s32 i=0; i<ued*ued*ued; i++)
1823 underground_emptiness[i] = 0;
1825 // Generate dungeons
1828 Initialize orp and ors. Try to find if some neighboring
1829 MapBlock has a tunnel ended in its side
1833 (float)(rand()%ued)+0.5,
1834 (float)(rand()%ued)+0.5,
1835 (float)(rand()%ued)+0.5
1842 for(s16 y=0; y<ued; y++)
1843 for(s16 x=0; x<ued; x++)
1845 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1846 if(getNode(ap).d == CONTENT_AIR)
1848 orp = v3f(x+1,y+1,0);
1849 goto continue_generating;
1853 catch(InvalidPositionException &e){}
1859 for(s16 y=0; y<ued; y++)
1860 for(s16 x=0; x<ued; x++)
1862 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1863 if(getNode(ap).d == CONTENT_AIR)
1865 orp = v3f(x+1,y+1,ued-1);
1866 goto continue_generating;
1870 catch(InvalidPositionException &e){}
1876 for(s16 y=0; y<ued; y++)
1877 for(s16 z=0; z<ued; z++)
1879 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1880 if(getNode(ap).d == CONTENT_AIR)
1882 orp = v3f(0,y+1,z+1);
1883 goto continue_generating;
1887 catch(InvalidPositionException &e){}
1893 for(s16 y=0; y<ued; y++)
1894 for(s16 z=0; z<ued; z++)
1896 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1897 if(getNode(ap).d == CONTENT_AIR)
1899 orp = v3f(ued-1,y+1,z+1);
1900 goto continue_generating;
1904 catch(InvalidPositionException &e){}
1906 continue_generating:
1909 Generate some tunnel starting from orp and ors
1911 for(u16 i=0; i<3; i++)
1914 (float)(rand()%ued)+0.5,
1915 (float)(rand()%ued)+0.5,
1916 (float)(rand()%ued)+0.5
1920 s16 rs = (rand()%(max_d-min_d+1))+min_d;
1924 for(float f=0; f<1.0; f+=0.04)
1926 v3f fp = orp + vec * f;
1927 v3s16 cp(fp.X, fp.Y, fp.Z);
1929 s16 d1 = d0 + rs - 1;
1930 for(s16 z0=d0; z0<=d1; z0++)
1932 s16 si = rs - abs(z0);
1933 for(s16 x0=-si; x0<=si-1; x0++)
1935 s16 si2 = rs - abs(x0);
1936 for(s16 y0=-si2+1; y0<=si2-1; y0++)
1942 if(isInArea(p, ued) == false)
1944 underground_emptiness[ued*ued*z + ued*y + x] = 1;
1954 // This is the basic material of what the visible flat ground
1956 u8 material = CONTENT_GRASS;
1958 u8 water_material = CONTENT_WATER;
1959 if(g_settings.getBool("endless_water"))
1960 water_material = CONTENT_OCEAN;
1962 s32 lowest_ground_y = 32767;
1963 s32 highest_ground_y = -32768;
1966 //sector->printHeightmaps();
1968 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1969 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1971 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1973 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1974 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1975 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1977 dstream<<"WARNING: Surface height not found in sector "
1978 "for block that is being emerged"<<std::endl;
1982 s16 surface_y = surface_y_f;
1983 //avg_ground_y += surface_y;
1984 if(surface_y < lowest_ground_y)
1985 lowest_ground_y = surface_y;
1986 if(surface_y > highest_ground_y)
1987 highest_ground_y = surface_y;
1989 s32 surface_depth = 0;
1991 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1993 //float min_slope = 0.45;
1994 //float max_slope = 0.85;
1995 float min_slope = 0.60;
1996 float max_slope = 1.20;
1997 float min_slope_depth = 5.0;
1998 float max_slope_depth = 0;
1999 if(slope < min_slope)
2000 surface_depth = min_slope_depth;
2001 else if(slope > max_slope)
2002 surface_depth = max_slope_depth;
2004 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2006 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2008 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2013 NOTE: If there are some man-made structures above the
2014 newly created block, they won't be taken into account.
2016 if(real_y > surface_y)
2017 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2023 if(real_y <= surface_y - surface_depth)
2026 if(underground_emptiness[
2027 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2028 +ued*(y0*ued/MAP_BLOCKSIZE)
2029 +(x0*ued/MAP_BLOCKSIZE)])
2035 n.d = CONTENT_STONE;
2038 // If node is at or under heightmap y
2039 else if(real_y <= surface_y)
2041 // If under water level, it's mud
2042 if(real_y < WATER_LEVEL)
2044 // Only the topmost node is grass
2045 else if(real_y <= surface_y - 1)
2047 // Else it's the main material
2051 // If node is over heightmap y
2053 // If under water level, it's water
2054 if(real_y < WATER_LEVEL)
2056 n.d = water_material;
2057 n.setLight(LIGHTBANK_DAY,
2058 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2064 block->setNode(v3s16(x0,y0,z0), n);
2069 Calculate is_underground
2071 // Probably underground if the highest part of block is under lowest
2073 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
2074 block->setIsUnderground(is_underground);
2077 Force lighting update if some part of block is underground
2078 This is needed because of caves.
2081 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
2082 if(some_part_underground)
2083 //if(is_underground)
2085 lighting_invalidated_blocks[block->getPos()] = block;
2092 if(some_part_underground)
2094 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2099 for(s16 i=0; i<underground_level*1; i++)
2104 (rand()%(MAP_BLOCKSIZE-2))+1,
2105 (rand()%(MAP_BLOCKSIZE-2))+1,
2106 (rand()%(MAP_BLOCKSIZE-2))+1
2112 //if(is_ground_content(block->getNode(cp).d))
2113 if(block->getNode(cp).d == CONTENT_STONE)
2115 block->setNode(cp, n);
2117 for(u16 i=0; i<26; i++)
2119 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2120 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2122 block->setNode(cp+g_26dirs[i], n);
2130 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2131 u16 coal_rareness = 60 / coal_amount;
2132 if(coal_rareness == 0)
2134 if(rand()%coal_rareness == 0)
2136 u16 a = rand() % 16;
2137 u16 amount = coal_amount * a*a*a / 1000;
2138 for(s16 i=0; i<amount; i++)
2141 (rand()%(MAP_BLOCKSIZE-2))+1,
2142 (rand()%(MAP_BLOCKSIZE-2))+1,
2143 (rand()%(MAP_BLOCKSIZE-2))+1
2147 n.d = CONTENT_COALSTONE;
2149 //dstream<<"Adding coalstone"<<std::endl;
2151 //if(is_ground_content(block->getNode(cp).d))
2152 if(block->getNode(cp).d == CONTENT_STONE)
2154 block->setNode(cp, n);
2156 for(u16 i=0; i<26; i++)
2158 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2159 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2161 block->setNode(cp+g_26dirs[i], n);
2168 Create a few rats in empty blocks underground
2172 //for(u16 i=0; i<2; i++)
2175 (rand()%(MAP_BLOCKSIZE-2))+1,
2176 (rand()%(MAP_BLOCKSIZE-2))+1,
2177 (rand()%(MAP_BLOCKSIZE-2))+1
2180 // Check that the place is empty
2181 //if(!is_ground_content(block->getNode(cp).d))
2184 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2185 block->addObject(obj);
2191 Add block to sector.
2193 sector->insertBlock(block);
2199 // An y-wise container of changed blocks
2200 core::map<s16, MapBlock*> changed_blocks_sector;
2203 Check if any sector's objects can be placed now.
2206 core::map<v3s16, u8> *objects = sector->getObjects();
2207 core::list<v3s16> objects_to_remove;
2208 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2209 i.atEnd() == false; i++)
2211 v3s16 p = i.getNode()->getKey();
2213 u8 d = i.getNode()->getValue();
2215 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2220 if(d == SECTOR_OBJECT_TEST)
2222 if(sector->isValidArea(p + v3s16(0,0,0),
2223 p + v3s16(0,0,0), &changed_blocks_sector))
2226 n.d = CONTENT_TORCH;
2227 sector->setNode(p, n);
2228 objects_to_remove.push_back(p);
2231 else if(d == SECTOR_OBJECT_TREE_1)
2233 v3s16 p_min = p + v3s16(-1,0,-1);
2234 v3s16 p_max = p + v3s16(1,4,1);
2235 if(sector->isValidArea(p_min, p_max,
2236 &changed_blocks_sector))
2240 sector->setNode(p+v3s16(0,0,0), n);
2241 sector->setNode(p+v3s16(0,1,0), n);
2242 sector->setNode(p+v3s16(0,2,0), n);
2243 sector->setNode(p+v3s16(0,3,0), n);
2245 n.d = CONTENT_LEAVES;
2247 sector->setNode(p+v3s16(0,4,0), n);
2249 sector->setNode(p+v3s16(-1,4,0), n);
2250 sector->setNode(p+v3s16(1,4,0), n);
2251 sector->setNode(p+v3s16(0,4,-1), n);
2252 sector->setNode(p+v3s16(0,4,1), n);
2253 sector->setNode(p+v3s16(1,4,1), n);
2254 sector->setNode(p+v3s16(-1,4,1), n);
2255 sector->setNode(p+v3s16(-1,4,-1), n);
2256 sector->setNode(p+v3s16(1,4,-1), n);
2258 sector->setNode(p+v3s16(-1,3,0), n);
2259 sector->setNode(p+v3s16(1,3,0), n);
2260 sector->setNode(p+v3s16(0,3,-1), n);
2261 sector->setNode(p+v3s16(0,3,1), n);
2262 sector->setNode(p+v3s16(1,3,1), n);
2263 sector->setNode(p+v3s16(-1,3,1), n);
2264 sector->setNode(p+v3s16(-1,3,-1), n);
2265 sector->setNode(p+v3s16(1,3,-1), n);
2267 objects_to_remove.push_back(p);
2269 // Lighting has to be recalculated for this one.
2270 sector->getBlocksInArea(p_min, p_max,
2271 lighting_invalidated_blocks);
2274 else if(d == SECTOR_OBJECT_BUSH_1)
2276 if(sector->isValidArea(p + v3s16(0,0,0),
2277 p + v3s16(0,0,0), &changed_blocks_sector))
2280 n.d = CONTENT_LEAVES;
2281 sector->setNode(p+v3s16(0,0,0), n);
2283 objects_to_remove.push_back(p);
2286 else if(d == SECTOR_OBJECT_RAVINE)
2289 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2290 v3s16 p_max = p + v3s16(6,6,6);
2291 if(sector->isValidArea(p_min, p_max,
2292 &changed_blocks_sector))
2295 n.d = CONTENT_STONE;
2298 s16 depth = maxdepth + (rand()%10);
2300 s16 minz = -6 - (-2);
2302 for(s16 x=-6; x<=6; x++)
2304 z += -1 + (rand()%3);
2309 for(s16 y=depth+(rand()%2); y<=6; y++)
2311 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2314 v3s16 p2 = p + v3s16(x,y,z-2);
2315 if(is_ground_content(sector->getNode(p2).d)
2316 && !is_mineral(sector->getNode(p2).d))
2317 sector->setNode(p2, n);
2320 v3s16 p2 = p + v3s16(x,y,z-1);
2321 if(is_ground_content(sector->getNode(p2).d)
2322 && !is_mineral(sector->getNode(p2).d))
2323 sector->setNode(p2, n2);
2326 v3s16 p2 = p + v3s16(x,y,z+0);
2327 if(is_ground_content(sector->getNode(p2).d)
2328 && !is_mineral(sector->getNode(p2).d))
2329 sector->setNode(p2, n2);
2332 v3s16 p2 = p + v3s16(x,y,z+1);
2333 if(is_ground_content(sector->getNode(p2).d)
2334 && !is_mineral(sector->getNode(p2).d))
2335 sector->setNode(p2, n);
2338 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2339 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2343 objects_to_remove.push_back(p);
2345 // Lighting has to be recalculated for this one.
2346 sector->getBlocksInArea(p_min, p_max,
2347 lighting_invalidated_blocks);
2352 dstream<<"ServerMap::emergeBlock(): "
2353 "Invalid heightmap object"
2358 catch(InvalidPositionException &e)
2360 dstream<<"WARNING: "<<__FUNCTION_NAME
2361 <<": while inserting object "<<(int)d
2362 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2363 <<" InvalidPositionException.what()="
2364 <<e.what()<<std::endl;
2365 // This is not too fatal and seems to happen sometimes.
2370 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2371 i != objects_to_remove.end(); i++)
2373 objects->remove(*i);
2376 for(core::map<s16, MapBlock*>::Iterator
2377 i = changed_blocks_sector.getIterator();
2378 i.atEnd() == false; i++)
2380 MapBlock *block = i.getNode()->getValue();
2382 changed_blocks.insert(block->getPos(), block);
2388 void ServerMap::createDir(std::string path)
2390 if(fs::CreateDir(path) == false)
2392 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2393 <<"\""<<path<<"\""<<std::endl;
2394 throw BaseException("ServerMap failed to create directory");
2398 std::string ServerMap::getSectorSubDir(v2s16 pos)
2401 snprintf(cc, 9, "%.4x%.4x",
2402 (unsigned int)pos.X&0xffff,
2403 (unsigned int)pos.Y&0xffff);
2405 return std::string(cc);
2408 std::string ServerMap::getSectorDir(v2s16 pos)
2410 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2413 v2s16 ServerMap::getSectorPos(std::string dirname)
2415 if(dirname.size() != 8)
2416 throw InvalidFilenameException("Invalid sector directory name");
2418 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2420 throw InvalidFilenameException("Invalid sector directory name");
2421 v2s16 pos((s16)x, (s16)y);
2425 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2427 v2s16 p2d = getSectorPos(sectordir);
2429 if(blockfile.size() != 4){
2430 throw InvalidFilenameException("Invalid block filename");
2433 int r = sscanf(blockfile.c_str(), "%4x", &y);
2435 throw InvalidFilenameException("Invalid block filename");
2436 return v3s16(p2d.X, y, p2d.Y);
2440 #define ENABLE_SECTOR_SAVING 1
2441 #define ENABLE_SECTOR_LOADING 1
2442 #define ENABLE_BLOCK_SAVING 1
2443 #define ENABLE_BLOCK_LOADING 1
2445 void ServerMap::save(bool only_changed)
2447 DSTACK(__FUNCTION_NAME);
2448 if(m_map_saving_enabled == false)
2450 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2454 if(only_changed == false)
2455 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2458 saveMasterHeightmap();
2460 u32 sector_meta_count = 0;
2461 u32 block_count = 0;
2464 JMutexAutoLock lock(m_sector_mutex);
2466 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2467 for(; i.atEnd() == false; i++)
2469 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2470 assert(sector->getId() == MAPSECTOR_SERVER);
2472 if(ENABLE_SECTOR_SAVING)
2474 if(sector->differs_from_disk || only_changed == false)
2476 saveSectorMeta(sector);
2477 sector_meta_count++;
2480 if(ENABLE_BLOCK_SAVING)
2482 core::list<MapBlock*> blocks;
2483 sector->getBlocks(blocks);
2484 core::list<MapBlock*>::Iterator j;
2485 for(j=blocks.begin(); j!=blocks.end(); j++)
2487 MapBlock *block = *j;
2488 if(block->getChangedFlag() || only_changed == false)
2500 Only print if something happened or saved whole map
2502 if(only_changed == false || sector_meta_count != 0
2503 || block_count != 0)
2505 dstream<<DTIME<<"ServerMap: Written: "
2506 <<sector_meta_count<<" sector metadata files, "
2507 <<block_count<<" block files"
2512 void ServerMap::loadAll()
2514 DSTACK(__FUNCTION_NAME);
2515 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2517 loadMasterHeightmap();
2519 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2521 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2523 JMutexAutoLock lock(m_sector_mutex);
2526 s32 printed_counter = -100000;
2527 s32 count = list.size();
2529 std::vector<fs::DirListNode>::iterator i;
2530 for(i=list.begin(); i!=list.end(); i++)
2532 if(counter > printed_counter + 10)
2534 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2535 printed_counter = counter;
2539 MapSector *sector = NULL;
2541 // We want directories
2545 sector = loadSectorMeta(i->name);
2547 catch(InvalidFilenameException &e)
2549 // This catches unknown crap in directory
2552 if(ENABLE_BLOCK_LOADING)
2554 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2555 (m_savedir+"/sectors/"+i->name);
2556 std::vector<fs::DirListNode>::iterator i2;
2557 for(i2=list2.begin(); i2!=list2.end(); i2++)
2563 loadBlock(i->name, i2->name, sector);
2565 catch(InvalidFilenameException &e)
2567 // This catches unknown crap in directory
2572 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2575 void ServerMap::saveMasterHeightmap()
2577 DSTACK(__FUNCTION_NAME);
2578 createDir(m_savedir);
2580 std::string fullpath = m_savedir + "/master_heightmap";
2581 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2582 if(o.good() == false)
2583 throw FileNotGoodException("Cannot open master heightmap");
2585 // Format used for writing
2586 u8 version = SER_FMT_VER_HIGHEST;
2589 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2591 [0] u8 serialization version
2592 [1] X master heightmap
2594 u32 fullsize = 1 + hmdata.getSize();
2595 SharedBuffer<u8> data(fullsize);
2598 memcpy(&data[1], *hmdata, hmdata.getSize());
2600 o.write((const char*)*data, fullsize);
2603 m_heightmap->serialize(o, version);
2606 void ServerMap::loadMasterHeightmap()
2608 DSTACK(__FUNCTION_NAME);
2609 std::string fullpath = m_savedir + "/master_heightmap";
2610 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2611 if(is.good() == false)
2612 throw FileNotGoodException("Cannot open master heightmap");
2614 if(m_heightmap != NULL)
2617 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2620 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2622 DSTACK(__FUNCTION_NAME);
2623 // Format used for writing
2624 u8 version = SER_FMT_VER_HIGHEST;
2626 v2s16 pos = sector->getPos();
2627 createDir(m_savedir);
2628 createDir(m_savedir+"/sectors");
2629 std::string dir = getSectorDir(pos);
2632 std::string fullpath = dir + "/heightmap";
2633 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2634 if(o.good() == false)
2635 throw FileNotGoodException("Cannot open master heightmap");
2637 sector->serialize(o, version);
2639 sector->differs_from_disk = false;
2642 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2644 DSTACK(__FUNCTION_NAME);
2646 v2s16 p2d = getSectorPos(dirname);
2647 std::string dir = m_savedir + "/sectors/" + dirname;
2649 std::string fullpath = dir + "/heightmap";
2650 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2651 if(is.good() == false)
2652 throw FileNotGoodException("Cannot open sector heightmap");
2654 ServerMapSector *sector = ServerMapSector::deSerialize
2655 (is, this, p2d, &m_hwrapper, m_sectors);
2657 sector->differs_from_disk = false;
2662 bool ServerMap::loadSectorFull(v2s16 p2d)
2664 DSTACK(__FUNCTION_NAME);
2665 std::string sectorsubdir = getSectorSubDir(p2d);
2667 MapSector *sector = NULL;
2669 JMutexAutoLock lock(m_sector_mutex);
2672 sector = loadSectorMeta(sectorsubdir);
2674 catch(InvalidFilenameException &e)
2678 catch(FileNotGoodException &e)
2682 catch(std::exception &e)
2687 if(ENABLE_BLOCK_LOADING)
2689 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2690 (m_savedir+"/sectors/"+sectorsubdir);
2691 std::vector<fs::DirListNode>::iterator i2;
2692 for(i2=list2.begin(); i2!=list2.end(); i2++)
2698 loadBlock(sectorsubdir, i2->name, sector);
2700 catch(InvalidFilenameException &e)
2702 // This catches unknown crap in directory
2710 bool ServerMap::deFlushSector(v2s16 p2d)
2712 DSTACK(__FUNCTION_NAME);
2713 // See if it already exists in memory
2715 MapSector *sector = getSectorNoGenerate(p2d);
2718 catch(InvalidPositionException &e)
2721 Try to load the sector from disk.
2723 if(loadSectorFull(p2d) == true)
2732 void ServerMap::saveBlock(MapBlock *block)
2734 DSTACK(__FUNCTION_NAME);
2736 Dummy blocks are not written
2738 if(block->isDummy())
2740 /*v3s16 p = block->getPos();
2741 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2742 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2746 // Format used for writing
2747 u8 version = SER_FMT_VER_HIGHEST;
2749 v3s16 p3d = block->getPos();
2750 v2s16 p2d(p3d.X, p3d.Z);
2751 createDir(m_savedir);
2752 createDir(m_savedir+"/sectors");
2753 std::string dir = getSectorDir(p2d);
2756 // Block file is map/sectors/xxxxxxxx/xxxx
2758 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2759 std::string fullpath = dir + "/" + cc;
2760 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2761 if(o.good() == false)
2762 throw FileNotGoodException("Cannot open block data");
2765 [0] u8 serialization version
2768 o.write((char*)&version, 1);
2770 block->serialize(o, version);
2773 Versions up from 9 have block objects.
2777 block->serializeObjects(o, version);
2780 // We just wrote it to the disk
2781 block->resetChangedFlag();
2784 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2786 DSTACK(__FUNCTION_NAME);
2790 // Block file is map/sectors/xxxxxxxx/xxxx
2791 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2792 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2793 if(is.good() == false)
2794 throw FileNotGoodException("Cannot open block file");
2796 v3s16 p3d = getBlockPos(sectordir, blockfile);
2797 v2s16 p2d(p3d.X, p3d.Z);
2799 assert(sector->getPos() == p2d);
2801 u8 version = SER_FMT_VER_INVALID;
2802 is.read((char*)&version, 1);
2804 /*u32 block_size = MapBlock::serializedLength(version);
2805 SharedBuffer<u8> data(block_size);
2806 is.read((char*)*data, block_size);*/
2808 // This will always return a sector because we're the server
2809 //MapSector *sector = emergeSector(p2d);
2811 MapBlock *block = NULL;
2812 bool created_new = false;
2814 block = sector->getBlockNoCreate(p3d.Y);
2816 catch(InvalidPositionException &e)
2818 block = sector->createBlankBlockNoInsert(p3d.Y);
2822 // deserialize block data
2823 block->deSerialize(is, version);
2826 Versions up from 9 have block objects.
2830 block->updateObjects(is, version, NULL, 0);
2834 sector->insertBlock(block);
2837 Convert old formats to new and save
2840 // Save old format blocks in new format
2841 if(version < SER_FMT_VER_HIGHEST)
2846 // We just loaded it from the disk, so it's up-to-date.
2847 block->resetChangedFlag();
2850 catch(SerializationError &e)
2852 dstream<<"WARNING: Invalid block data on disk "
2853 "(SerializationError). Ignoring."
2858 // Gets from master heightmap
2859 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2861 assert(m_heightmap != NULL);
2869 corners[0] = m_heightmap->getGroundHeight
2870 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2871 corners[1] = m_heightmap->getGroundHeight
2872 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2873 corners[2] = m_heightmap->getGroundHeight
2874 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2875 corners[3] = m_heightmap->getGroundHeight
2876 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2879 void ServerMap::PrintInfo(std::ostream &out)
2890 ClientMap::ClientMap(
2892 JMutex &range_mutex,
2893 s16 &viewing_range_nodes,
2894 bool &viewing_range_all,
2895 scene::ISceneNode* parent,
2896 scene::ISceneManager* mgr,
2900 scene::ISceneNode(parent, mgr, id),
2903 m_range_mutex(range_mutex),
2904 m_viewing_range_nodes(viewing_range_nodes),
2905 m_viewing_range_all(viewing_range_all)
2909 /*m_box = core::aabbox3d<f32>(0,0,0,
2910 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2911 /*m_box = core::aabbox3d<f32>(0,0,0,
2912 map->getSizeNodes().X * BS,
2913 map->getSizeNodes().Y * BS,
2914 map->getSizeNodes().Z * BS);*/
2915 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2916 BS*1000000,BS*1000000,BS*1000000);
2918 //setPosition(v3f(BS,BS,BS));
2921 ClientMap::~ClientMap()
2923 JMutexAutoLock lock(mesh_mutex);
2932 MapSector * ClientMap::emergeSector(v2s16 p2d)
2934 DSTACK(__FUNCTION_NAME);
2935 // Check that it doesn't exist already
2937 return getSectorNoGenerate(p2d);
2939 catch(InvalidPositionException &e)
2943 // Create a sector with no heightmaps
2944 ClientMapSector *sector = new ClientMapSector(this, p2d);
2947 JMutexAutoLock lock(m_sector_mutex);
2948 m_sectors.insert(p2d, sector);
2954 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2956 DSTACK(__FUNCTION_NAME);
2957 ClientMapSector *sector = NULL;
2959 JMutexAutoLock lock(m_sector_mutex);
2961 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2965 sector = (ClientMapSector*)n->getValue();
2966 assert(sector->getId() == MAPSECTOR_CLIENT);
2970 sector = new ClientMapSector(this, p2d);
2972 JMutexAutoLock lock(m_sector_mutex);
2973 m_sectors.insert(p2d, sector);
2977 sector->deSerialize(is);
2980 void ClientMap::OnRegisterSceneNode()
2984 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2985 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2988 ISceneNode::OnRegisterSceneNode();
2991 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2993 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2994 DSTACK(__FUNCTION_NAME);
2996 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2999 Get time for measuring timeout.
3001 Measuring time is very useful for long delays when the
3002 machine is swapping a lot.
3004 int time1 = time(0);
3006 //s32 daynight_i = m_client->getDayNightIndex();
3007 u32 daynight_ratio = m_client->getDayNightRatio();
3010 Collect all blocks that are in the view range
3012 Should not optimize more here as we want to auto-update
3013 all changed nodes in viewing range at the next step.
3016 s16 viewing_range_nodes;
3017 bool viewing_range_all;
3019 JMutexAutoLock lock(m_range_mutex);
3020 viewing_range_nodes = m_viewing_range_nodes;
3021 viewing_range_all = m_viewing_range_all;
3024 m_camera_mutex.Lock();
3025 v3f camera_position = m_camera_position;
3026 v3f camera_direction = m_camera_direction;
3027 m_camera_mutex.Unlock();
3030 Get all blocks and draw all visible ones
3033 v3s16 cam_pos_nodes(
3034 camera_position.X / BS,
3035 camera_position.Y / BS,
3036 camera_position.Z / BS);
3038 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
3040 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3041 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3043 // Take a fair amount as we will be dropping more out later
3045 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3046 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3047 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3049 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3050 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3051 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3053 u32 vertex_count = 0;
3055 // For limiting number of mesh updates per frame
3056 u32 mesh_update_count = 0;
3058 //NOTE: The sectors map should be locked but we're not doing it
3059 // because it'd cause too much delays
3061 int timecheck_counter = 0;
3063 core::map<v2s16, MapSector*>::Iterator si;
3064 si = m_sectors.getIterator();
3065 for(; si.atEnd() == false; si++)
3068 timecheck_counter++;
3069 if(timecheck_counter > 50)
3071 int time2 = time(0);
3072 if(time2 > time1 + 4)
3074 dstream<<"ClientMap::renderMap(): "
3075 "Rendering takes ages, returning."
3082 MapSector *sector = si.getNode()->getValue();
3083 v2s16 sp = sector->getPos();
3085 if(viewing_range_all == false)
3087 if(sp.X < p_blocks_min.X
3088 || sp.X > p_blocks_max.X
3089 || sp.Y < p_blocks_min.Z
3090 || sp.Y > p_blocks_max.Z)
3094 core::list< MapBlock * > sectorblocks;
3095 sector->getBlocks(sectorblocks);
3101 core::list< MapBlock * >::Iterator i;
3102 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3104 MapBlock *block = *i;
3107 Compare block position to camera position, skip
3108 if not seen on display
3111 v3s16 blockpos_nodes = block->getPosRelative();
3113 // Block center position
3115 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3116 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3117 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3120 // Block position relative to camera
3121 v3f blockpos_relative = blockpos - camera_position;
3123 // Distance in camera direction (+=front, -=back)
3124 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3127 f32 d = blockpos_relative.getLength();
3129 if(viewing_range_all == false)
3131 // If block is far away, don't draw it
3132 if(d > viewing_range_nodes * BS)
3133 // This is nicer when fog is used
3134 //if((dforward+d)/2 > viewing_range_nodes * BS)
3138 // Maximum radius of a block
3139 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3141 // If block is (nearly) touching the camera, don't
3142 // bother validating further (that is, render it anyway)
3143 if(d > block_max_radius * 1.5)
3145 // Cosine of the angle between the camera direction
3146 // and the block direction (camera_direction is an unit vector)
3147 f32 cosangle = dforward / d;
3149 // Compensate for the size of the block
3150 // (as the block has to be shown even if it's a bit off FOV)
3151 // This is an estimate.
3152 cosangle += block_max_radius / dforward;
3154 // If block is not in the field of view, skip it
3155 //if(cosangle < cos(FOV_ANGLE/2))
3156 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3161 Draw the faces of the block
3164 bool mesh_expired = false;
3167 JMutexAutoLock lock(block->mesh_mutex);
3169 mesh_expired = block->getMeshExpired();
3171 // Mesh has not been expired and there is no mesh:
3172 // block has no content
3173 if(block->mesh == NULL && mesh_expired == false)
3177 f32 faraway = BS*50;
3178 //f32 faraway = viewing_range_nodes * BS;
3181 This has to be done with the mesh_mutex unlocked
3183 if(mesh_expired && mesh_update_count < 6
3184 && (d < faraway || mesh_update_count < 3))
3185 //if(mesh_expired && mesh_update_count < 4)
3187 mesh_update_count++;
3189 // Mesh has been expired: generate new mesh
3190 //block->updateMeshes(daynight_i);
3191 block->updateMesh(daynight_ratio);
3193 mesh_expired = false;
3197 Don't draw an expired mesh that is far away
3199 /*if(mesh_expired && d >= faraway)
3202 // Instead, delete it
3203 JMutexAutoLock lock(block->mesh_mutex);
3206 block->mesh->drop();
3209 // And continue to next block
3214 JMutexAutoLock lock(block->mesh_mutex);
3216 scene::SMesh *mesh = block->mesh;
3221 u32 c = mesh->getMeshBufferCount();
3223 for(u32 i=0; i<c; i++)
3225 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3226 const video::SMaterial& material = buf->getMaterial();
3227 video::IMaterialRenderer* rnd =
3228 driver->getMaterialRenderer(material.MaterialType);
3229 bool transparent = (rnd && rnd->isTransparent());
3230 // Render transparent on transparent pass and likewise.
3231 if(transparent == is_transparent_pass)
3233 driver->setMaterial(buf->getMaterial());
3234 driver->drawMeshBuffer(buf);
3235 vertex_count += buf->getVertexCount();
3239 } // foreach sectorblocks
3242 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3243 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3246 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3249 Add it to all blocks touching it
3252 v3s16(0,0,0), // this
3253 v3s16(0,0,1), // back
3254 v3s16(0,1,0), // top
3255 v3s16(1,0,0), // right
3256 v3s16(0,0,-1), // front
3257 v3s16(0,-1,0), // bottom
3258 v3s16(-1,0,0), // left
3260 for(u16 i=0; i<7; i++)
3262 v3s16 p2 = p + dirs[i];
3263 // Block position of neighbor (or requested) node
3264 v3s16 blockpos = getNodeBlockPos(p2);
3265 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3266 if(blockref == NULL)
3268 // Relative position of requested node
3269 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3270 blockref->setTempMod(relpos, mod);
3272 return getNodeBlockPos(p);
3274 v3s16 ClientMap::clearTempMod(v3s16 p)
3277 v3s16(0,0,0), // this
3278 v3s16(0,0,1), // back
3279 v3s16(0,1,0), // top
3280 v3s16(1,0,0), // right
3281 v3s16(0,0,-1), // front
3282 v3s16(0,-1,0), // bottom
3283 v3s16(-1,0,0), // left
3285 for(u16 i=0; i<7; i++)
3287 v3s16 p2 = p + dirs[i];
3288 // Block position of neighbor (or requested) node
3289 v3s16 blockpos = getNodeBlockPos(p2);
3290 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3291 if(blockref == NULL)
3293 // Relative position of requested node
3294 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3295 blockref->clearTempMod(relpos);
3297 return getNodeBlockPos(p);
3300 void ClientMap::PrintInfo(std::ostream &out)
3311 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3316 MapVoxelManipulator::~MapVoxelManipulator()
3318 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3323 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3325 TimeTaker timer1("emerge", &emerge_time);
3327 // Units of these are MapBlocks
3328 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3329 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3331 VoxelArea block_area_nodes
3332 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3334 addArea(block_area_nodes);
3336 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3337 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3338 for(s32 x=p_min.X; x<=p_max.X; x++)
3341 core::map<v3s16, bool>::Node *n;
3342 n = m_loaded_blocks.find(p);
3346 bool block_data_inexistent = false;
3349 TimeTaker timer1("emerge load", &emerge_load_time);
3351 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3352 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3355 dstream<<std::endl;*/
3357 MapBlock *block = m_map->getBlockNoCreate(p);
3358 if(block->isDummy())
3359 block_data_inexistent = true;
3361 block->copyTo(*this);
3363 catch(InvalidPositionException &e)
3365 block_data_inexistent = true;
3368 if(block_data_inexistent)
3370 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3371 // Fill with VOXELFLAG_INEXISTENT
3372 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3373 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3375 s32 i = m_area.index(a.MinEdge.X,y,z);
3376 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3380 m_loaded_blocks.insert(p, true);
3383 //dstream<<"emerge done"<<std::endl;
3388 void MapVoxelManipulator::emerge(VoxelArea a)
3390 TimeTaker timer1("emerge", &emerge_time);
3392 v3s16 size = a.getExtent();
3394 VoxelArea padded = a;
3395 padded.pad(m_area.getExtent() / 4);
3398 for(s16 z=0; z<size.Z; z++)
3399 for(s16 y=0; y<size.Y; y++)
3400 for(s16 x=0; x<size.X; x++)
3403 s32 i = m_area.index(a.MinEdge + p);
3404 // Don't touch nodes that have already been loaded
3405 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3409 TimeTaker timer1("emerge load", &emerge_load_time);
3410 MapNode n = m_map->getNode(a.MinEdge + p);
3414 catch(InvalidPositionException &e)
3416 m_flags[i] = VOXELFLAG_INEXISTENT;
3424 TODO: Add an option to only update eg. water and air nodes.
3425 This will make it interfere less with important stuff if
3428 void MapVoxelManipulator::blitBack
3429 (core::map<v3s16, MapBlock*> & modified_blocks)
3431 if(m_area.getExtent() == v3s16(0,0,0))
3434 //TimeTaker timer1("blitBack");
3437 Initialize block cache
3439 v3s16 blockpos_last;
3440 MapBlock *block = NULL;
3441 bool block_checked_in_modified = false;
3443 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3444 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3445 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3449 u8 f = m_flags[m_area.index(p)];
3450 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3453 MapNode &n = m_data[m_area.index(p)];
3455 v3s16 blockpos = getNodeBlockPos(p);
3460 if(block == NULL || blockpos != blockpos_last){
3461 block = m_map->getBlockNoCreate(blockpos);
3462 blockpos_last = blockpos;
3463 block_checked_in_modified = false;
3466 // Calculate relative position in block
3467 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3469 // Don't continue if nothing has changed here
3470 if(block->getNode(relpos) == n)
3473 //m_map->setNode(m_area.MinEdge + p, n);
3474 block->setNode(relpos, n);
3477 Make sure block is in modified_blocks
3479 if(block_checked_in_modified == false)
3481 modified_blocks[blockpos] = block;
3482 block_checked_in_modified = true;
3485 catch(InvalidPositionException &e)