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
1839 for(s16 y=0; y<ued; y++)
1840 for(s16 x=0; x<ued; x++)
1842 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1843 if(getNode(ap).d == CONTENT_AIR)
1845 orp = v3f(x+1,y+1,0);
1850 catch(InvalidPositionException &e){}
1856 for(s16 y=0; y<ued; y++)
1857 for(s16 x=0; x<ued; x++)
1859 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1860 if(getNode(ap).d == CONTENT_AIR)
1862 orp = v3f(x+1,y+1,ued-1);
1867 catch(InvalidPositionException &e){}
1873 for(s16 y=0; y<ued; y++)
1874 for(s16 z=0; z<ued; z++)
1876 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1877 if(getNode(ap).d == CONTENT_AIR)
1879 orp = v3f(0,y+1,z+1);
1884 catch(InvalidPositionException &e){}
1890 for(s16 y=0; y<ued; y++)
1891 for(s16 z=0; z<ued; z++)
1893 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1894 if(getNode(ap).d == CONTENT_AIR)
1896 orp = v3f(ued-1,y+1,z+1);
1901 catch(InvalidPositionException &e){}
1904 Generate some tunnel starting from orp and ors
1906 for(u16 i=0; i<3; i++)
1909 (float)(rand()%(ued-1))+0.5,
1910 (float)(rand()%(ued-1))+0.5,
1911 (float)(rand()%(ued-1))+0.5
1915 s16 rs = (rand()%(max_d-min_d+1))+min_d;
1919 for(float f=0; f<1.0; f+=0.04)
1921 v3f fp = orp + vec * f;
1922 v3s16 cp(fp.X, fp.Y, fp.Z);
1924 s16 d1 = d0 + rs - 1;
1925 for(s16 z0=d0; z0<=d1; z0++)
1927 s16 si = rs - abs(z0);
1928 for(s16 x0=-si; x0<=si-1; x0++)
1930 s16 si2 = rs - abs(x0);
1931 for(s16 y0=-si2+1; y0<=si2-1; y0++)
1937 if(isInArea(p, ued) == false)
1939 underground_emptiness[ued*ued*z + ued*y + x] = 1;
1950 // This is the basic material of what the visible flat ground
1952 u8 material = CONTENT_GRASS;
1954 u8 water_material = CONTENT_WATER;
1955 if(g_settings.getBool("endless_water"))
1956 water_material = CONTENT_OCEAN;
1958 s32 lowest_ground_y = 32767;
1959 s32 highest_ground_y = -32768;
1962 //sector->printHeightmaps();
1964 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1965 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1967 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1969 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1970 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1971 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1973 dstream<<"WARNING: Surface height not found in sector "
1974 "for block that is being emerged"<<std::endl;
1978 s16 surface_y = surface_y_f;
1979 //avg_ground_y += surface_y;
1980 if(surface_y < lowest_ground_y)
1981 lowest_ground_y = surface_y;
1982 if(surface_y > highest_ground_y)
1983 highest_ground_y = surface_y;
1985 s32 surface_depth = 0;
1987 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1989 //float min_slope = 0.45;
1990 //float max_slope = 0.85;
1991 float min_slope = 0.60;
1992 float max_slope = 1.20;
1993 float min_slope_depth = 5.0;
1994 float max_slope_depth = 0;
1995 if(slope < min_slope)
1996 surface_depth = min_slope_depth;
1997 else if(slope > max_slope)
1998 surface_depth = max_slope_depth;
2000 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2002 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2004 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2009 NOTE: If there are some man-made structures above the
2010 newly created block, they won't be taken into account.
2012 if(real_y > surface_y)
2013 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2019 if(real_y <= surface_y - surface_depth)
2022 if(underground_emptiness[
2023 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2024 +ued*(y0*ued/MAP_BLOCKSIZE)
2025 +(x0*ued/MAP_BLOCKSIZE)])
2031 n.d = CONTENT_STONE;
2034 // If node is at or under heightmap y
2035 else if(real_y <= surface_y)
2037 // If under water level, it's mud
2038 if(real_y < WATER_LEVEL)
2040 // Only the topmost node is grass
2041 else if(real_y <= surface_y - 1)
2043 // Else it's the main material
2047 // If node is over heightmap y
2049 // If under water level, it's water
2050 if(real_y < WATER_LEVEL)
2052 n.d = water_material;
2053 n.setLight(LIGHTBANK_DAY,
2054 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2060 block->setNode(v3s16(x0,y0,z0), n);
2065 Calculate is_underground
2067 // Probably underground if the highest part of block is under lowest
2069 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
2070 block->setIsUnderground(is_underground);
2073 Force lighting update if some part of block is underground
2074 This is needed because of caves.
2077 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
2078 if(some_part_underground)
2079 //if(is_underground)
2081 lighting_invalidated_blocks[block->getPos()] = block;
2088 if(some_part_underground)
2090 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2095 for(s16 i=0; i<underground_level*1; i++)
2100 (rand()%(MAP_BLOCKSIZE-2))+1,
2101 (rand()%(MAP_BLOCKSIZE-2))+1,
2102 (rand()%(MAP_BLOCKSIZE-2))+1
2108 //if(is_ground_content(block->getNode(cp).d))
2109 if(block->getNode(cp).d == CONTENT_STONE)
2111 block->setNode(cp, n);
2113 for(u16 i=0; i<26; i++)
2115 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2116 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2118 block->setNode(cp+g_26dirs[i], n);
2126 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2127 u16 coal_rareness = 60 / coal_amount;
2128 if(coal_rareness == 0)
2130 if(rand()%coal_rareness == 0)
2132 u16 a = rand() % 16;
2133 u16 amount = coal_amount * a*a*a / 1000;
2134 for(s16 i=0; i<amount; i++)
2137 (rand()%(MAP_BLOCKSIZE-2))+1,
2138 (rand()%(MAP_BLOCKSIZE-2))+1,
2139 (rand()%(MAP_BLOCKSIZE-2))+1
2143 n.d = CONTENT_COALSTONE;
2145 //dstream<<"Adding coalstone"<<std::endl;
2147 //if(is_ground_content(block->getNode(cp).d))
2148 if(block->getNode(cp).d == CONTENT_STONE)
2150 block->setNode(cp, n);
2152 for(u16 i=0; i<26; i++)
2154 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2155 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2157 block->setNode(cp+g_26dirs[i], n);
2164 Create a few rats in empty blocks underground
2168 //for(u16 i=0; i<2; i++)
2171 (rand()%(MAP_BLOCKSIZE-2))+1,
2172 (rand()%(MAP_BLOCKSIZE-2))+1,
2173 (rand()%(MAP_BLOCKSIZE-2))+1
2176 // Check that the place is empty
2177 //if(!is_ground_content(block->getNode(cp).d))
2180 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2181 block->addObject(obj);
2187 Add block to sector.
2189 sector->insertBlock(block);
2195 // An y-wise container of changed blocks
2196 core::map<s16, MapBlock*> changed_blocks_sector;
2199 Check if any sector's objects can be placed now.
2202 core::map<v3s16, u8> *objects = sector->getObjects();
2203 core::list<v3s16> objects_to_remove;
2204 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2205 i.atEnd() == false; i++)
2207 v3s16 p = i.getNode()->getKey();
2209 u8 d = i.getNode()->getValue();
2211 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2216 if(d == SECTOR_OBJECT_TEST)
2218 if(sector->isValidArea(p + v3s16(0,0,0),
2219 p + v3s16(0,0,0), &changed_blocks_sector))
2222 n.d = CONTENT_TORCH;
2223 sector->setNode(p, n);
2224 objects_to_remove.push_back(p);
2227 else if(d == SECTOR_OBJECT_TREE_1)
2229 v3s16 p_min = p + v3s16(-1,0,-1);
2230 v3s16 p_max = p + v3s16(1,4,1);
2231 if(sector->isValidArea(p_min, p_max,
2232 &changed_blocks_sector))
2236 sector->setNode(p+v3s16(0,0,0), n);
2237 sector->setNode(p+v3s16(0,1,0), n);
2238 sector->setNode(p+v3s16(0,2,0), n);
2239 sector->setNode(p+v3s16(0,3,0), n);
2241 n.d = CONTENT_LEAVES;
2243 sector->setNode(p+v3s16(0,4,0), n);
2245 sector->setNode(p+v3s16(-1,4,0), n);
2246 sector->setNode(p+v3s16(1,4,0), n);
2247 sector->setNode(p+v3s16(0,4,-1), n);
2248 sector->setNode(p+v3s16(0,4,1), n);
2249 sector->setNode(p+v3s16(1,4,1), n);
2250 sector->setNode(p+v3s16(-1,4,1), n);
2251 sector->setNode(p+v3s16(-1,4,-1), n);
2252 sector->setNode(p+v3s16(1,4,-1), n);
2254 sector->setNode(p+v3s16(-1,3,0), n);
2255 sector->setNode(p+v3s16(1,3,0), n);
2256 sector->setNode(p+v3s16(0,3,-1), n);
2257 sector->setNode(p+v3s16(0,3,1), n);
2258 sector->setNode(p+v3s16(1,3,1), n);
2259 sector->setNode(p+v3s16(-1,3,1), n);
2260 sector->setNode(p+v3s16(-1,3,-1), n);
2261 sector->setNode(p+v3s16(1,3,-1), n);
2263 objects_to_remove.push_back(p);
2265 // Lighting has to be recalculated for this one.
2266 sector->getBlocksInArea(p_min, p_max,
2267 lighting_invalidated_blocks);
2270 else if(d == SECTOR_OBJECT_BUSH_1)
2272 if(sector->isValidArea(p + v3s16(0,0,0),
2273 p + v3s16(0,0,0), &changed_blocks_sector))
2276 n.d = CONTENT_LEAVES;
2277 sector->setNode(p+v3s16(0,0,0), n);
2279 objects_to_remove.push_back(p);
2282 else if(d == SECTOR_OBJECT_RAVINE)
2285 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2286 v3s16 p_max = p + v3s16(6,6,6);
2287 if(sector->isValidArea(p_min, p_max,
2288 &changed_blocks_sector))
2291 n.d = CONTENT_STONE;
2294 s16 depth = maxdepth + (rand()%10);
2296 s16 minz = -6 - (-2);
2298 for(s16 x=-6; x<=6; x++)
2300 z += -1 + (rand()%3);
2305 for(s16 y=depth+(rand()%2); y<=6; y++)
2307 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2310 v3s16 p2 = p + v3s16(x,y,z-2);
2311 if(is_ground_content(sector->getNode(p2).d)
2312 && !is_mineral(sector->getNode(p2).d))
2313 sector->setNode(p2, n);
2316 v3s16 p2 = p + v3s16(x,y,z-1);
2317 if(is_ground_content(sector->getNode(p2).d)
2318 && !is_mineral(sector->getNode(p2).d))
2319 sector->setNode(p2, n2);
2322 v3s16 p2 = p + v3s16(x,y,z+0);
2323 if(is_ground_content(sector->getNode(p2).d)
2324 && !is_mineral(sector->getNode(p2).d))
2325 sector->setNode(p2, n2);
2328 v3s16 p2 = p + v3s16(x,y,z+1);
2329 if(is_ground_content(sector->getNode(p2).d)
2330 && !is_mineral(sector->getNode(p2).d))
2331 sector->setNode(p2, n);
2334 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2335 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2339 objects_to_remove.push_back(p);
2341 // Lighting has to be recalculated for this one.
2342 sector->getBlocksInArea(p_min, p_max,
2343 lighting_invalidated_blocks);
2348 dstream<<"ServerMap::emergeBlock(): "
2349 "Invalid heightmap object"
2354 catch(InvalidPositionException &e)
2356 dstream<<"WARNING: "<<__FUNCTION_NAME
2357 <<": while inserting object "<<(int)d
2358 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2359 <<" InvalidPositionException.what()="
2360 <<e.what()<<std::endl;
2361 // This is not too fatal and seems to happen sometimes.
2366 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2367 i != objects_to_remove.end(); i++)
2369 objects->remove(*i);
2372 for(core::map<s16, MapBlock*>::Iterator
2373 i = changed_blocks_sector.getIterator();
2374 i.atEnd() == false; i++)
2376 MapBlock *block = i.getNode()->getValue();
2378 changed_blocks.insert(block->getPos(), block);
2384 void ServerMap::createDir(std::string path)
2386 if(fs::CreateDir(path) == false)
2388 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2389 <<"\""<<path<<"\""<<std::endl;
2390 throw BaseException("ServerMap failed to create directory");
2394 std::string ServerMap::getSectorSubDir(v2s16 pos)
2397 snprintf(cc, 9, "%.4x%.4x",
2398 (unsigned int)pos.X&0xffff,
2399 (unsigned int)pos.Y&0xffff);
2401 return std::string(cc);
2404 std::string ServerMap::getSectorDir(v2s16 pos)
2406 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2409 v2s16 ServerMap::getSectorPos(std::string dirname)
2411 if(dirname.size() != 8)
2412 throw InvalidFilenameException("Invalid sector directory name");
2414 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2416 throw InvalidFilenameException("Invalid sector directory name");
2417 v2s16 pos((s16)x, (s16)y);
2421 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2423 v2s16 p2d = getSectorPos(sectordir);
2425 if(blockfile.size() != 4){
2426 throw InvalidFilenameException("Invalid block filename");
2429 int r = sscanf(blockfile.c_str(), "%4x", &y);
2431 throw InvalidFilenameException("Invalid block filename");
2432 return v3s16(p2d.X, y, p2d.Y);
2436 #define ENABLE_SECTOR_SAVING 1
2437 #define ENABLE_SECTOR_LOADING 1
2438 #define ENABLE_BLOCK_SAVING 1
2439 #define ENABLE_BLOCK_LOADING 1
2441 void ServerMap::save(bool only_changed)
2443 DSTACK(__FUNCTION_NAME);
2444 if(m_map_saving_enabled == false)
2446 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2450 if(only_changed == false)
2451 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2454 saveMasterHeightmap();
2456 u32 sector_meta_count = 0;
2457 u32 block_count = 0;
2460 JMutexAutoLock lock(m_sector_mutex);
2462 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2463 for(; i.atEnd() == false; i++)
2465 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2466 assert(sector->getId() == MAPSECTOR_SERVER);
2468 if(ENABLE_SECTOR_SAVING)
2470 if(sector->differs_from_disk || only_changed == false)
2472 saveSectorMeta(sector);
2473 sector_meta_count++;
2476 if(ENABLE_BLOCK_SAVING)
2478 core::list<MapBlock*> blocks;
2479 sector->getBlocks(blocks);
2480 core::list<MapBlock*>::Iterator j;
2481 for(j=blocks.begin(); j!=blocks.end(); j++)
2483 MapBlock *block = *j;
2484 if(block->getChangedFlag() || only_changed == false)
2496 Only print if something happened or saved whole map
2498 if(only_changed == false || sector_meta_count != 0
2499 || block_count != 0)
2501 dstream<<DTIME<<"ServerMap: Written: "
2502 <<sector_meta_count<<" sector metadata files, "
2503 <<block_count<<" block files"
2508 void ServerMap::loadAll()
2510 DSTACK(__FUNCTION_NAME);
2511 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2513 loadMasterHeightmap();
2515 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2517 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2519 JMutexAutoLock lock(m_sector_mutex);
2522 s32 printed_counter = -100000;
2523 s32 count = list.size();
2525 std::vector<fs::DirListNode>::iterator i;
2526 for(i=list.begin(); i!=list.end(); i++)
2528 if(counter > printed_counter + 10)
2530 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2531 printed_counter = counter;
2535 MapSector *sector = NULL;
2537 // We want directories
2541 sector = loadSectorMeta(i->name);
2543 catch(InvalidFilenameException &e)
2545 // This catches unknown crap in directory
2548 if(ENABLE_BLOCK_LOADING)
2550 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2551 (m_savedir+"/sectors/"+i->name);
2552 std::vector<fs::DirListNode>::iterator i2;
2553 for(i2=list2.begin(); i2!=list2.end(); i2++)
2559 loadBlock(i->name, i2->name, sector);
2561 catch(InvalidFilenameException &e)
2563 // This catches unknown crap in directory
2568 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2571 void ServerMap::saveMasterHeightmap()
2573 DSTACK(__FUNCTION_NAME);
2574 createDir(m_savedir);
2576 std::string fullpath = m_savedir + "/master_heightmap";
2577 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2578 if(o.good() == false)
2579 throw FileNotGoodException("Cannot open master heightmap");
2581 // Format used for writing
2582 u8 version = SER_FMT_VER_HIGHEST;
2585 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2587 [0] u8 serialization version
2588 [1] X master heightmap
2590 u32 fullsize = 1 + hmdata.getSize();
2591 SharedBuffer<u8> data(fullsize);
2594 memcpy(&data[1], *hmdata, hmdata.getSize());
2596 o.write((const char*)*data, fullsize);
2599 m_heightmap->serialize(o, version);
2602 void ServerMap::loadMasterHeightmap()
2604 DSTACK(__FUNCTION_NAME);
2605 std::string fullpath = m_savedir + "/master_heightmap";
2606 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2607 if(is.good() == false)
2608 throw FileNotGoodException("Cannot open master heightmap");
2610 if(m_heightmap != NULL)
2613 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2616 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2618 DSTACK(__FUNCTION_NAME);
2619 // Format used for writing
2620 u8 version = SER_FMT_VER_HIGHEST;
2622 v2s16 pos = sector->getPos();
2623 createDir(m_savedir);
2624 createDir(m_savedir+"/sectors");
2625 std::string dir = getSectorDir(pos);
2628 std::string fullpath = dir + "/heightmap";
2629 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2630 if(o.good() == false)
2631 throw FileNotGoodException("Cannot open master heightmap");
2633 sector->serialize(o, version);
2635 sector->differs_from_disk = false;
2638 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2640 DSTACK(__FUNCTION_NAME);
2642 v2s16 p2d = getSectorPos(dirname);
2643 std::string dir = m_savedir + "/sectors/" + dirname;
2645 std::string fullpath = dir + "/heightmap";
2646 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2647 if(is.good() == false)
2648 throw FileNotGoodException("Cannot open sector heightmap");
2650 ServerMapSector *sector = ServerMapSector::deSerialize
2651 (is, this, p2d, &m_hwrapper, m_sectors);
2653 sector->differs_from_disk = false;
2658 bool ServerMap::loadSectorFull(v2s16 p2d)
2660 DSTACK(__FUNCTION_NAME);
2661 std::string sectorsubdir = getSectorSubDir(p2d);
2663 MapSector *sector = NULL;
2665 JMutexAutoLock lock(m_sector_mutex);
2668 sector = loadSectorMeta(sectorsubdir);
2670 catch(InvalidFilenameException &e)
2674 catch(FileNotGoodException &e)
2678 catch(std::exception &e)
2683 if(ENABLE_BLOCK_LOADING)
2685 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2686 (m_savedir+"/sectors/"+sectorsubdir);
2687 std::vector<fs::DirListNode>::iterator i2;
2688 for(i2=list2.begin(); i2!=list2.end(); i2++)
2694 loadBlock(sectorsubdir, i2->name, sector);
2696 catch(InvalidFilenameException &e)
2698 // This catches unknown crap in directory
2706 bool ServerMap::deFlushSector(v2s16 p2d)
2708 DSTACK(__FUNCTION_NAME);
2709 // See if it already exists in memory
2711 MapSector *sector = getSectorNoGenerate(p2d);
2714 catch(InvalidPositionException &e)
2717 Try to load the sector from disk.
2719 if(loadSectorFull(p2d) == true)
2728 void ServerMap::saveBlock(MapBlock *block)
2730 DSTACK(__FUNCTION_NAME);
2732 Dummy blocks are not written
2734 if(block->isDummy())
2736 /*v3s16 p = block->getPos();
2737 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2738 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2742 // Format used for writing
2743 u8 version = SER_FMT_VER_HIGHEST;
2745 v3s16 p3d = block->getPos();
2746 v2s16 p2d(p3d.X, p3d.Z);
2747 createDir(m_savedir);
2748 createDir(m_savedir+"/sectors");
2749 std::string dir = getSectorDir(p2d);
2752 // Block file is map/sectors/xxxxxxxx/xxxx
2754 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2755 std::string fullpath = dir + "/" + cc;
2756 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2757 if(o.good() == false)
2758 throw FileNotGoodException("Cannot open block data");
2761 [0] u8 serialization version
2764 o.write((char*)&version, 1);
2766 block->serialize(o, version);
2769 Versions up from 9 have block objects.
2773 block->serializeObjects(o, version);
2776 // We just wrote it to the disk
2777 block->resetChangedFlag();
2780 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2782 DSTACK(__FUNCTION_NAME);
2786 // Block file is map/sectors/xxxxxxxx/xxxx
2787 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2788 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2789 if(is.good() == false)
2790 throw FileNotGoodException("Cannot open block file");
2792 v3s16 p3d = getBlockPos(sectordir, blockfile);
2793 v2s16 p2d(p3d.X, p3d.Z);
2795 assert(sector->getPos() == p2d);
2797 u8 version = SER_FMT_VER_INVALID;
2798 is.read((char*)&version, 1);
2800 /*u32 block_size = MapBlock::serializedLength(version);
2801 SharedBuffer<u8> data(block_size);
2802 is.read((char*)*data, block_size);*/
2804 // This will always return a sector because we're the server
2805 //MapSector *sector = emergeSector(p2d);
2807 MapBlock *block = NULL;
2808 bool created_new = false;
2810 block = sector->getBlockNoCreate(p3d.Y);
2812 catch(InvalidPositionException &e)
2814 block = sector->createBlankBlockNoInsert(p3d.Y);
2818 // deserialize block data
2819 block->deSerialize(is, version);
2822 Versions up from 9 have block objects.
2826 block->updateObjects(is, version, NULL, 0);
2830 sector->insertBlock(block);
2833 Convert old formats to new and save
2836 // Save old format blocks in new format
2837 if(version < SER_FMT_VER_HIGHEST)
2842 // We just loaded it from the disk, so it's up-to-date.
2843 block->resetChangedFlag();
2846 catch(SerializationError &e)
2848 dstream<<"WARNING: Invalid block data on disk "
2849 "(SerializationError). Ignoring."
2854 // Gets from master heightmap
2855 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2857 assert(m_heightmap != NULL);
2865 corners[0] = m_heightmap->getGroundHeight
2866 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2867 corners[1] = m_heightmap->getGroundHeight
2868 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2869 corners[2] = m_heightmap->getGroundHeight
2870 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2871 corners[3] = m_heightmap->getGroundHeight
2872 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2875 void ServerMap::PrintInfo(std::ostream &out)
2886 ClientMap::ClientMap(
2888 JMutex &range_mutex,
2889 s16 &viewing_range_nodes,
2890 bool &viewing_range_all,
2891 scene::ISceneNode* parent,
2892 scene::ISceneManager* mgr,
2896 scene::ISceneNode(parent, mgr, id),
2899 m_range_mutex(range_mutex),
2900 m_viewing_range_nodes(viewing_range_nodes),
2901 m_viewing_range_all(viewing_range_all)
2905 /*m_box = core::aabbox3d<f32>(0,0,0,
2906 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2907 /*m_box = core::aabbox3d<f32>(0,0,0,
2908 map->getSizeNodes().X * BS,
2909 map->getSizeNodes().Y * BS,
2910 map->getSizeNodes().Z * BS);*/
2911 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2912 BS*1000000,BS*1000000,BS*1000000);
2914 //setPosition(v3f(BS,BS,BS));
2917 ClientMap::~ClientMap()
2919 JMutexAutoLock lock(mesh_mutex);
2928 MapSector * ClientMap::emergeSector(v2s16 p2d)
2930 DSTACK(__FUNCTION_NAME);
2931 // Check that it doesn't exist already
2933 return getSectorNoGenerate(p2d);
2935 catch(InvalidPositionException &e)
2939 // Create a sector with no heightmaps
2940 ClientMapSector *sector = new ClientMapSector(this, p2d);
2943 JMutexAutoLock lock(m_sector_mutex);
2944 m_sectors.insert(p2d, sector);
2950 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2952 DSTACK(__FUNCTION_NAME);
2953 ClientMapSector *sector = NULL;
2955 JMutexAutoLock lock(m_sector_mutex);
2957 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2961 sector = (ClientMapSector*)n->getValue();
2962 assert(sector->getId() == MAPSECTOR_CLIENT);
2966 sector = new ClientMapSector(this, p2d);
2968 JMutexAutoLock lock(m_sector_mutex);
2969 m_sectors.insert(p2d, sector);
2973 sector->deSerialize(is);
2976 void ClientMap::OnRegisterSceneNode()
2980 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2981 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2984 ISceneNode::OnRegisterSceneNode();
2987 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2989 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2990 DSTACK(__FUNCTION_NAME);
2992 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2995 Get time for measuring timeout.
2997 Measuring time is very useful for long delays when the
2998 machine is swapping a lot.
3000 int time1 = time(0);
3002 //s32 daynight_i = m_client->getDayNightIndex();
3003 u32 daynight_ratio = m_client->getDayNightRatio();
3006 Collect all blocks that are in the view range
3008 Should not optimize more here as we want to auto-update
3009 all changed nodes in viewing range at the next step.
3012 s16 viewing_range_nodes;
3013 bool viewing_range_all;
3015 JMutexAutoLock lock(m_range_mutex);
3016 viewing_range_nodes = m_viewing_range_nodes;
3017 viewing_range_all = m_viewing_range_all;
3020 m_camera_mutex.Lock();
3021 v3f camera_position = m_camera_position;
3022 v3f camera_direction = m_camera_direction;
3023 m_camera_mutex.Unlock();
3026 Get all blocks and draw all visible ones
3029 v3s16 cam_pos_nodes(
3030 camera_position.X / BS,
3031 camera_position.Y / BS,
3032 camera_position.Z / BS);
3034 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
3036 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3037 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3039 // Take a fair amount as we will be dropping more out later
3041 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3042 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3043 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3045 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3046 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3047 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3049 u32 vertex_count = 0;
3051 // For limiting number of mesh updates per frame
3052 u32 mesh_update_count = 0;
3054 //NOTE: The sectors map should be locked but we're not doing it
3055 // because it'd cause too much delays
3057 int timecheck_counter = 0;
3059 core::map<v2s16, MapSector*>::Iterator si;
3060 si = m_sectors.getIterator();
3061 for(; si.atEnd() == false; si++)
3064 timecheck_counter++;
3065 if(timecheck_counter > 50)
3067 int time2 = time(0);
3068 if(time2 > time1 + 4)
3070 dstream<<"ClientMap::renderMap(): "
3071 "Rendering takes ages, returning."
3078 MapSector *sector = si.getNode()->getValue();
3079 v2s16 sp = sector->getPos();
3081 if(viewing_range_all == false)
3083 if(sp.X < p_blocks_min.X
3084 || sp.X > p_blocks_max.X
3085 || sp.Y < p_blocks_min.Z
3086 || sp.Y > p_blocks_max.Z)
3090 core::list< MapBlock * > sectorblocks;
3091 sector->getBlocks(sectorblocks);
3097 core::list< MapBlock * >::Iterator i;
3098 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3100 MapBlock *block = *i;
3103 Compare block position to camera position, skip
3104 if not seen on display
3107 v3s16 blockpos_nodes = block->getPosRelative();
3109 // Block center position
3111 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3112 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3113 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3116 // Block position relative to camera
3117 v3f blockpos_relative = blockpos - camera_position;
3119 // Distance in camera direction (+=front, -=back)
3120 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3123 f32 d = blockpos_relative.getLength();
3125 if(viewing_range_all == false)
3127 // If block is far away, don't draw it
3128 if(d > viewing_range_nodes * BS)
3129 // This is nicer when fog is used
3130 //if((dforward+d)/2 > viewing_range_nodes * BS)
3134 // Maximum radius of a block
3135 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3137 // If block is (nearly) touching the camera, don't
3138 // bother validating further (that is, render it anyway)
3139 if(d > block_max_radius * 1.5)
3141 // Cosine of the angle between the camera direction
3142 // and the block direction (camera_direction is an unit vector)
3143 f32 cosangle = dforward / d;
3145 // Compensate for the size of the block
3146 // (as the block has to be shown even if it's a bit off FOV)
3147 // This is an estimate.
3148 cosangle += block_max_radius / dforward;
3150 // If block is not in the field of view, skip it
3151 //if(cosangle < cos(FOV_ANGLE/2))
3152 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3157 Draw the faces of the block
3160 bool mesh_expired = false;
3163 JMutexAutoLock lock(block->mesh_mutex);
3165 mesh_expired = block->getMeshExpired();
3167 // Mesh has not been expired and there is no mesh:
3168 // block has no content
3169 if(block->mesh == NULL && mesh_expired == false)
3173 f32 faraway = BS*50;
3174 //f32 faraway = viewing_range_nodes * BS;
3177 This has to be done with the mesh_mutex unlocked
3179 if(mesh_expired && mesh_update_count < 6
3180 && (d < faraway || mesh_update_count < 3))
3181 //if(mesh_expired && mesh_update_count < 4)
3183 mesh_update_count++;
3185 // Mesh has been expired: generate new mesh
3186 //block->updateMeshes(daynight_i);
3187 block->updateMesh(daynight_ratio);
3189 mesh_expired = false;
3193 Don't draw an expired mesh that is far away
3195 /*if(mesh_expired && d >= faraway)
3198 // Instead, delete it
3199 JMutexAutoLock lock(block->mesh_mutex);
3202 block->mesh->drop();
3205 // And continue to next block
3210 JMutexAutoLock lock(block->mesh_mutex);
3212 scene::SMesh *mesh = block->mesh;
3217 u32 c = mesh->getMeshBufferCount();
3219 for(u32 i=0; i<c; i++)
3221 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3222 const video::SMaterial& material = buf->getMaterial();
3223 video::IMaterialRenderer* rnd =
3224 driver->getMaterialRenderer(material.MaterialType);
3225 bool transparent = (rnd && rnd->isTransparent());
3226 // Render transparent on transparent pass and likewise.
3227 if(transparent == is_transparent_pass)
3229 driver->setMaterial(buf->getMaterial());
3230 driver->drawMeshBuffer(buf);
3231 vertex_count += buf->getVertexCount();
3235 } // foreach sectorblocks
3238 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3239 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3242 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3245 Add it to all blocks touching it
3248 v3s16(0,0,0), // this
3249 v3s16(0,0,1), // back
3250 v3s16(0,1,0), // top
3251 v3s16(1,0,0), // right
3252 v3s16(0,0,-1), // front
3253 v3s16(0,-1,0), // bottom
3254 v3s16(-1,0,0), // left
3256 for(u16 i=0; i<7; i++)
3258 v3s16 p2 = p + dirs[i];
3259 // Block position of neighbor (or requested) node
3260 v3s16 blockpos = getNodeBlockPos(p2);
3261 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3262 if(blockref == NULL)
3264 // Relative position of requested node
3265 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3266 blockref->setTempMod(relpos, mod);
3268 return getNodeBlockPos(p);
3270 v3s16 ClientMap::clearTempMod(v3s16 p)
3273 v3s16(0,0,0), // this
3274 v3s16(0,0,1), // back
3275 v3s16(0,1,0), // top
3276 v3s16(1,0,0), // right
3277 v3s16(0,0,-1), // front
3278 v3s16(0,-1,0), // bottom
3279 v3s16(-1,0,0), // left
3281 for(u16 i=0; i<7; i++)
3283 v3s16 p2 = p + dirs[i];
3284 // Block position of neighbor (or requested) node
3285 v3s16 blockpos = getNodeBlockPos(p2);
3286 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3287 if(blockref == NULL)
3289 // Relative position of requested node
3290 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3291 blockref->clearTempMod(relpos);
3293 return getNodeBlockPos(p);
3296 void ClientMap::PrintInfo(std::ostream &out)
3307 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3312 MapVoxelManipulator::~MapVoxelManipulator()
3314 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3319 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3321 TimeTaker timer1("emerge", &emerge_time);
3323 // Units of these are MapBlocks
3324 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3325 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3327 VoxelArea block_area_nodes
3328 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3330 addArea(block_area_nodes);
3332 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3333 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3334 for(s32 x=p_min.X; x<=p_max.X; x++)
3337 core::map<v3s16, bool>::Node *n;
3338 n = m_loaded_blocks.find(p);
3342 bool block_data_inexistent = false;
3345 TimeTaker timer1("emerge load", &emerge_load_time);
3347 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3348 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3351 dstream<<std::endl;*/
3353 MapBlock *block = m_map->getBlockNoCreate(p);
3354 if(block->isDummy())
3355 block_data_inexistent = true;
3357 block->copyTo(*this);
3359 catch(InvalidPositionException &e)
3361 block_data_inexistent = true;
3364 if(block_data_inexistent)
3366 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3367 // Fill with VOXELFLAG_INEXISTENT
3368 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3369 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3371 s32 i = m_area.index(a.MinEdge.X,y,z);
3372 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3376 m_loaded_blocks.insert(p, true);
3379 //dstream<<"emerge done"<<std::endl;
3384 void MapVoxelManipulator::emerge(VoxelArea a)
3386 TimeTaker timer1("emerge", &emerge_time);
3388 v3s16 size = a.getExtent();
3390 VoxelArea padded = a;
3391 padded.pad(m_area.getExtent() / 4);
3394 for(s16 z=0; z<size.Z; z++)
3395 for(s16 y=0; y<size.Y; y++)
3396 for(s16 x=0; x<size.X; x++)
3399 s32 i = m_area.index(a.MinEdge + p);
3400 // Don't touch nodes that have already been loaded
3401 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3405 TimeTaker timer1("emerge load", &emerge_load_time);
3406 MapNode n = m_map->getNode(a.MinEdge + p);
3410 catch(InvalidPositionException &e)
3412 m_flags[i] = VOXELFLAG_INEXISTENT;
3420 TODO: Add an option to only update eg. water and air nodes.
3421 This will make it interfere less with important stuff if
3424 void MapVoxelManipulator::blitBack
3425 (core::map<v3s16, MapBlock*> & modified_blocks)
3427 if(m_area.getExtent() == v3s16(0,0,0))
3430 //TimeTaker timer1("blitBack");
3433 Initialize block cache
3435 v3s16 blockpos_last;
3436 MapBlock *block = NULL;
3437 bool block_checked_in_modified = false;
3439 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3440 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3441 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3445 u8 f = m_flags[m_area.index(p)];
3446 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3449 MapNode &n = m_data[m_area.index(p)];
3451 v3s16 blockpos = getNodeBlockPos(p);
3456 if(block == NULL || blockpos != blockpos_last){
3457 block = m_map->getBlockNoCreate(blockpos);
3458 blockpos_last = blockpos;
3459 block_checked_in_modified = false;
3462 // Calculate relative position in block
3463 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3465 // Don't continue if nothing has changed here
3466 if(block->getNode(relpos) == n)
3469 //m_map->setNode(m_area.MinEdge + p, n);
3470 block->setNode(relpos, n);
3473 Make sure block is in modified_blocks
3475 if(block_checked_in_modified == false)
3477 modified_blocks[blockpos] = block;
3478 block_checked_in_modified = true;
3481 catch(InvalidPositionException &e)