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 #define sleep_ms(x) Sleep(x)
33 #define sleep_ms(x) usleep(x*1000)
36 MapBlockPointerCache::MapBlockPointerCache(Map *map)
39 m_map->m_blockcachelock.cacheCreated();
41 m_from_cache_count = 0;
45 MapBlockPointerCache::~MapBlockPointerCache()
47 m_map->m_blockcachelock.cacheRemoved();
49 /*dstream<<"MapBlockPointerCache:"
50 <<" from_cache_count="<<m_from_cache_count
51 <<" from_map_count="<<m_from_map_count
55 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
57 core::map<v3s16, MapBlock*>::Node *n = NULL;
67 // Throws InvalidPositionException if not found
68 MapBlock *b = m_map->getBlockNoCreate(p);
77 Map::Map(std::ostream &dout):
79 m_camera_position(0,0,0),
80 m_camera_direction(0,0,1),
85 m_sector_mutex.Init();
86 m_camera_mutex.Init();
87 assert(m_sector_mutex.IsInitialized());
88 assert(m_camera_mutex.IsInitialized());
90 // Get this so that the player can stay on it at first
91 //getSector(v2s16(0,0));
99 /*updater.setRun(false);
100 while(updater.IsRunning())
106 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
107 for(; i.atEnd() == false; i++)
109 MapSector *sector = i.getNode()->getValue();
114 MapSector * Map::getSectorNoGenerate(v2s16 p)
116 JMutexAutoLock lock(m_sector_mutex);
118 if(m_sector_cache != NULL && p == m_sector_cache_p){
119 MapSector * sector = m_sector_cache;
120 // Reset inactivity timer
121 sector->usage_timer = 0.0;
125 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
126 // If sector doesn't exist, throw an exception
129 throw InvalidPositionException();
132 MapSector *sector = n->getValue();
134 // Cache the last result
135 m_sector_cache_p = p;
136 m_sector_cache = sector;
138 //MapSector * ref(sector);
140 // Reset inactivity timer
141 sector->usage_timer = 0.0;
145 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorNoGenerate(p2d);
150 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
155 f32 Map::getGroundHeight(v2s16 p, bool generate)
158 v2s16 sectorpos = getNodeSectorPos(p);
159 MapSector * sref = getSectorNoGenerate(sectorpos);
160 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
161 f32 y = sref->getGroundHeight(relpos);
164 catch(InvalidPositionException &e)
166 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
170 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
172 /*m_dout<<DTIME<<"Map::setGroundHeight(("
174 <<"), "<<y<<")"<<std::endl;*/
175 v2s16 sectorpos = getNodeSectorPos(p);
176 MapSector * sref = getSectorNoGenerate(sectorpos);
177 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
178 //sref->mutex.Lock();
179 sref->setGroundHeight(relpos, y);
180 //sref->mutex.Unlock();
183 bool Map::isNodeUnderground(v3s16 p)
185 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock * block = getBlockNoCreate(blockpos);
188 return block->getIsUnderground();
190 catch(InvalidPositionException &e)
197 Goes recursively through the neighbours of the node.
199 Alters only transparent nodes.
201 If the lighting of the neighbour is lower than the lighting of
202 the node was (before changing it to 0 at the step before), the
203 lighting of the neighbour is set to 0 and then the same stuff
204 repeats for the neighbour.
206 The ending nodes of the routine are stored in light_sources.
207 This is useful when a light is removed. In such case, this
208 routine can be called for the light node and then again for
209 light_sources to re-light the area without the removed light.
211 values of from_nodes are lighting values.
213 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
214 core::map<v3s16, bool> & light_sources,
215 core::map<v3s16, MapBlock*> & modified_blocks)
218 v3s16(0,0,1), // back
220 v3s16(1,0,0), // right
221 v3s16(0,0,-1), // front
222 v3s16(0,-1,0), // bottom
223 v3s16(-1,0,0), // left
226 if(from_nodes.size() == 0)
229 u32 blockchangecount = 0;
231 core::map<v3s16, u8> unlighted_nodes;
232 core::map<v3s16, u8>::Iterator j;
233 j = from_nodes.getIterator();
236 Initialize block cache
239 MapBlock *block = NULL;
240 // Cache this a bit, too
241 bool block_checked_in_modified = false;
243 for(; j.atEnd() == false; j++)
245 v3s16 pos = j.getNode()->getKey();
246 v3s16 blockpos = getNodeBlockPos(pos);
248 // Only fetch a new block if the block position has changed
250 if(block == NULL || blockpos != blockpos_last){
251 block = getBlockNoCreate(blockpos);
252 blockpos_last = blockpos;
254 block_checked_in_modified = false;
258 catch(InvalidPositionException &e)
266 // Calculate relative position in block
267 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
269 // Get node straight from the block
270 MapNode n = block->getNode(relpos);
272 u8 oldlight = j.getNode()->getValue();
274 // Loop through 6 neighbors
275 for(u16 i=0; i<6; i++)
277 // Get the position of the neighbor node
278 v3s16 n2pos = pos + dirs[i];
280 // Get the block where the node is located
281 v3s16 blockpos = getNodeBlockPos(n2pos);
285 // Only fetch a new block if the block position has changed
287 if(block == NULL || blockpos != blockpos_last){
288 block = getBlockNoCreate(blockpos);
289 blockpos_last = blockpos;
291 block_checked_in_modified = false;
295 catch(InvalidPositionException &e)
300 // Calculate relative position in block
301 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
302 // Get node straight from the block
303 MapNode n2 = block->getNode(relpos);
305 bool changed = false;
307 //TODO: Optimize output by optimizing light_sources?
310 If the neighbor is dimmer than what was specified
311 as oldlight (the light of the previous node)
313 if(n2.getLight() < oldlight)
316 And the neighbor is transparent and it has some light
318 if(n2.light_propagates() && n2.getLight() != 0)
321 Set light to 0 and add to queue
324 u8 current_light = n2.getLight();
326 block->setNode(relpos, n2);
328 unlighted_nodes.insert(n2pos, current_light);
332 Remove from light_sources if it is there
333 NOTE: This doesn't happen nearly at all
335 /*if(light_sources.find(n2pos))
337 std::cout<<"Removed from light_sources"<<std::endl;
338 light_sources.remove(n2pos);
343 if(light_sources.find(n2pos) != NULL)
344 light_sources.remove(n2pos);*/
347 light_sources.insert(n2pos, true);
350 // Add to modified_blocks
351 if(changed == true && block_checked_in_modified == false)
353 // If the block is not found in modified_blocks, add.
354 if(modified_blocks.find(blockpos) == NULL)
356 modified_blocks.insert(blockpos, block);
358 block_checked_in_modified = true;
361 catch(InvalidPositionException &e)
368 /*dstream<<"unspreadLight(): Changed block "
369 <<blockchangecount<<" times"
370 <<" for "<<from_nodes.size()<<" nodes"
373 if(unlighted_nodes.size() > 0)
374 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
378 A single-node wrapper of the above
380 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
381 core::map<v3s16, bool> & light_sources,
382 core::map<v3s16, MapBlock*> & modified_blocks)
384 core::map<v3s16, u8> from_nodes;
385 from_nodes.insert(pos, lightwas);
387 unspreadLight(from_nodes, light_sources, modified_blocks);
391 Lights neighbors of from_nodes, collects all them and then
394 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
395 core::map<v3s16, MapBlock*> & modified_blocks)
397 const v3s16 dirs[6] = {
398 v3s16(0,0,1), // back
400 v3s16(1,0,0), // right
401 v3s16(0,0,-1), // front
402 v3s16(0,-1,0), // bottom
403 v3s16(-1,0,0), // left
406 if(from_nodes.size() == 0)
409 u32 blockchangecount = 0;
411 core::map<v3s16, bool> lighted_nodes;
412 core::map<v3s16, bool>::Iterator j;
413 j = from_nodes.getIterator();
416 Initialize block cache
419 MapBlock *block = NULL;
420 // Cache this a bit, too
421 bool block_checked_in_modified = false;
423 for(; j.atEnd() == false; j++)
424 //for(; j != from_nodes.end(); j++)
426 v3s16 pos = j.getNode()->getKey();
428 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
429 v3s16 blockpos = getNodeBlockPos(pos);
431 // Only fetch a new block if the block position has changed
433 if(block == NULL || blockpos != blockpos_last){
434 block = getBlockNoCreate(blockpos);
435 blockpos_last = blockpos;
437 block_checked_in_modified = false;
441 catch(InvalidPositionException &e)
449 // Calculate relative position in block
450 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
452 // Get node straight from the block
453 MapNode n = block->getNode(relpos);
455 u8 oldlight = n.getLight();
456 u8 newlight = diminish_light(oldlight);
458 // Loop through 6 neighbors
459 for(u16 i=0; i<6; i++){
460 // Get the position of the neighbor node
461 v3s16 n2pos = pos + dirs[i];
463 // Get the block where the node is located
464 v3s16 blockpos = getNodeBlockPos(n2pos);
468 // Only fetch a new block if the block position has changed
470 if(block == NULL || blockpos != blockpos_last){
471 block = getBlockNoCreate(blockpos);
472 blockpos_last = blockpos;
474 block_checked_in_modified = false;
478 catch(InvalidPositionException &e)
483 // Calculate relative position in block
484 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
485 // Get node straight from the block
486 MapNode n2 = block->getNode(relpos);
488 bool changed = false;
490 If the neighbor is brighter than the current node,
491 add to list (it will light up this node on its turn)
493 if(n2.getLight() > undiminish_light(oldlight))
495 lighted_nodes.insert(n2pos, true);
496 //lighted_nodes.push_back(n2pos);
500 If the neighbor is dimmer than how much light this node
501 would spread on it, add to list
503 if(n2.getLight() < newlight)
505 if(n2.light_propagates())
507 n2.setLight(newlight);
508 block->setNode(relpos, n2);
509 lighted_nodes.insert(n2pos, true);
510 //lighted_nodes.push_back(n2pos);
515 // Add to modified_blocks
516 if(changed == true && block_checked_in_modified == false)
518 // If the block is not found in modified_blocks, add.
519 if(modified_blocks.find(blockpos) == NULL)
521 modified_blocks.insert(blockpos, block);
523 block_checked_in_modified = true;
526 catch(InvalidPositionException &e)
533 /*dstream<<"spreadLight(): Changed block "
534 <<blockchangecount<<" times"
535 <<" for "<<from_nodes.size()<<" nodes"
538 if(lighted_nodes.size() > 0)
539 spreadLight(lighted_nodes, modified_blocks);
543 A single-node source variation of the above.
545 void Map::lightNeighbors(v3s16 pos,
546 core::map<v3s16, MapBlock*> & modified_blocks)
548 core::map<v3s16, bool> from_nodes;
549 from_nodes.insert(pos, true);
550 spreadLight(from_nodes, modified_blocks);
553 v3s16 Map::getBrightestNeighbour(v3s16 p)
556 v3s16(0,0,1), // back
558 v3s16(1,0,0), // right
559 v3s16(0,0,-1), // front
560 v3s16(0,-1,0), // bottom
561 v3s16(-1,0,0), // left
564 u8 brightest_light = 0;
565 v3s16 brightest_pos(0,0,0);
566 bool found_something = false;
568 // Loop through 6 neighbors
569 for(u16 i=0; i<6; i++){
570 // Get the position of the neighbor node
571 v3s16 n2pos = p + dirs[i];
576 catch(InvalidPositionException &e)
580 if(n2.getLight() > brightest_light || found_something == false){
581 brightest_light = n2.getLight();
582 brightest_pos = n2pos;
583 found_something = true;
587 if(found_something == false)
588 throw InvalidPositionException();
590 return brightest_pos;
594 Propagates sunlight down from a node.
595 Starting point gets sunlight.
597 Returns the lowest y value of where the sunlight went.
599 s16 Map::propagateSunlight(v3s16 start,
600 core::map<v3s16, MapBlock*> & modified_blocks)
605 v3s16 pos(start.X, y, start.Z);
607 v3s16 blockpos = getNodeBlockPos(pos);
610 block = getBlockNoCreate(blockpos);
612 catch(InvalidPositionException &e)
617 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
618 MapNode n = block->getNode(relpos);
620 if(n.sunlight_propagates())
622 n.setLight(LIGHT_SUN);
623 block->setNode(relpos, n);
625 modified_blocks.insert(blockpos, block);
634 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
635 core::map<v3s16, MapBlock*> & modified_blocks)
637 /*m_dout<<DTIME<<"Map::updateLighting(): "
638 <<a_blocks.getSize()<<" blocks... ";*/
642 u32 count_was = modified_blocks.size();
644 core::map<v3s16, bool> light_sources;
646 core::map<v3s16, u8> unlight_from;
648 core::map<v3s16, MapBlock*>::Iterator i;
649 i = a_blocks.getIterator();
650 for(; i.atEnd() == false; i++)
652 MapBlock *block = i.getNode()->getValue();
656 // Don't bother with dummy blocks.
660 v3s16 pos = block->getPos();
661 modified_blocks.insert(pos, block);
664 Clear all light from block
666 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
667 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
668 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
673 MapNode n = block->getNode(v3s16(x,y,z));
674 u8 oldlight = n.getLight();
676 block->setNode(v3s16(x,y,z), n);
678 // Collect borders for unlighting
679 if(x==0 || x == MAP_BLOCKSIZE-1
680 || y==0 || y == MAP_BLOCKSIZE-1
681 || z==0 || z == MAP_BLOCKSIZE-1)
683 v3s16 p_map = p + v3s16(
686 MAP_BLOCKSIZE*pos.Z);
687 unlight_from.insert(p_map, oldlight);
690 catch(InvalidPositionException &e)
693 This would happen when dealing with a
697 dstream<<"updateLighting(): InvalidPositionException"
702 bool bottom_valid = block->propagateSunlight(light_sources);
704 // If bottom is valid, we're done.
708 /*dstream<<"Bottom for sunlight-propagated block ("
709 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
712 // Else get the block below and loop to it
716 block = getBlockNoCreate(pos);
718 catch(InvalidPositionException &e)
727 //TimeTaker timer("unspreadLight", g_device);
728 unspreadLight(unlight_from, light_sources, modified_blocks);
733 u32 diff = modified_blocks.size() - count_was;
734 count_was = modified_blocks.size();
735 dstream<<"unspreadLight modified "<<diff<<std::endl;
738 // TODO: Spread light from propagated sunlight?
739 // Yes, add it to light_sources... somehow.
740 // It has to be added at somewhere above, in the loop.
742 // NOTE: This actually works quite fine without it
743 // - Find out why it works
746 //TimeTaker timer("spreadLight", g_device);
747 spreadLight(light_sources, modified_blocks);
752 u32 diff = modified_blocks.size() - count_was;
753 count_was = modified_blocks.size();
754 dstream<<"spreadLight modified "<<diff<<std::endl;
757 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
761 This is called after changing a node from transparent to opaque.
762 The lighting value of the node should be left as-is after changing
763 other values. This sets the lighting value to 0.
765 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
766 core::map<v3s16, MapBlock*> &modified_blocks)*/
767 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
768 core::map<v3s16, MapBlock*> &modified_blocks)
771 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
772 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
774 u8 lightwas = getNode(p).getLight();
776 //core::list<v3s16> light_sources;
777 core::map<v3s16, bool> light_sources;
778 //MapNode n = getNode(p);
781 From this node to nodes underneath:
782 If lighting is sunlight (1.0), unlight neighbours and
787 bool node_under_sunlight = true;
789 v3s16 toppos = p + v3s16(0,1,0);
792 If there is a node at top and it doesn't have sunlight,
793 there has not been any sunlight going down.
795 Otherwise there probably is.
798 MapNode topnode = getNode(toppos);
800 if(topnode.getLight() != LIGHT_SUN)
801 node_under_sunlight = false;
803 catch(InvalidPositionException &e)
807 // Add the block of the added node to modified_blocks
808 v3s16 blockpos = getNodeBlockPos(p);
809 MapBlock * block = getBlockNoCreate(blockpos);
810 assert(block != NULL);
811 modified_blocks.insert(blockpos, block);
813 if(isValidPosition(p) == false)
816 // Unlight neighbours of node.
817 // This means setting light of all consequent dimmer nodes
819 // This also collects the nodes at the border which will spread
820 // light again into this.
821 unLightNeighbors(p, lightwas, light_sources, modified_blocks);
827 If node is under sunlight, take all sunlighted nodes under
828 it and clear light from them and from where the light has
831 if(node_under_sunlight)
835 //m_dout<<DTIME<<"y="<<y<<std::endl;
836 v3s16 n2pos(p.X, y, p.Z);
842 catch(InvalidPositionException &e)
847 if(n2.getLight() == LIGHT_SUN)
849 //m_dout<<DTIME<<"doing"<<std::endl;
850 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
860 Spread light from all nodes that might be capable of doing so
861 TODO: Convert to spreadLight
863 spreadLight(light_sources, modified_blocks);
868 void Map::removeNodeAndUpdate(v3s16 p,
869 core::map<v3s16, MapBlock*> &modified_blocks)
872 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
873 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
875 bool node_under_sunlight = true;
877 v3s16 toppos = p + v3s16(0,1,0);
879 // Node will be replaced with this
880 u8 replace_material = CONTENT_AIR;
882 // NOTE: Water is now managed elsewhere
886 Find out with what material the node will be replaced.
887 It will be replaced with the mostly seen buildable_to.
891 v3s16(0,0,1), // back
893 v3s16(1,0,0), // right
894 v3s16(0,0,-1), // front
895 v3s16(0,-1,0), // bottom
896 v3s16(-1,0,0), // left
899 core::map<u8, u16> neighbor_rankings;
901 for(u32 i=0; i<sizeof(dirs)/sizeof(dirs[0]); i++)
904 MapNode n2 = getNode(p + dirs[i]);
906 if(material_buildable_to(n2.d))
908 if(neighbor_rankings.find(n2.d) == NULL)
909 neighbor_rankings[n2.d] = 1;
911 neighbor_rankings[n2.d]
912 = neighbor_rankings[n2.d] + 1;
915 catch(InvalidPositionException &e)
920 u16 highest_ranking = 0;
922 for(core::map<u8, u16>::Iterator
923 i = neighbor_rankings.getIterator();
924 i.atEnd() == false; i++)
926 u8 m = i.getNode()->getKey();
927 u8 c = i.getNode()->getValue();
929 c > highest_ranking ||
930 // Prefer something else than air
931 (c >= highest_ranking && m != CONTENT_AIR)
935 replace_material = m;
944 If there is a node at top and it doesn't have sunlight,
945 there will be no sunlight going down.
948 MapNode topnode = getNode(toppos);
950 if(topnode.getLight() != LIGHT_SUN)
951 node_under_sunlight = false;
953 catch(InvalidPositionException &e)
958 Unlight neighbors (in case the node is a light source)
960 //core::list<v3s16> light_sources;
961 core::map<v3s16, bool> light_sources;
962 unLightNeighbors(p, getNode(p).getLight(),
963 light_sources, modified_blocks);
969 n.d = replace_material;
976 spreadLight(light_sources, modified_blocks);
978 // Add the block of the removed node to modified_blocks
979 v3s16 blockpos = getNodeBlockPos(p);
980 MapBlock * block = getBlockNoCreate(blockpos);
981 assert(block != NULL);
982 modified_blocks.insert(blockpos, block);
985 If the removed node was under sunlight, propagate the
986 sunlight down from it and then light all neighbors
987 of the propagated blocks.
989 if(node_under_sunlight)
991 s16 ybottom = propagateSunlight(p, modified_blocks);
992 /*m_dout<<DTIME<<"Node was under sunlight. "
993 "Propagating sunlight";
994 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
996 for(; y >= ybottom; y--)
998 v3s16 p2(p.X, y, p.Z);
999 /*m_dout<<DTIME<<"lighting neighbors of node ("
1000 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1002 lightNeighbors(p2, modified_blocks);
1007 // Set the lighting of this node to 0
1009 MapNode n = getNode(p);
1013 catch(InvalidPositionException &e)
1019 // Get the brightest neighbour node and propagate light from it
1020 v3s16 n2p = getBrightestNeighbour(p);
1022 MapNode n2 = getNode(n2p);
1023 lightNeighbors(n2p, modified_blocks);
1025 catch(InvalidPositionException &e)
1030 void Map::updateMeshes(v3s16 blockpos)
1032 assert(mapType() == MAPTYPE_CLIENT);
1035 v3s16 p = blockpos + v3s16(0,0,0);
1036 MapBlock *b = getBlockNoCreate(p);
1039 catch(InvalidPositionException &e){}
1041 v3s16 p = blockpos + v3s16(-1,0,0);
1042 MapBlock *b = getBlockNoCreate(p);
1045 catch(InvalidPositionException &e){}
1047 v3s16 p = blockpos + v3s16(0,-1,0);
1048 MapBlock *b = getBlockNoCreate(p);
1051 catch(InvalidPositionException &e){}
1053 v3s16 p = blockpos + v3s16(0,0,-1);
1054 MapBlock *b = getBlockNoCreate(p);
1057 catch(InvalidPositionException &e){}
1061 Updates usage timers
1063 void Map::timerUpdate(float dtime)
1065 JMutexAutoLock lock(m_sector_mutex);
1067 core::map<v2s16, MapSector*>::Iterator si;
1069 si = m_sectors.getIterator();
1070 for(; si.atEnd() == false; si++)
1072 MapSector *sector = si.getNode()->getValue();
1073 sector->usage_timer += dtime;
1077 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1080 Wait for caches to be removed before continuing.
1082 This disables the existence of caches while locked
1084 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1086 core::list<v2s16>::Iterator j;
1087 for(j=list.begin(); j!=list.end(); j++)
1089 MapSector *sector = m_sectors[*j];
1092 sector->deleteBlocks();
1097 If sector is in sector cache, remove it from there
1099 if(m_sector_cache == sector)
1101 m_sector_cache = NULL;
1104 Remove from map and delete
1106 m_sectors.remove(*j);
1112 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1113 core::list<v3s16> *deleted_blocks)
1115 JMutexAutoLock lock(m_sector_mutex);
1117 core::list<v2s16> sector_deletion_queue;
1118 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1119 for(; i.atEnd() == false; i++)
1121 MapSector *sector = i.getNode()->getValue();
1123 Delete sector from memory if it hasn't been used in a long time
1125 if(sector->usage_timer > timeout)
1127 sector_deletion_queue.push_back(i.getNode()->getKey());
1129 if(deleted_blocks != NULL)
1131 // Collect positions of blocks of sector
1132 MapSector *sector = i.getNode()->getValue();
1133 core::list<MapBlock*> blocks;
1134 sector->getBlocks(blocks);
1135 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1136 i != blocks.end(); i++)
1138 deleted_blocks->push_back((*i)->getPos());
1143 deleteSectors(sector_deletion_queue, only_blocks);
1144 return sector_deletion_queue.getSize();
1147 void Map::PrintInfo(std::ostream &out)
1156 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1160 m_savedir = savedir;
1161 m_map_saving_enabled = false;
1165 // If directory exists, check contents and load if possible
1166 if(fs::PathExists(m_savedir))
1168 // If directory is empty, it is safe to save into it.
1169 if(fs::GetDirListing(m_savedir).size() == 0)
1171 dstream<<DTIME<<"Server: Empty save directory is valid."
1173 m_map_saving_enabled = true;
1177 // Load master heightmap
1178 loadMasterHeightmap();
1180 // Load sector (0,0) and throw and exception on fail
1181 if(loadSectorFull(v2s16(0,0)) == false)
1182 throw LoadError("Failed to load sector (0,0)");
1184 dstream<<DTIME<<"Server: Successfully loaded master "
1185 "heightmap and sector (0,0) from "<<savedir<<
1186 ", assuming valid save directory."
1189 m_map_saving_enabled = true;
1190 // Map loaded, not creating new one
1194 // If directory doesn't exist, it is safe to save to it
1196 m_map_saving_enabled = true;
1199 catch(std::exception &e)
1201 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1202 <<", exception: "<<e.what()<<std::endl;
1203 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1204 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1207 dstream<<DTIME<<"Initializing new map."<<std::endl;
1209 // Create master heightmap
1210 ValueGenerator *maxgen =
1211 ValueGenerator::deSerialize(hmp.randmax);
1212 ValueGenerator *factorgen =
1213 ValueGenerator::deSerialize(hmp.randfactor);
1214 ValueGenerator *basegen =
1215 ValueGenerator::deSerialize(hmp.base);
1216 m_heightmap = new UnlimitedHeightmap
1217 (hmp.blocksize, maxgen, factorgen, basegen);
1219 // Set map parameters
1222 // Create zero sector
1223 emergeSector(v2s16(0,0));
1225 // Initially write whole map
1229 ServerMap::~ServerMap()
1233 if(m_map_saving_enabled)
1236 // Save only changed parts
1238 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1242 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1245 catch(std::exception &e)
1247 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1248 <<", exception: "<<e.what()<<std::endl;
1251 if(m_heightmap != NULL)
1255 MapSector * ServerMap::emergeSector(v2s16 p2d)
1257 DSTACK("%s: p2d=(%d,%d)",
1260 // Check that it doesn't exist already
1262 return getSectorNoGenerate(p2d);
1264 catch(InvalidPositionException &e)
1269 Try to load the sector from disk.
1271 if(loadSectorFull(p2d) == true)
1273 return getSectorNoGenerate(p2d);
1277 If there is no master heightmap, throw.
1279 if(m_heightmap == NULL)
1281 throw InvalidPositionException("emergeSector(): no heightmap");
1285 Do not generate over-limit
1287 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1288 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1289 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1290 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1291 throw InvalidPositionException("emergeSector(): pos. over limit");
1294 Generate sector and heightmaps
1297 // Number of heightmaps in sector in each direction
1298 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1300 // Heightmap side width
1301 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1303 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1305 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1306 " heightmaps and objects"<<std::endl;*/
1308 // Loop through sub-heightmaps
1309 for(s16 y=0; y<hm_split; y++)
1310 for(s16 x=0; x<hm_split; x++)
1312 v2s16 p_in_sector = v2s16(x,y);
1313 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1315 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1316 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1317 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1318 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1321 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1322 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1325 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1327 sector->setHeightmap(p_in_sector, hm);
1329 //TODO: Make these values configurable
1330 //hm->generateContinued(0.0, 0.0, corners);
1331 hm->generateContinued(0.5, 0.2, corners);
1332 //hm->generateContinued(1.0, 0.2, corners);
1333 //hm->generateContinued(2.0, 0.2, corners);
1343 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1344 sector->setObjects(objects);
1346 v2s16 mhm_p = p2d * hm_split;
1348 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1349 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1350 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1351 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1354 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1355 float avgslope = 0.0;
1356 avgslope += fabs(avgheight - corners[0]);
1357 avgslope += fabs(avgheight - corners[1]);
1358 avgslope += fabs(avgheight - corners[2]);
1359 avgslope += fabs(avgheight - corners[3]);
1361 avgslope /= MAP_BLOCKSIZE;
1362 //dstream<<"avgslope="<<avgslope<<std::endl;
1364 float pitness = 0.0;
1366 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1369 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1372 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1375 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1379 pitness /= MAP_BLOCKSIZE;
1380 //dstream<<"pitness="<<pitness<<std::endl;
1383 Plant some trees if there is not much slope
1386 // Avgslope is the derivative of a hill
1387 float t = avgslope * avgslope;
1388 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1391 tree_max = a / (t/0.03);
1394 u32 count = (rand()%(tree_max+1));
1395 //u32 count = tree_max;
1396 for(u32 i=0; i<count; i++)
1398 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1399 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1400 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1403 objects->insert(v3s16(x, y, z),
1404 SECTOR_OBJECT_TREE_1);
1408 Plant some bushes if sector is pit-like
1411 // Pitness usually goes at around -0.5...0.5
1413 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1415 bush_max = (pitness*a*4);
1418 u32 count = (rand()%(bush_max+1));
1419 for(u32 i=0; i<count; i++)
1421 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1422 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1423 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1426 objects->insert(v3s16(x, y, z),
1427 SECTOR_OBJECT_BUSH_1);
1431 Add ravine (randomly)
1433 if(m_params.ravines_amount != 0)
1435 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1438 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1439 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1442 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1443 objects->insert(v3s16(x, y, z),
1444 SECTOR_OBJECT_RAVINE);
1451 JMutexAutoLock lock(m_sector_mutex);
1452 m_sectors.insert(p2d, sector);
1457 MapBlock * ServerMap::emergeBlock(
1459 bool only_from_disk,
1460 core::map<v3s16, MapBlock*> &changed_blocks,
1461 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1464 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1466 p.X, p.Y, p.Z, only_from_disk);
1468 /*dstream<<"ServerMap::emergeBlock(): "
1469 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1470 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1471 v2s16 p2d(p.X, p.Z);
1474 This will create or load a sector if not found in memory.
1475 If block exists on disk, it will be loaded.
1477 NOTE: On old save formats, this will be slow, as it generates
1478 lighting on blocks for them.
1480 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1481 assert(sector->getId() == MAPSECTOR_SERVER);
1483 // Try to get a block from the sector
1484 MapBlock *block = NULL;
1485 bool not_on_disk = false;
1487 block = sector->getBlockNoCreate(block_y);
1488 if(block->isDummy() == true)
1493 catch(InvalidPositionException &e)
1499 If block was not found on disk and not going to generate a
1500 new one, make sure there is a dummy block in place.
1502 if(not_on_disk && only_from_disk)
1506 // Create dummy block
1507 block = new MapBlock(this, p, true);
1509 // Add block to sector
1510 sector->insertBlock(block);
1516 //dstream<<"Not found on disk, generating."<<std::endl;
1519 Do not generate over-limit
1521 if(blockpos_over_limit(p))
1522 throw InvalidPositionException("emergeBlock(): pos. over limit");
1527 Go on generating the block.
1529 TODO: If a dungeon gets generated so that it's side gets
1530 revealed to the outside air, the lighting should be
1535 If block doesn't exist, create one.
1536 If it exists, it is a dummy. In that case unDummify() it.
1540 block = sector->createBlankBlockNoInsert(block_y);
1544 // Remove the block so that nobody can get a half-generated one.
1545 sector->removeBlock(block);
1546 // Allocate the block to be a proper one.
1550 // Randomize a bit. This makes dungeons.
1551 /*bool low_block_is_empty = false;
1553 low_block_is_empty = true;*/
1556 //const s32 ued = 8;
1557 bool underground_emptiness[ued*ued*ued];
1558 for(s32 i=0; i<ued*ued*ued; i++)
1560 underground_emptiness[i] = ((rand() % 5) == 0);
1565 This is a messy hack to sort the emptiness a bit
1567 for(s32 j=0; j<2; j++)
1568 for(s32 y0=0; y0<ued; y0++)
1569 for(s32 z0=0; z0<ued; z0++)
1570 for(s32 x0=0; x0<ued; x0++)
1573 bool &e0 = underground_emptiness[
1574 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1575 +ued*(y0*ued/MAP_BLOCKSIZE)
1576 +(x0*ued/MAP_BLOCKSIZE)];
1579 v3s16(0,0,1), // back
1580 v3s16(1,0,0), // right
1581 v3s16(0,0,-1), // front
1582 v3s16(-1,0,0), // left
1583 /*v3s16(0,1,0), // top
1584 v3s16(0,-1,0), // bottom*/
1586 for(s32 i=0; i<4; i++)
1588 v3s16 p1 = p0 + dirs[i];
1589 if(isInArea(p1, ued) == false)
1591 bool &e1 = underground_emptiness[
1592 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1593 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1594 +(p1.X*ued/MAP_BLOCKSIZE)];
1599 v3s16(0,1,0), // top
1600 v3s16(0,-1,0), // bottom
1601 /*v3s16(0,0,1), // back
1602 v3s16(1,0,0), // right
1603 v3s16(0,0,-1), // front
1604 v3s16(-1,0,0), // left*/
1606 for(s32 i=0; i<2; i++)
1608 v3s16 p2 = p1 + dirs[i];
1611 if(isInArea(p2, ued) == false)
1613 bool &e2 = underground_emptiness[
1614 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1615 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1616 +(p2.X*ued/MAP_BLOCKSIZE)];
1631 // This is the basic material of what the visible flat ground
1633 u8 material = CONTENT_GRASS;
1635 u8 water_material = CONTENT_WATER;
1636 if(g_settings.getBool("endless_water"))
1637 water_material = CONTENT_OCEAN;
1639 s32 lowest_ground_y = 32767;
1640 s32 highest_ground_y = -32768;
1643 //sector->printHeightmaps();
1645 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1646 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1648 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1650 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1651 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1652 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1654 dstream<<"WARNING: Surface height not found in sector "
1655 "for block that is being emerged"<<std::endl;
1659 s16 surface_y = surface_y_f;
1660 //avg_ground_y += surface_y;
1661 if(surface_y < lowest_ground_y)
1662 lowest_ground_y = surface_y;
1663 if(surface_y > highest_ground_y)
1664 highest_ground_y = surface_y;
1666 s32 surface_depth = 0;
1668 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1670 //float min_slope = 0.45;
1671 //float max_slope = 0.85;
1672 float min_slope = 0.60;
1673 float max_slope = 1.20;
1674 float min_slope_depth = 5.0;
1675 float max_slope_depth = 0;
1676 if(slope < min_slope)
1677 surface_depth = min_slope_depth;
1678 else if(slope > max_slope)
1679 surface_depth = max_slope_depth;
1681 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1683 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1685 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1690 NOTE: If there are some man-made structures above the
1691 newly created block, they won't be taken into account.
1693 if(real_y > surface_y)
1694 n.setLight(LIGHT_SUN);
1700 // If node is very low
1701 /*if(real_y <= surface_y - 7)
1704 if(underground_emptiness[
1705 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1706 +ued*(y0*ued/MAP_BLOCKSIZE)
1707 +(x0*ued/MAP_BLOCKSIZE)])
1713 n.d = CONTENT_STONE;
1716 // If node is under surface level
1717 else if(real_y <= surface_y - surface_depth)
1718 n.d = CONTENT_STONE;
1720 if(real_y <= surface_y - surface_depth)
1723 if(underground_emptiness[
1724 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1725 +ued*(y0*ued/MAP_BLOCKSIZE)
1726 +(x0*ued/MAP_BLOCKSIZE)])
1732 n.d = CONTENT_STONE;
1735 // If node is at or under heightmap y
1736 else if(real_y <= surface_y)
1738 // If under water level, it's mud
1739 if(real_y < WATER_LEVEL)
1741 // Only the topmost node is grass
1742 else if(real_y <= surface_y - 1)
1744 // Else it's the main material
1748 // If node is over heightmap y
1750 // If under water level, it's water
1751 if(real_y < WATER_LEVEL)
1753 n.d = water_material;
1754 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1760 block->setNode(v3s16(x0,y0,z0), n);
1765 Calculate is_underground
1767 // Probably underground if the highest part of block is under lowest
1769 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
1770 block->setIsUnderground(is_underground);
1773 Force lighting update if some part of block is underground
1774 This is needed because of caves.
1777 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
1778 if(some_part_underground)
1779 //if(is_underground)
1781 lighting_invalidated_blocks[block->getPos()] = block;
1788 //if(is_underground)
1789 if(some_part_underground)
1791 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
1792 for(s16 i=0; i<underground_level*3; i++)
1797 (rand()%(MAP_BLOCKSIZE-2))+1,
1798 (rand()%(MAP_BLOCKSIZE-2))+1,
1799 (rand()%(MAP_BLOCKSIZE-2))+1
1805 //if(is_ground_content(block->getNode(cp).d))
1806 if(block->getNode(cp).d == CONTENT_STONE)
1808 block->setNode(cp, n);
1810 for(u16 i=0; i<26; i++)
1812 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
1813 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
1815 block->setNode(cp+g_26dirs[i], n);
1822 Create a few rats in empty blocks underground
1826 //for(u16 i=0; i<2; i++)
1829 (rand()%(MAP_BLOCKSIZE-2))+1,
1830 (rand()%(MAP_BLOCKSIZE-2))+1,
1831 (rand()%(MAP_BLOCKSIZE-2))+1
1834 // Check that the place is empty
1835 //if(!is_ground_content(block->getNode(cp).d))
1838 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1839 block->addObject(obj);
1845 Add block to sector.
1847 sector->insertBlock(block);
1850 Do some interpolation for dungeons
1855 TimeTaker timer("interpolation", g_device);
1857 MapVoxelManipulator vmanip(this);
1859 v3s16 relpos = block->getPosRelative();
1861 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1862 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1863 /*vmanip.interpolate(VoxelArea(relpos,
1864 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1866 core::map<v3s16, MapBlock*> modified_blocks;
1867 vmanip.blitBack(modified_blocks);
1868 dstream<<"blitBack modified "<<modified_blocks.size()
1869 <<" blocks"<<std::endl;
1871 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1872 for(core::map<v3s16, MapBlock*>::Iterator
1873 i = modified_blocks.getIterator();
1874 i.atEnd() == false; i++)
1876 MapBlock *block = i.getNode()->getValue();
1878 changed_blocks.insert(block->getPos(), block);
1879 //lighting_invalidated_blocks.insert(block->getPos(), block);
1889 // An y-wise container of changed blocks
1890 core::map<s16, MapBlock*> changed_blocks_sector;
1893 Check if any sector's objects can be placed now.
1896 core::map<v3s16, u8> *objects = sector->getObjects();
1897 core::list<v3s16> objects_to_remove;
1898 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1899 i.atEnd() == false; i++)
1901 v3s16 p = i.getNode()->getKey();
1903 u8 d = i.getNode()->getValue();
1905 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1910 if(d == SECTOR_OBJECT_TEST)
1912 if(sector->isValidArea(p + v3s16(0,0,0),
1913 p + v3s16(0,0,0), &changed_blocks_sector))
1916 n.d = CONTENT_TORCH;
1917 sector->setNode(p, n);
1918 objects_to_remove.push_back(p);
1921 else if(d == SECTOR_OBJECT_TREE_1)
1923 v3s16 p_min = p + v3s16(-1,0,-1);
1924 v3s16 p_max = p + v3s16(1,4,1);
1925 if(sector->isValidArea(p_min, p_max,
1926 &changed_blocks_sector))
1930 sector->setNode(p+v3s16(0,0,0), n);
1931 sector->setNode(p+v3s16(0,1,0), n);
1932 sector->setNode(p+v3s16(0,2,0), n);
1933 sector->setNode(p+v3s16(0,3,0), n);
1935 n.d = CONTENT_LEAVES;
1937 sector->setNode(p+v3s16(0,4,0), n);
1939 sector->setNode(p+v3s16(-1,4,0), n);
1940 sector->setNode(p+v3s16(1,4,0), n);
1941 sector->setNode(p+v3s16(0,4,-1), n);
1942 sector->setNode(p+v3s16(0,4,1), n);
1943 sector->setNode(p+v3s16(1,4,1), n);
1944 sector->setNode(p+v3s16(-1,4,1), n);
1945 sector->setNode(p+v3s16(-1,4,-1), n);
1946 sector->setNode(p+v3s16(1,4,-1), n);
1948 sector->setNode(p+v3s16(-1,3,0), n);
1949 sector->setNode(p+v3s16(1,3,0), n);
1950 sector->setNode(p+v3s16(0,3,-1), n);
1951 sector->setNode(p+v3s16(0,3,1), n);
1952 sector->setNode(p+v3s16(1,3,1), n);
1953 sector->setNode(p+v3s16(-1,3,1), n);
1954 sector->setNode(p+v3s16(-1,3,-1), n);
1955 sector->setNode(p+v3s16(1,3,-1), n);
1957 objects_to_remove.push_back(p);
1959 // Lighting has to be recalculated for this one.
1960 sector->getBlocksInArea(p_min, p_max,
1961 lighting_invalidated_blocks);
1964 else if(d == SECTOR_OBJECT_BUSH_1)
1966 if(sector->isValidArea(p + v3s16(0,0,0),
1967 p + v3s16(0,0,0), &changed_blocks_sector))
1970 n.d = CONTENT_LEAVES;
1971 sector->setNode(p+v3s16(0,0,0), n);
1973 objects_to_remove.push_back(p);
1976 else if(d == SECTOR_OBJECT_RAVINE)
1979 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
1980 v3s16 p_max = p + v3s16(6,6,6);
1981 if(sector->isValidArea(p_min, p_max,
1982 &changed_blocks_sector))
1985 n.d = CONTENT_STONE;
1988 s16 depth = maxdepth + (rand()%10);
1990 s16 minz = -6 - (-2);
1992 for(s16 x=-6; x<=6; x++)
1994 z += -1 + (rand()%3);
1999 for(s16 y=depth+(rand()%2); y<=6; y++)
2001 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2004 v3s16 p2 = p + v3s16(x,y,z-2);
2005 if(is_ground_content(sector->getNode(p2).d))
2006 sector->setNode(p2, n);
2009 v3s16 p2 = p + v3s16(x,y,z-1);
2010 if(is_ground_content(sector->getNode(p2).d))
2011 sector->setNode(p2, n2);
2014 v3s16 p2 = p + v3s16(x,y,z+0);
2015 if(is_ground_content(sector->getNode(p2).d))
2016 sector->setNode(p2, n2);
2019 v3s16 p2 = p + v3s16(x,y,z+1);
2020 if(is_ground_content(sector->getNode(p2).d))
2021 sector->setNode(p2, n);
2024 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2025 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2029 objects_to_remove.push_back(p);
2031 // Lighting has to be recalculated for this one.
2032 sector->getBlocksInArea(p_min, p_max,
2033 lighting_invalidated_blocks);
2038 dstream<<"ServerMap::emergeBlock(): "
2039 "Invalid heightmap object"
2044 catch(InvalidPositionException &e)
2046 dstream<<"WARNING: "<<__FUNCTION_NAME
2047 <<": while inserting object "<<(int)d
2048 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2049 <<" InvalidPositionException.what()="
2050 <<e.what()<<std::endl;
2051 // This is not too fatal and seems to happen sometimes.
2056 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2057 i != objects_to_remove.end(); i++)
2059 objects->remove(*i);
2062 for(core::map<s16, MapBlock*>::Iterator
2063 i = changed_blocks_sector.getIterator();
2064 i.atEnd() == false; i++)
2066 MapBlock *block = i.getNode()->getValue();
2068 changed_blocks.insert(block->getPos(), block);
2074 void ServerMap::createDir(std::string path)
2076 if(fs::CreateDir(path) == false)
2078 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2079 <<"\""<<path<<"\""<<std::endl;
2080 throw BaseException("ServerMap failed to create directory");
2084 std::string ServerMap::getSectorSubDir(v2s16 pos)
2087 snprintf(cc, 9, "%.4x%.4x",
2088 (unsigned int)pos.X&0xffff,
2089 (unsigned int)pos.Y&0xffff);
2091 return std::string(cc);
2094 std::string ServerMap::getSectorDir(v2s16 pos)
2096 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2099 v2s16 ServerMap::getSectorPos(std::string dirname)
2101 if(dirname.size() != 8)
2102 throw InvalidFilenameException("Invalid sector directory name");
2104 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2106 throw InvalidFilenameException("Invalid sector directory name");
2107 v2s16 pos((s16)x, (s16)y);
2111 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2113 v2s16 p2d = getSectorPos(sectordir);
2115 if(blockfile.size() != 4){
2116 throw InvalidFilenameException("Invalid block filename");
2119 int r = sscanf(blockfile.c_str(), "%4x", &y);
2121 throw InvalidFilenameException("Invalid block filename");
2122 return v3s16(p2d.X, y, p2d.Y);
2126 #define ENABLE_SECTOR_SAVING 1
2127 #define ENABLE_SECTOR_LOADING 1
2128 #define ENABLE_BLOCK_SAVING 1
2129 #define ENABLE_BLOCK_LOADING 1
2131 void ServerMap::save(bool only_changed)
2133 DSTACK(__FUNCTION_NAME);
2134 if(m_map_saving_enabled == false)
2136 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2140 if(only_changed == false)
2141 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2144 saveMasterHeightmap();
2146 u32 sector_meta_count = 0;
2147 u32 block_count = 0;
2150 JMutexAutoLock lock(m_sector_mutex);
2152 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2153 for(; i.atEnd() == false; i++)
2155 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2156 assert(sector->getId() == MAPSECTOR_SERVER);
2158 if(ENABLE_SECTOR_SAVING)
2160 if(sector->differs_from_disk || only_changed == false)
2162 saveSectorMeta(sector);
2163 sector_meta_count++;
2166 if(ENABLE_BLOCK_SAVING)
2168 core::list<MapBlock*> blocks;
2169 sector->getBlocks(blocks);
2170 core::list<MapBlock*>::Iterator j;
2171 for(j=blocks.begin(); j!=blocks.end(); j++)
2173 MapBlock *block = *j;
2174 if(block->getChangedFlag() || only_changed == false)
2185 u32 deleted_count = 0;
2186 deleted_count = deleteUnusedSectors
2187 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2190 Only print if something happened or saved whole map
2192 if(only_changed == false || sector_meta_count != 0
2193 || block_count != 0 || deleted_count != 0)
2195 dstream<<DTIME<<"ServerMap: Written: "
2196 <<sector_meta_count<<" sector metadata files, "
2197 <<block_count<<" block files, "
2198 <<deleted_count<<" sectors unloaded from memory."
2203 void ServerMap::loadAll()
2205 DSTACK(__FUNCTION_NAME);
2206 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2208 loadMasterHeightmap();
2210 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2212 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2214 JMutexAutoLock lock(m_sector_mutex);
2217 s32 printed_counter = -100000;
2218 s32 count = list.size();
2220 std::vector<fs::DirListNode>::iterator i;
2221 for(i=list.begin(); i!=list.end(); i++)
2223 if(counter > printed_counter + 10)
2225 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2226 printed_counter = counter;
2230 MapSector *sector = NULL;
2232 // We want directories
2236 sector = loadSectorMeta(i->name);
2238 catch(InvalidFilenameException &e)
2240 // This catches unknown crap in directory
2243 if(ENABLE_BLOCK_LOADING)
2245 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2246 (m_savedir+"/sectors/"+i->name);
2247 std::vector<fs::DirListNode>::iterator i2;
2248 for(i2=list2.begin(); i2!=list2.end(); i2++)
2254 loadBlock(i->name, i2->name, sector);
2256 catch(InvalidFilenameException &e)
2258 // This catches unknown crap in directory
2263 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2266 void ServerMap::saveMasterHeightmap()
2268 DSTACK(__FUNCTION_NAME);
2269 createDir(m_savedir);
2271 std::string fullpath = m_savedir + "/master_heightmap";
2272 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2273 if(o.good() == false)
2274 throw FileNotGoodException("Cannot open master heightmap");
2276 // Format used for writing
2277 u8 version = SER_FMT_VER_HIGHEST;
2280 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2282 [0] u8 serialization version
2283 [1] X master heightmap
2285 u32 fullsize = 1 + hmdata.getSize();
2286 SharedBuffer<u8> data(fullsize);
2289 memcpy(&data[1], *hmdata, hmdata.getSize());
2291 o.write((const char*)*data, fullsize);
2294 m_heightmap->serialize(o, version);
2297 void ServerMap::loadMasterHeightmap()
2299 DSTACK(__FUNCTION_NAME);
2300 std::string fullpath = m_savedir + "/master_heightmap";
2301 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2302 if(is.good() == false)
2303 throw FileNotGoodException("Cannot open master heightmap");
2305 if(m_heightmap != NULL)
2308 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2311 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2313 DSTACK(__FUNCTION_NAME);
2314 // Format used for writing
2315 u8 version = SER_FMT_VER_HIGHEST;
2317 v2s16 pos = sector->getPos();
2318 createDir(m_savedir);
2319 createDir(m_savedir+"/sectors");
2320 std::string dir = getSectorDir(pos);
2323 std::string fullpath = dir + "/heightmap";
2324 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2325 if(o.good() == false)
2326 throw FileNotGoodException("Cannot open master heightmap");
2328 sector->serialize(o, version);
2330 sector->differs_from_disk = false;
2333 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2335 DSTACK(__FUNCTION_NAME);
2337 v2s16 p2d = getSectorPos(dirname);
2338 std::string dir = m_savedir + "/sectors/" + dirname;
2340 std::string fullpath = dir + "/heightmap";
2341 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2342 if(is.good() == false)
2343 throw FileNotGoodException("Cannot open sector heightmap");
2345 ServerMapSector *sector = ServerMapSector::deSerialize
2346 (is, this, p2d, &m_hwrapper, m_sectors);
2348 sector->differs_from_disk = false;
2353 bool ServerMap::loadSectorFull(v2s16 p2d)
2355 DSTACK(__FUNCTION_NAME);
2356 std::string sectorsubdir = getSectorSubDir(p2d);
2358 MapSector *sector = NULL;
2360 JMutexAutoLock lock(m_sector_mutex);
2363 sector = loadSectorMeta(sectorsubdir);
2365 catch(InvalidFilenameException &e)
2369 catch(FileNotGoodException &e)
2373 catch(std::exception &e)
2378 if(ENABLE_BLOCK_LOADING)
2380 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2381 (m_savedir+"/sectors/"+sectorsubdir);
2382 std::vector<fs::DirListNode>::iterator i2;
2383 for(i2=list2.begin(); i2!=list2.end(); i2++)
2389 loadBlock(sectorsubdir, i2->name, sector);
2391 catch(InvalidFilenameException &e)
2393 // This catches unknown crap in directory
2401 bool ServerMap::deFlushSector(v2s16 p2d)
2403 DSTACK(__FUNCTION_NAME);
2404 // See if it already exists in memory
2406 MapSector *sector = getSectorNoGenerate(p2d);
2409 catch(InvalidPositionException &e)
2412 Try to load the sector from disk.
2414 if(loadSectorFull(p2d) == true)
2423 void ServerMap::saveBlock(MapBlock *block)
2425 DSTACK(__FUNCTION_NAME);
2427 Dummy blocks are not written
2429 if(block->isDummy())
2431 /*v3s16 p = block->getPos();
2432 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2433 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2437 // Format used for writing
2438 u8 version = SER_FMT_VER_HIGHEST;
2440 v3s16 p3d = block->getPos();
2441 v2s16 p2d(p3d.X, p3d.Z);
2442 createDir(m_savedir);
2443 createDir(m_savedir+"/sectors");
2444 std::string dir = getSectorDir(p2d);
2447 // Block file is map/sectors/xxxxxxxx/xxxx
2449 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2450 std::string fullpath = dir + "/" + cc;
2451 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2452 if(o.good() == false)
2453 throw FileNotGoodException("Cannot open block data");
2456 [0] u8 serialization version
2459 o.write((char*)&version, 1);
2461 block->serialize(o, version);
2464 Versions up from 9 have block objects.
2468 block->serializeObjects(o, version);
2471 // We just wrote it to the disk
2472 block->resetChangedFlag();
2475 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2477 DSTACK(__FUNCTION_NAME);
2481 // Block file is map/sectors/xxxxxxxx/xxxx
2482 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2483 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2484 if(is.good() == false)
2485 throw FileNotGoodException("Cannot open block file");
2487 v3s16 p3d = getBlockPos(sectordir, blockfile);
2488 v2s16 p2d(p3d.X, p3d.Z);
2490 assert(sector->getPos() == p2d);
2492 u8 version = SER_FMT_VER_INVALID;
2493 is.read((char*)&version, 1);
2495 /*u32 block_size = MapBlock::serializedLength(version);
2496 SharedBuffer<u8> data(block_size);
2497 is.read((char*)*data, block_size);*/
2499 // This will always return a sector because we're the server
2500 //MapSector *sector = emergeSector(p2d);
2502 MapBlock *block = NULL;
2503 bool created_new = false;
2505 block = sector->getBlockNoCreate(p3d.Y);
2507 catch(InvalidPositionException &e)
2509 block = sector->createBlankBlockNoInsert(p3d.Y);
2513 // deserialize block data
2514 block->deSerialize(is, version);
2517 Versions up from 9 have block objects.
2521 block->updateObjects(is, version, NULL);
2525 sector->insertBlock(block);
2528 Convert old formats to new and save
2531 // Save old format blocks in new format
2532 if(version < SER_FMT_VER_HIGHEST)
2537 // We just loaded it from the disk, so it's up-to-date.
2538 block->resetChangedFlag();
2541 catch(SerializationError &e)
2543 dstream<<"WARNING: Invalid block data on disk "
2544 "(SerializationError). Ignoring."
2549 // Gets from master heightmap
2550 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2552 assert(m_heightmap != NULL);
2560 corners[0] = m_heightmap->getGroundHeight
2561 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2562 corners[1] = m_heightmap->getGroundHeight
2563 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2564 corners[2] = m_heightmap->getGroundHeight
2565 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2566 corners[3] = m_heightmap->getGroundHeight
2567 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2570 void ServerMap::PrintInfo(std::ostream &out)
2579 ClientMap::ClientMap(
2581 scene::ISceneNode* parent,
2582 scene::ISceneManager* mgr,
2586 scene::ISceneNode(parent, mgr, id),
2592 /*m_box = core::aabbox3d<f32>(0,0,0,
2593 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2594 /*m_box = core::aabbox3d<f32>(0,0,0,
2595 map->getSizeNodes().X * BS,
2596 map->getSizeNodes().Y * BS,
2597 map->getSizeNodes().Z * BS);*/
2598 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2599 BS*1000000,BS*1000000,BS*1000000);
2601 //setPosition(v3f(BS,BS,BS));
2604 ClientMap::~ClientMap()
2606 JMutexAutoLock lock(mesh_mutex);
2615 MapSector * ClientMap::emergeSector(v2s16 p2d)
2617 DSTACK(__FUNCTION_NAME);
2618 // Check that it doesn't exist already
2620 return getSectorNoGenerate(p2d);
2622 catch(InvalidPositionException &e)
2626 // Create a sector with no heightmaps
2627 ClientMapSector *sector = new ClientMapSector(this, p2d);
2630 JMutexAutoLock lock(m_sector_mutex);
2631 m_sectors.insert(p2d, sector);
2637 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2639 DSTACK(__FUNCTION_NAME);
2640 ClientMapSector *sector = NULL;
2642 JMutexAutoLock lock(m_sector_mutex);
2644 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2648 sector = (ClientMapSector*)n->getValue();
2649 assert(sector->getId() == MAPSECTOR_CLIENT);
2653 sector = new ClientMapSector(this, p2d);
2655 JMutexAutoLock lock(m_sector_mutex);
2656 m_sectors.insert(p2d, sector);
2660 sector->deSerialize(is);
2663 void ClientMap::OnRegisterSceneNode()
2667 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2668 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2671 ISceneNode::OnRegisterSceneNode();
2674 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2676 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2677 DSTACK(__FUNCTION_NAME);
2679 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2682 Get time for measuring timeout.
2684 Measuring time is very useful for long delays when the
2685 machine is swapping a lot.
2687 int time1 = time(0);
2690 Collect all blocks that are in the view range
2692 Should not optimize more here as we want to auto-update
2693 all changed nodes in viewing range at the next step.
2696 s16 viewing_range_nodes;
2697 bool viewing_range_all;
2699 JMutexAutoLock lock(g_range_mutex);
2700 viewing_range_nodes = g_viewing_range_nodes;
2701 viewing_range_all = g_viewing_range_all;
2704 m_camera_mutex.Lock();
2705 v3f camera_position = m_camera_position;
2706 v3f camera_direction = m_camera_direction;
2707 m_camera_mutex.Unlock();
2710 Get all blocks and draw all visible ones
2713 v3s16 cam_pos_nodes(
2714 camera_position.X / BS,
2715 camera_position.Y / BS,
2716 camera_position.Z / BS);
2718 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2720 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2721 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2723 // Take a fair amount as we will be dropping more out later
2725 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2726 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2727 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2729 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2730 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2731 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2733 u32 vertex_count = 0;
2735 core::map<v2s16, MapSector*>::Iterator si;
2737 //NOTE: The sectors map should be locked but we're not doing it
2738 // because it'd cause too much delays
2740 si = m_sectors.getIterator();
2741 for(; si.atEnd() == false; si++)
2744 static int timecheck_counter = 0;
2745 timecheck_counter++;
2746 if(timecheck_counter > 50)
2748 int time2 = time(0);
2749 if(time2 > time1 + 4)
2751 dstream<<"ClientMap::renderMap(): "
2752 "Rendering takes ages, returning."
2759 MapSector *sector = si.getNode()->getValue();
2760 v2s16 sp = sector->getPos();
2762 if(viewing_range_all == false)
2764 if(sp.X < p_blocks_min.X
2765 || sp.X > p_blocks_max.X
2766 || sp.Y < p_blocks_min.Z
2767 || sp.Y > p_blocks_max.Z)
2771 core::list< MapBlock * > sectorblocks;
2772 sector->getBlocks(sectorblocks);
2778 core::list< MapBlock * >::Iterator i;
2779 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2781 MapBlock *block = *i;
2784 Compare block position to camera position, skip
2785 if not seen on display
2788 v3s16 blockpos_nodes = block->getPosRelative();
2790 // Block center position
2792 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2793 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2794 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2797 // Block position relative to camera
2798 v3f blockpos_relative = blockpos - camera_position;
2800 // Distance in camera direction (+=front, -=back)
2801 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2804 f32 d = blockpos_relative.getLength();
2806 if(viewing_range_all == false)
2808 // If block is far away, don't draw it
2809 if(d > viewing_range_nodes * BS)
2810 // This is nicer when fog is used
2811 //if((dforward+d)/2 > viewing_range_nodes * BS)
2815 // Maximum radius of a block
2816 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2818 // If block is (nearly) touching the camera, don't
2819 // bother validating further (that is, render it anyway)
2820 if(d > block_max_radius * 1.5)
2822 // Cosine of the angle between the camera direction
2823 // and the block direction (camera_direction is an unit vector)
2824 f32 cosangle = dforward / d;
2826 // Compensate for the size of the block
2827 // (as the block has to be shown even if it's a bit off FOV)
2828 // This is an estimate.
2829 cosangle += block_max_radius / dforward;
2831 // If block is not in the field of view, skip it
2832 //if(cosangle < cos(FOV_ANGLE/2))
2833 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2838 Draw the faces of the block
2842 JMutexAutoLock lock(block->mesh_mutex);
2844 // Cancel if block has no mesh
2845 if(block->mesh == NULL)
2848 u32 c = block->mesh->getMeshBufferCount();
2850 for(u32 i=0; i<c; i++)
2852 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2853 const video::SMaterial& material = buf->getMaterial();
2854 video::IMaterialRenderer* rnd =
2855 driver->getMaterialRenderer(material.MaterialType);
2856 bool transparent = (rnd && rnd->isTransparent());
2857 // Render transparent on transparent pass and likewise.
2858 if(transparent == is_transparent_pass)
2860 driver->setMaterial(buf->getMaterial());
2861 driver->drawMeshBuffer(buf);
2862 vertex_count += buf->getVertexCount();
2866 } // foreach sectorblocks
2869 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2870 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2873 void ClientMap::updateMesh()
2878 void ClientMap::PrintInfo(std::ostream &out)
2888 MapVoxelManipulator::MapVoxelManipulator(Map *map)
2893 MapVoxelManipulator::~MapVoxelManipulator()
2895 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
2900 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
2902 TimeTaker timer1("emerge", g_device, &emerge_time);
2904 // Units of these are MapBlocks
2905 v3s16 p_min = getNodeBlockPos(a.MinEdge);
2906 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
2908 VoxelArea block_area_nodes
2909 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2911 addArea(block_area_nodes);
2913 for(s32 z=p_min.Z; z<=p_max.Z; z++)
2914 for(s32 y=p_min.Y; y<=p_max.Y; y++)
2915 for(s32 x=p_min.X; x<=p_max.X; x++)
2918 core::map<v3s16, bool>::Node *n;
2919 n = m_loaded_blocks.find(p);
2923 bool block_data_inexistent = false;
2926 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
2928 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
2929 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2932 dstream<<std::endl;*/
2934 MapBlock *block = m_map->getBlockNoCreate(p);
2935 if(block->isDummy())
2936 block_data_inexistent = true;
2938 block->copyTo(*this);
2940 catch(InvalidPositionException &e)
2942 block_data_inexistent = true;
2945 if(block_data_inexistent)
2947 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
2948 // Fill with VOXELFLAG_INEXISTENT
2949 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
2950 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
2952 s32 i = m_area.index(a.MinEdge.X,y,z);
2953 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
2957 m_loaded_blocks.insert(p, true);
2960 //dstream<<"emerge done"<<std::endl;
2965 void MapVoxelManipulator::emerge(VoxelArea a)
2967 TimeTaker timer1("emerge", g_device, &emerge_time);
2969 v3s16 size = a.getExtent();
2971 VoxelArea padded = a;
2972 padded.pad(m_area.getExtent() / 4);
2975 for(s16 z=0; z<size.Z; z++)
2976 for(s16 y=0; y<size.Y; y++)
2977 for(s16 x=0; x<size.X; x++)
2980 s32 i = m_area.index(a.MinEdge + p);
2981 // Don't touch nodes that have already been loaded
2982 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
2986 TimeTaker timer1("emerge load", g_device, &emerge_load_time);
2987 MapNode n = m_map->getNode(a.MinEdge + p);
2991 catch(InvalidPositionException &e)
2993 m_flags[i] = VOXELFLAG_INEXISTENT;
3001 TODO: Add an option to only update eg. water and air nodes.
3002 This will make it interfere less with important stuff if
3005 void MapVoxelManipulator::blitBack
3006 (core::map<v3s16, MapBlock*> & modified_blocks)
3008 if(m_area.getExtent() == v3s16(0,0,0))
3011 //TimeTaker timer1("blitBack", g_device);
3014 Initialize block cache
3016 v3s16 blockpos_last;
3017 MapBlock *block = NULL;
3018 bool block_checked_in_modified = false;
3020 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3021 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3022 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3026 u8 f = m_flags[m_area.index(p)];
3027 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3030 MapNode &n = m_data[m_area.index(p)];
3032 v3s16 blockpos = getNodeBlockPos(p);
3037 if(block == NULL || blockpos != blockpos_last){
3038 block = m_map->getBlockNoCreate(blockpos);
3039 blockpos_last = blockpos;
3040 block_checked_in_modified = false;
3043 // Calculate relative position in block
3044 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3046 // Don't continue if nothing has changed here
3047 if(block->getNode(relpos) == n)
3050 //m_map->setNode(m_area.MinEdge + p, n);
3051 block->setNode(relpos, n);
3054 Make sure block is in modified_blocks
3056 if(block_checked_in_modified == false)
3058 modified_blocks[blockpos] = block;
3059 block_checked_in_modified = true;
3062 catch(InvalidPositionException &e)