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"
33 Map::Map(std::ostream &dout):
35 m_camera_position(0,0,0),
36 m_camera_direction(0,0,1),
41 m_sector_mutex.Init();
42 m_camera_mutex.Init();
43 assert(m_sector_mutex.IsInitialized());
44 assert(m_camera_mutex.IsInitialized());
46 // Get this so that the player can stay on it at first
47 //getSector(v2s16(0,0));
55 /*updater.setRun(false);
56 while(updater.IsRunning())
62 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
63 for(; i.atEnd() == false; i++)
65 MapSector *sector = i.getNode()->getValue();
70 MapSector * Map::getSectorNoGenerate(v2s16 p)
72 JMutexAutoLock lock(m_sector_mutex);
74 if(m_sector_cache != NULL && p == m_sector_cache_p){
75 MapSector * sector = m_sector_cache;
76 // Reset inactivity timer
77 sector->usage_timer = 0.0;
81 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
82 // If sector doesn't exist, throw an exception
85 throw InvalidPositionException();
88 MapSector *sector = n->getValue();
90 // Cache the last result
92 m_sector_cache = sector;
94 //MapSector * ref(sector);
96 // Reset inactivity timer
97 sector->usage_timer = 0.0;
101 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
103 v2s16 p2d(p3d.X, p3d.Z);
104 MapSector * sector = getSectorNoGenerate(p2d);
106 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
111 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
115 v2s16 p2d(p3d.X, p3d.Z);
116 MapSector * sector = getSectorNoGenerate(p2d);
117 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
120 catch(InvalidPositionException &e)
126 f32 Map::getGroundHeight(v2s16 p, bool generate)
129 v2s16 sectorpos = getNodeSectorPos(p);
130 MapSector * sref = getSectorNoGenerate(sectorpos);
131 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
132 f32 y = sref->getGroundHeight(relpos);
135 catch(InvalidPositionException &e)
137 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
141 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
143 /*m_dout<<DTIME<<"Map::setGroundHeight(("
145 <<"), "<<y<<")"<<std::endl;*/
146 v2s16 sectorpos = getNodeSectorPos(p);
147 MapSector * sref = getSectorNoGenerate(sectorpos);
148 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
149 //sref->mutex.Lock();
150 sref->setGroundHeight(relpos, y);
151 //sref->mutex.Unlock();
154 bool Map::isNodeUnderground(v3s16 p)
156 v3s16 blockpos = getNodeBlockPos(p);
158 MapBlock * block = getBlockNoCreate(blockpos);
159 return block->getIsUnderground();
161 catch(InvalidPositionException &e)
168 Goes recursively through the neighbours of the node.
170 Alters only transparent nodes.
172 If the lighting of the neighbour is lower than the lighting of
173 the node was (before changing it to 0 at the step before), the
174 lighting of the neighbour is set to 0 and then the same stuff
175 repeats for the neighbour.
177 The ending nodes of the routine are stored in light_sources.
178 This is useful when a light is removed. In such case, this
179 routine can be called for the light node and then again for
180 light_sources to re-light the area without the removed light.
182 values of from_nodes are lighting values.
184 void Map::unspreadLight(enum LightBank bank,
185 core::map<v3s16, u8> & from_nodes,
186 core::map<v3s16, bool> & light_sources,
187 core::map<v3s16, MapBlock*> & modified_blocks)
190 v3s16(0,0,1), // back
192 v3s16(1,0,0), // right
193 v3s16(0,0,-1), // front
194 v3s16(0,-1,0), // bottom
195 v3s16(-1,0,0), // left
198 if(from_nodes.size() == 0)
201 u32 blockchangecount = 0;
203 core::map<v3s16, u8> unlighted_nodes;
204 core::map<v3s16, u8>::Iterator j;
205 j = from_nodes.getIterator();
208 Initialize block cache
211 MapBlock *block = NULL;
212 // Cache this a bit, too
213 bool block_checked_in_modified = false;
215 for(; j.atEnd() == false; j++)
217 v3s16 pos = j.getNode()->getKey();
218 v3s16 blockpos = getNodeBlockPos(pos);
220 // Only fetch a new block if the block position has changed
222 if(block == NULL || blockpos != blockpos_last){
223 block = getBlockNoCreate(blockpos);
224 blockpos_last = blockpos;
226 block_checked_in_modified = false;
230 catch(InvalidPositionException &e)
238 // Calculate relative position in block
239 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
241 // Get node straight from the block
242 MapNode n = block->getNode(relpos);
244 u8 oldlight = j.getNode()->getValue();
246 // Loop through 6 neighbors
247 for(u16 i=0; i<6; i++)
249 // Get the position of the neighbor node
250 v3s16 n2pos = pos + dirs[i];
252 // Get the block where the node is located
253 v3s16 blockpos = getNodeBlockPos(n2pos);
257 // Only fetch a new block if the block position has changed
259 if(block == NULL || blockpos != blockpos_last){
260 block = getBlockNoCreate(blockpos);
261 blockpos_last = blockpos;
263 block_checked_in_modified = false;
267 catch(InvalidPositionException &e)
272 // Calculate relative position in block
273 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
274 // Get node straight from the block
275 MapNode n2 = block->getNode(relpos);
277 bool changed = false;
279 //TODO: Optimize output by optimizing light_sources?
282 If the neighbor is dimmer than what was specified
283 as oldlight (the light of the previous node)
285 if(n2.getLight(bank) < oldlight)
288 And the neighbor is transparent and it has some light
290 if(n2.light_propagates() && n2.getLight(bank) != 0)
293 Set light to 0 and add to queue
296 u8 current_light = n2.getLight(bank);
297 n2.setLight(bank, 0);
298 block->setNode(relpos, n2);
300 unlighted_nodes.insert(n2pos, current_light);
304 Remove from light_sources if it is there
305 NOTE: This doesn't happen nearly at all
307 /*if(light_sources.find(n2pos))
309 std::cout<<"Removed from light_sources"<<std::endl;
310 light_sources.remove(n2pos);
315 if(light_sources.find(n2pos) != NULL)
316 light_sources.remove(n2pos);*/
319 light_sources.insert(n2pos, true);
322 // Add to modified_blocks
323 if(changed == true && block_checked_in_modified == false)
325 // If the block is not found in modified_blocks, add.
326 if(modified_blocks.find(blockpos) == NULL)
328 modified_blocks.insert(blockpos, block);
330 block_checked_in_modified = true;
333 catch(InvalidPositionException &e)
340 /*dstream<<"unspreadLight(): Changed block "
341 <<blockchangecount<<" times"
342 <<" for "<<from_nodes.size()<<" nodes"
345 if(unlighted_nodes.size() > 0)
346 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
350 A single-node wrapper of the above
352 void Map::unLightNeighbors(enum LightBank bank,
353 v3s16 pos, u8 lightwas,
354 core::map<v3s16, bool> & light_sources,
355 core::map<v3s16, MapBlock*> & modified_blocks)
357 core::map<v3s16, u8> from_nodes;
358 from_nodes.insert(pos, lightwas);
360 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
364 Lights neighbors of from_nodes, collects all them and then
367 void Map::spreadLight(enum LightBank bank,
368 core::map<v3s16, bool> & from_nodes,
369 core::map<v3s16, MapBlock*> & modified_blocks)
371 const v3s16 dirs[6] = {
372 v3s16(0,0,1), // back
374 v3s16(1,0,0), // right
375 v3s16(0,0,-1), // front
376 v3s16(0,-1,0), // bottom
377 v3s16(-1,0,0), // left
380 if(from_nodes.size() == 0)
383 u32 blockchangecount = 0;
385 core::map<v3s16, bool> lighted_nodes;
386 core::map<v3s16, bool>::Iterator j;
387 j = from_nodes.getIterator();
390 Initialize block cache
393 MapBlock *block = NULL;
394 // Cache this a bit, too
395 bool block_checked_in_modified = false;
397 for(; j.atEnd() == false; j++)
398 //for(; j != from_nodes.end(); j++)
400 v3s16 pos = j.getNode()->getKey();
402 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
403 v3s16 blockpos = getNodeBlockPos(pos);
405 // Only fetch a new block if the block position has changed
407 if(block == NULL || blockpos != blockpos_last){
408 block = getBlockNoCreate(blockpos);
409 blockpos_last = blockpos;
411 block_checked_in_modified = false;
415 catch(InvalidPositionException &e)
423 // Calculate relative position in block
424 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
426 // Get node straight from the block
427 MapNode n = block->getNode(relpos);
429 u8 oldlight = n.getLight(bank);
430 u8 newlight = diminish_light(oldlight);
432 // Loop through 6 neighbors
433 for(u16 i=0; i<6; i++){
434 // Get the position of the neighbor node
435 v3s16 n2pos = pos + dirs[i];
437 // Get the block where the node is located
438 v3s16 blockpos = getNodeBlockPos(n2pos);
442 // Only fetch a new block if the block position has changed
444 if(block == NULL || blockpos != blockpos_last){
445 block = getBlockNoCreate(blockpos);
446 blockpos_last = blockpos;
448 block_checked_in_modified = false;
452 catch(InvalidPositionException &e)
457 // Calculate relative position in block
458 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
459 // Get node straight from the block
460 MapNode n2 = block->getNode(relpos);
462 bool changed = false;
464 If the neighbor is brighter than the current node,
465 add to list (it will light up this node on its turn)
467 if(n2.getLight(bank) > undiminish_light(oldlight))
469 lighted_nodes.insert(n2pos, true);
470 //lighted_nodes.push_back(n2pos);
474 If the neighbor is dimmer than how much light this node
475 would spread on it, add to list
477 if(n2.getLight(bank) < newlight)
479 if(n2.light_propagates())
481 n2.setLight(bank, newlight);
482 block->setNode(relpos, n2);
483 lighted_nodes.insert(n2pos, true);
484 //lighted_nodes.push_back(n2pos);
489 // Add to modified_blocks
490 if(changed == true && block_checked_in_modified == false)
492 // If the block is not found in modified_blocks, add.
493 if(modified_blocks.find(blockpos) == NULL)
495 modified_blocks.insert(blockpos, block);
497 block_checked_in_modified = true;
500 catch(InvalidPositionException &e)
507 /*dstream<<"spreadLight(): Changed block "
508 <<blockchangecount<<" times"
509 <<" for "<<from_nodes.size()<<" nodes"
512 if(lighted_nodes.size() > 0)
513 spreadLight(bank, lighted_nodes, modified_blocks);
517 A single-node source variation of the above.
519 void Map::lightNeighbors(enum LightBank bank,
521 core::map<v3s16, MapBlock*> & modified_blocks)
523 core::map<v3s16, bool> from_nodes;
524 from_nodes.insert(pos, true);
525 spreadLight(bank, from_nodes, modified_blocks);
528 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
531 v3s16(0,0,1), // back
533 v3s16(1,0,0), // right
534 v3s16(0,0,-1), // front
535 v3s16(0,-1,0), // bottom
536 v3s16(-1,0,0), // left
539 u8 brightest_light = 0;
540 v3s16 brightest_pos(0,0,0);
541 bool found_something = false;
543 // Loop through 6 neighbors
544 for(u16 i=0; i<6; i++){
545 // Get the position of the neighbor node
546 v3s16 n2pos = p + dirs[i];
551 catch(InvalidPositionException &e)
555 if(n2.getLight(bank) > brightest_light || found_something == false){
556 brightest_light = n2.getLight(bank);
557 brightest_pos = n2pos;
558 found_something = true;
562 if(found_something == false)
563 throw InvalidPositionException();
565 return brightest_pos;
569 Propagates sunlight down from a node.
570 Starting point gets sunlight.
572 Returns the lowest y value of where the sunlight went.
574 Mud is turned into grass in where the sunlight stops.
576 s16 Map::propagateSunlight(v3s16 start,
577 core::map<v3s16, MapBlock*> & modified_blocks)
582 v3s16 pos(start.X, y, start.Z);
584 v3s16 blockpos = getNodeBlockPos(pos);
587 block = getBlockNoCreate(blockpos);
589 catch(InvalidPositionException &e)
594 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
595 MapNode n = block->getNode(relpos);
597 if(n.sunlight_propagates())
599 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
600 block->setNode(relpos, n);
602 modified_blocks.insert(blockpos, block);
606 // Turn mud into grass
607 if(n.d == CONTENT_MUD)
610 block->setNode(relpos, n);
611 modified_blocks.insert(blockpos, block);
614 // Sunlight goes no further
621 void Map::updateLighting(enum LightBank bank,
622 core::map<v3s16, MapBlock*> & a_blocks,
623 core::map<v3s16, MapBlock*> & modified_blocks)
625 /*m_dout<<DTIME<<"Map::updateLighting(): "
626 <<a_blocks.getSize()<<" blocks... ";*/
630 u32 count_was = modified_blocks.size();
632 core::map<v3s16, bool> light_sources;
634 core::map<v3s16, u8> unlight_from;
636 core::map<v3s16, MapBlock*>::Iterator i;
637 i = a_blocks.getIterator();
638 for(; i.atEnd() == false; i++)
640 MapBlock *block = i.getNode()->getValue();
644 // Don't bother with dummy blocks.
648 v3s16 pos = block->getPos();
649 modified_blocks.insert(pos, block);
652 Clear all light from block
654 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
655 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
656 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
661 MapNode n = block->getNode(v3s16(x,y,z));
662 u8 oldlight = n.getLight(bank);
664 block->setNode(v3s16(x,y,z), n);
666 // Collect borders for unlighting
667 if(x==0 || x == MAP_BLOCKSIZE-1
668 || y==0 || y == MAP_BLOCKSIZE-1
669 || z==0 || z == MAP_BLOCKSIZE-1)
671 v3s16 p_map = p + v3s16(
674 MAP_BLOCKSIZE*pos.Z);
675 unlight_from.insert(p_map, oldlight);
678 catch(InvalidPositionException &e)
681 This would happen when dealing with a
685 dstream<<"updateLighting(): InvalidPositionException"
690 if(bank == LIGHTBANK_DAY)
692 bool bottom_valid = block->propagateSunlight(light_sources);
694 // If bottom is valid, we're done.
698 else if(bank == LIGHTBANK_NIGHT)
707 /*dstream<<"Bottom for sunlight-propagated block ("
708 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
711 // Else get the block below and loop to it
715 block = getBlockNoCreate(pos);
717 catch(InvalidPositionException &e)
726 //TimeTaker timer("unspreadLight");
727 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
732 u32 diff = modified_blocks.size() - count_was;
733 count_was = modified_blocks.size();
734 dstream<<"unspreadLight modified "<<diff<<std::endl;
737 // TODO: Spread light from propagated sunlight?
738 // Yes, add it to light_sources... somehow.
739 // It has to be added at somewhere above, in the loop.
741 // NOTE: This actually works fine without doing so
742 // - Find out why it works
745 //TimeTaker timer("spreadLight");
746 spreadLight(bank, light_sources, modified_blocks);
751 u32 diff = modified_blocks.size() - count_was;
752 count_was = modified_blocks.size();
753 dstream<<"spreadLight modified "<<diff<<std::endl;
756 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
759 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
760 core::map<v3s16, MapBlock*> & modified_blocks)
762 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
763 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
766 Update information about whether day and night light differ
768 for(core::map<v3s16, MapBlock*>::Iterator
769 i = modified_blocks.getIterator();
770 i.atEnd() == false; i++)
772 MapBlock *block = i.getNode()->getValue();
773 block->updateDayNightDiff();
778 This is called after changing a node from transparent to opaque.
779 The lighting value of the node should be left as-is after changing
780 other values. This sets the lighting value to 0.
782 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
783 core::map<v3s16, MapBlock*> &modified_blocks)*/
784 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
785 core::map<v3s16, MapBlock*> &modified_blocks)
788 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
789 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
792 From this node to nodes underneath:
793 If lighting is sunlight (1.0), unlight neighbours and
798 v3s16 toppos = p + v3s16(0,1,0);
799 v3s16 bottompos = p + v3s16(0,-1,0);
801 bool node_under_sunlight = true;
802 core::map<v3s16, bool> light_sources;
805 If there is a node at top and it doesn't have sunlight,
806 there has not been any sunlight going down.
808 Otherwise there probably is.
811 MapNode topnode = getNode(toppos);
813 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
814 node_under_sunlight = false;
816 catch(InvalidPositionException &e)
820 if(n.d != CONTENT_TORCH)
823 If there is grass below, change it to mud
826 MapNode bottomnode = getNode(bottompos);
828 if(bottomnode.d == CONTENT_GRASS
829 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
831 bottomnode.d = CONTENT_MUD;
832 setNode(bottompos, bottomnode);
835 catch(InvalidPositionException &e)
840 enum LightBank banks[] =
845 for(s32 i=0; i<2; i++)
847 enum LightBank bank = banks[i];
849 u8 lightwas = getNode(p).getLight(bank);
851 // Add the block of the added node to modified_blocks
852 v3s16 blockpos = getNodeBlockPos(p);
853 MapBlock * block = getBlockNoCreate(blockpos);
854 assert(block != NULL);
855 modified_blocks.insert(blockpos, block);
857 if(isValidPosition(p) == false)
860 // Unlight neighbours of node.
861 // This means setting light of all consequent dimmer nodes
863 // This also collects the nodes at the border which will spread
864 // light again into this.
865 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
873 If node is under sunlight, take all sunlighted nodes under
874 it and clear light from them and from where the light has
876 TODO: This could be optimized by mass-unlighting instead
879 if(node_under_sunlight)
883 //m_dout<<DTIME<<"y="<<y<<std::endl;
884 v3s16 n2pos(p.X, y, p.Z);
890 catch(InvalidPositionException &e)
895 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
897 //m_dout<<DTIME<<"doing"<<std::endl;
898 unLightNeighbors(LIGHTBANK_DAY,
899 n2pos, n2.getLight(LIGHTBANK_DAY),
900 light_sources, modified_blocks);
901 n2.setLight(LIGHTBANK_DAY, 0);
909 for(s32 i=0; i<2; i++)
911 enum LightBank bank = banks[i];
914 Spread light from all nodes that might be capable of doing so
915 TODO: Convert to spreadLight
917 spreadLight(bank, light_sources, modified_blocks);
921 Update information about whether day and night light differ
923 for(core::map<v3s16, MapBlock*>::Iterator
924 i = modified_blocks.getIterator();
925 i.atEnd() == false; i++)
927 MapBlock *block = i.getNode()->getValue();
928 block->updateDayNightDiff();
934 void Map::removeNodeAndUpdate(v3s16 p,
935 core::map<v3s16, MapBlock*> &modified_blocks)
938 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
939 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
941 bool node_under_sunlight = true;
943 v3s16 toppos = p + v3s16(0,1,0);
945 // Node will be replaced with this
946 u8 replace_material = CONTENT_AIR;
949 If there is a node at top and it doesn't have sunlight,
950 there will be no sunlight going down.
953 MapNode topnode = getNode(toppos);
955 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
956 node_under_sunlight = false;
958 catch(InvalidPositionException &e)
962 core::map<v3s16, bool> light_sources;
964 enum LightBank banks[] =
969 for(s32 i=0; i<2; i++)
971 enum LightBank bank = banks[i];
974 Unlight neighbors (in case the node is a light source)
976 unLightNeighbors(bank, p,
977 getNode(p).getLight(bank),
978 light_sources, modified_blocks);
983 This also clears the lighting.
987 n.d = replace_material;
990 for(s32 i=0; i<2; i++)
992 enum LightBank bank = banks[i];
997 spreadLight(bank, light_sources, modified_blocks);
1000 // Add the block of the removed node to modified_blocks
1001 v3s16 blockpos = getNodeBlockPos(p);
1002 MapBlock * block = getBlockNoCreate(blockpos);
1003 assert(block != NULL);
1004 modified_blocks.insert(blockpos, block);
1007 If the removed node was under sunlight, propagate the
1008 sunlight down from it and then light all neighbors
1009 of the propagated blocks.
1011 if(node_under_sunlight)
1013 s16 ybottom = propagateSunlight(p, modified_blocks);
1014 /*m_dout<<DTIME<<"Node was under sunlight. "
1015 "Propagating sunlight";
1016 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1018 for(; y >= ybottom; y--)
1020 v3s16 p2(p.X, y, p.Z);
1021 /*m_dout<<DTIME<<"lighting neighbors of node ("
1022 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1024 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1029 // Set the lighting of this node to 0
1030 // TODO: Is this needed? Lighting is cleared up there already.
1032 MapNode n = getNode(p);
1033 n.setLight(LIGHTBANK_DAY, 0);
1036 catch(InvalidPositionException &e)
1042 for(s32 i=0; i<2; i++)
1044 enum LightBank bank = banks[i];
1046 // Get the brightest neighbour node and propagate light from it
1047 v3s16 n2p = getBrightestNeighbour(bank, p);
1049 MapNode n2 = getNode(n2p);
1050 lightNeighbors(bank, n2p, modified_blocks);
1052 catch(InvalidPositionException &e)
1058 Update information about whether day and night light differ
1060 for(core::map<v3s16, MapBlock*>::Iterator
1061 i = modified_blocks.getIterator();
1062 i.atEnd() == false; i++)
1064 MapBlock *block = i.getNode()->getValue();
1065 block->updateDayNightDiff();
1070 void Map::expireMeshes(bool only_daynight_diffed)
1072 TimeTaker timer("expireMeshes()");
1074 core::map<v2s16, MapSector*>::Iterator si;
1075 si = m_sectors.getIterator();
1076 for(; si.atEnd() == false; si++)
1078 MapSector *sector = si.getNode()->getValue();
1080 core::list< MapBlock * > sectorblocks;
1081 sector->getBlocks(sectorblocks);
1083 core::list< MapBlock * >::Iterator i;
1084 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1086 MapBlock *block = *i;
1088 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1094 JMutexAutoLock lock(block->mesh_mutex);
1095 if(block->mesh != NULL)
1097 /*block->mesh->drop();
1098 block->mesh = NULL;*/
1099 block->setMeshExpired(true);
1106 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1108 assert(mapType() == MAPTYPE_CLIENT);
1111 v3s16 p = blockpos + v3s16(0,0,0);
1112 MapBlock *b = getBlockNoCreate(p);
1113 b->updateMesh(daynight_ratio);
1115 catch(InvalidPositionException &e){}
1118 v3s16 p = blockpos + v3s16(-1,0,0);
1119 MapBlock *b = getBlockNoCreate(p);
1120 b->updateMesh(daynight_ratio);
1122 catch(InvalidPositionException &e){}
1124 v3s16 p = blockpos + v3s16(0,-1,0);
1125 MapBlock *b = getBlockNoCreate(p);
1126 b->updateMesh(daynight_ratio);
1128 catch(InvalidPositionException &e){}
1130 v3s16 p = blockpos + v3s16(0,0,-1);
1131 MapBlock *b = getBlockNoCreate(p);
1132 b->updateMesh(daynight_ratio);
1134 catch(InvalidPositionException &e){}
1137 v3s16 p = blockpos + v3s16(1,0,0);
1138 MapBlock *b = getBlockNoCreate(p);
1139 b->updateMesh(daynight_ratio);
1141 catch(InvalidPositionException &e){}
1143 v3s16 p = blockpos + v3s16(0,1,0);
1144 MapBlock *b = getBlockNoCreate(p);
1145 b->updateMesh(daynight_ratio);
1147 catch(InvalidPositionException &e){}
1149 v3s16 p = blockpos + v3s16(0,0,1);
1150 MapBlock *b = getBlockNoCreate(p);
1151 b->updateMesh(daynight_ratio);
1153 catch(InvalidPositionException &e){}*/
1158 bool Map::dayNightDiffed(v3s16 blockpos)
1161 v3s16 p = blockpos + v3s16(0,0,0);
1162 MapBlock *b = getBlockNoCreate(p);
1163 if(b->dayNightDiffed())
1166 catch(InvalidPositionException &e){}
1169 v3s16 p = blockpos + v3s16(-1,0,0);
1170 MapBlock *b = getBlockNoCreate(p);
1171 if(b->dayNightDiffed())
1174 catch(InvalidPositionException &e){}
1176 v3s16 p = blockpos + v3s16(0,-1,0);
1177 MapBlock *b = getBlockNoCreate(p);
1178 if(b->dayNightDiffed())
1181 catch(InvalidPositionException &e){}
1183 v3s16 p = blockpos + v3s16(0,0,-1);
1184 MapBlock *b = getBlockNoCreate(p);
1185 if(b->dayNightDiffed())
1188 catch(InvalidPositionException &e){}
1191 v3s16 p = blockpos + v3s16(1,0,0);
1192 MapBlock *b = getBlockNoCreate(p);
1193 if(b->dayNightDiffed())
1196 catch(InvalidPositionException &e){}
1198 v3s16 p = blockpos + v3s16(0,1,0);
1199 MapBlock *b = getBlockNoCreate(p);
1200 if(b->dayNightDiffed())
1203 catch(InvalidPositionException &e){}
1205 v3s16 p = blockpos + v3s16(0,0,1);
1206 MapBlock *b = getBlockNoCreate(p);
1207 if(b->dayNightDiffed())
1210 catch(InvalidPositionException &e){}
1216 Updates usage timers
1218 void Map::timerUpdate(float dtime)
1220 JMutexAutoLock lock(m_sector_mutex);
1222 core::map<v2s16, MapSector*>::Iterator si;
1224 si = m_sectors.getIterator();
1225 for(; si.atEnd() == false; si++)
1227 MapSector *sector = si.getNode()->getValue();
1228 sector->usage_timer += dtime;
1232 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1235 Wait for caches to be removed before continuing.
1237 This disables the existence of caches while locked
1239 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1241 core::list<v2s16>::Iterator j;
1242 for(j=list.begin(); j!=list.end(); j++)
1244 MapSector *sector = m_sectors[*j];
1247 sector->deleteBlocks();
1252 If sector is in sector cache, remove it from there
1254 if(m_sector_cache == sector)
1256 m_sector_cache = NULL;
1259 Remove from map and delete
1261 m_sectors.remove(*j);
1267 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1268 core::list<v3s16> *deleted_blocks)
1270 JMutexAutoLock lock(m_sector_mutex);
1272 core::list<v2s16> sector_deletion_queue;
1273 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1274 for(; i.atEnd() == false; i++)
1276 MapSector *sector = i.getNode()->getValue();
1278 Delete sector from memory if it hasn't been used in a long time
1280 if(sector->usage_timer > timeout)
1282 sector_deletion_queue.push_back(i.getNode()->getKey());
1284 if(deleted_blocks != NULL)
1286 // Collect positions of blocks of sector
1287 MapSector *sector = i.getNode()->getValue();
1288 core::list<MapBlock*> blocks;
1289 sector->getBlocks(blocks);
1290 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1291 i != blocks.end(); i++)
1293 deleted_blocks->push_back((*i)->getPos());
1298 deleteSectors(sector_deletion_queue, only_blocks);
1299 return sector_deletion_queue.getSize();
1302 void Map::PrintInfo(std::ostream &out)
1311 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1315 m_savedir = savedir;
1316 m_map_saving_enabled = false;
1320 // If directory exists, check contents and load if possible
1321 if(fs::PathExists(m_savedir))
1323 // If directory is empty, it is safe to save into it.
1324 if(fs::GetDirListing(m_savedir).size() == 0)
1326 dstream<<DTIME<<"Server: Empty save directory is valid."
1328 m_map_saving_enabled = true;
1332 // Load master heightmap
1333 loadMasterHeightmap();
1335 // Load sector (0,0) and throw and exception on fail
1336 if(loadSectorFull(v2s16(0,0)) == false)
1337 throw LoadError("Failed to load sector (0,0)");
1339 dstream<<DTIME<<"Server: Successfully loaded master "
1340 "heightmap and sector (0,0) from "<<savedir<<
1341 ", assuming valid save directory."
1344 m_map_saving_enabled = true;
1345 // Map loaded, not creating new one
1349 // If directory doesn't exist, it is safe to save to it
1351 m_map_saving_enabled = true;
1354 catch(std::exception &e)
1356 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1357 <<", exception: "<<e.what()<<std::endl;
1358 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1359 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1362 dstream<<DTIME<<"Initializing new map."<<std::endl;
1364 // Create master heightmap
1365 ValueGenerator *maxgen =
1366 ValueGenerator::deSerialize(hmp.randmax);
1367 ValueGenerator *factorgen =
1368 ValueGenerator::deSerialize(hmp.randfactor);
1369 ValueGenerator *basegen =
1370 ValueGenerator::deSerialize(hmp.base);
1371 m_heightmap = new UnlimitedHeightmap
1372 (hmp.blocksize, maxgen, factorgen, basegen);
1374 // Set map parameters
1377 // Create zero sector
1378 emergeSector(v2s16(0,0));
1380 // Initially write whole map
1384 ServerMap::~ServerMap()
1388 if(m_map_saving_enabled)
1391 // Save only changed parts
1393 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1397 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1400 catch(std::exception &e)
1402 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1403 <<", exception: "<<e.what()<<std::endl;
1406 if(m_heightmap != NULL)
1410 MapSector * ServerMap::emergeSector(v2s16 p2d)
1412 DSTACK("%s: p2d=(%d,%d)",
1415 // Check that it doesn't exist already
1417 return getSectorNoGenerate(p2d);
1419 catch(InvalidPositionException &e)
1424 Try to load the sector from disk.
1426 if(loadSectorFull(p2d) == true)
1428 return getSectorNoGenerate(p2d);
1432 If there is no master heightmap, throw.
1434 if(m_heightmap == NULL)
1436 throw InvalidPositionException("emergeSector(): no heightmap");
1440 Do not generate over-limit
1442 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1443 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1444 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1445 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1446 throw InvalidPositionException("emergeSector(): pos. over limit");
1449 Generate sector and heightmaps
1452 // Number of heightmaps in sector in each direction
1453 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1455 // Heightmap side width
1456 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1458 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1460 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1461 " heightmaps and objects"<<std::endl;*/
1463 // Loop through sub-heightmaps
1464 for(s16 y=0; y<hm_split; y++)
1465 for(s16 x=0; x<hm_split; x++)
1467 v2s16 p_in_sector = v2s16(x,y);
1468 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1470 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1471 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1472 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1473 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1476 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1477 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1480 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1482 sector->setHeightmap(p_in_sector, hm);
1484 //TODO: Make these values configurable
1485 //hm->generateContinued(0.0, 0.0, corners);
1486 hm->generateContinued(0.5, 0.2, corners);
1487 //hm->generateContinued(1.0, 0.2, corners);
1488 //hm->generateContinued(2.0, 0.2, corners);
1498 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1499 sector->setObjects(objects);
1501 v2s16 mhm_p = p2d * hm_split;
1503 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1504 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1505 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1506 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1509 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1510 float avgslope = 0.0;
1511 avgslope += fabs(avgheight - corners[0]);
1512 avgslope += fabs(avgheight - corners[1]);
1513 avgslope += fabs(avgheight - corners[2]);
1514 avgslope += fabs(avgheight - corners[3]);
1516 avgslope /= MAP_BLOCKSIZE;
1517 //dstream<<"avgslope="<<avgslope<<std::endl;
1519 float pitness = 0.0;
1521 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1524 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1527 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1530 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1534 pitness /= MAP_BLOCKSIZE;
1535 //dstream<<"pitness="<<pitness<<std::endl;
1538 Plant some trees if there is not much slope
1541 // Avgslope is the derivative of a hill
1542 float t = avgslope * avgslope;
1543 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1546 tree_max = a / (t/0.03);
1549 u32 count = (myrand()%(tree_max+1));
1550 //u32 count = tree_max;
1551 for(u32 i=0; i<count; i++)
1553 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
1554 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
1555 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1558 objects->insert(v3s16(x, y, z),
1559 SECTOR_OBJECT_TREE_1);
1563 Plant some bushes if sector is pit-like
1566 // Pitness usually goes at around -0.5...0.5
1568 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1570 bush_max = (pitness*a*4);
1573 u32 count = (myrand()%(bush_max+1));
1574 for(u32 i=0; i<count; i++)
1576 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
1577 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
1578 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1581 objects->insert(v3s16(x, y, z),
1582 SECTOR_OBJECT_BUSH_1);
1586 Add ravine (randomly)
1588 if(m_params.ravines_amount != 0)
1590 if(myrand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1593 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1594 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1597 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1598 objects->insert(v3s16(x, y, z),
1599 SECTOR_OBJECT_RAVINE);
1606 JMutexAutoLock lock(m_sector_mutex);
1607 m_sectors.insert(p2d, sector);
1612 MapBlock * ServerMap::emergeBlock(
1614 bool only_from_disk,
1615 core::map<v3s16, MapBlock*> &changed_blocks,
1616 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1619 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1621 p.X, p.Y, p.Z, only_from_disk);
1623 /*dstream<<"ServerMap::emergeBlock(): "
1624 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1625 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1626 v2s16 p2d(p.X, p.Z);
1629 This will create or load a sector if not found in memory.
1630 If block exists on disk, it will be loaded.
1632 NOTE: On old save formats, this will be slow, as it generates
1633 lighting on blocks for them.
1635 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1636 assert(sector->getId() == MAPSECTOR_SERVER);
1638 // Try to get a block from the sector
1639 MapBlock *block = NULL;
1640 bool not_on_disk = false;
1642 block = sector->getBlockNoCreate(block_y);
1643 if(block->isDummy() == true)
1648 catch(InvalidPositionException &e)
1654 If block was not found on disk and not going to generate a
1655 new one, make sure there is a dummy block in place.
1657 if(not_on_disk && only_from_disk)
1661 // Create dummy block
1662 block = new MapBlock(this, p, true);
1664 // Add block to sector
1665 sector->insertBlock(block);
1671 //dstream<<"Not found on disk, generating."<<std::endl;
1672 //TimeTaker("emergeBlock()", g_irrlicht);
1675 Do not generate over-limit
1677 if(blockpos_over_limit(p))
1678 throw InvalidPositionException("emergeBlock(): pos. over limit");
1683 Go on generating the block.
1685 TODO: If a dungeon gets generated so that it's side gets
1686 revealed to the outside air, the lighting should be
1691 If block doesn't exist, create one.
1692 If it exists, it is a dummy. In that case unDummify() it.
1696 block = sector->createBlankBlockNoInsert(block_y);
1700 // Remove the block so that nobody can get a half-generated one.
1701 sector->removeBlock(block);
1702 // Allocate the block to be a proper one.
1708 Initialize dungeon making by creating a random table
1710 const s32 ued_max = 5;
1711 const s32 ued_min = 3;
1712 bool underground_emptiness[ued_max*ued_max*ued_max];
1713 s32 ued = (myrand()%(ued_max-ued_min+1))+1;
1714 //s32 ued = ued_max;
1715 for(s32 i=0; i<ued*ued*ued; i++)
1717 underground_emptiness[i] = ((myrand() % 5) == 0);
1721 This is a messy hack to sort the emptiness a bit
1723 // Iterator through a few times
1724 for(s32 j=0; j<2; j++)
1725 for(s32 y0=0; y0<ued; y0++)
1726 for(s32 z0=0; z0<ued; z0++)
1727 for(s32 x0=0; x0<ued; x0++)
1730 bool &e0 = underground_emptiness[
1731 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1732 +ued*(y0*ued/MAP_BLOCKSIZE)
1733 +(x0*ued/MAP_BLOCKSIZE)];
1736 v3s16(0,0,1), // back
1737 v3s16(1,0,0), // right
1738 v3s16(0,0,-1), // front
1739 v3s16(-1,0,0), // left
1740 /*v3s16(0,1,0), // top
1741 v3s16(0,-1,0), // bottom*/
1744 for(s32 i=0; i<4; i++)
1746 v3s16 p1 = p0 + dirs[i];
1747 if(isInArea(p1, ued) == false)
1749 bool &e1 = underground_emptiness[
1750 ued*ued*(p1.Z*ued/MAP_BLOCKSIZE)
1751 +ued*(p1.Y*ued/MAP_BLOCKSIZE)
1752 +(p1.X*ued/MAP_BLOCKSIZE)];
1757 v3s16(0,1,0), // top
1758 v3s16(0,-1,0), // bottom
1759 /*v3s16(0,0,1), // back
1760 v3s16(1,0,0), // right
1761 v3s16(0,0,-1), // front
1762 v3s16(-1,0,0), // left*/
1764 for(s32 i=0; i<2; i++)
1766 v3s16 p2 = p1 + dirs[i];
1769 if(isInArea(p2, ued) == false)
1771 bool &e2 = underground_emptiness[
1772 ued*ued*(p2.Z*ued/MAP_BLOCKSIZE)
1773 +ued*(p2.Y*ued/MAP_BLOCKSIZE)
1774 +(p2.X*ued/MAP_BLOCKSIZE)];
1790 Create dungeon making table
1792 const s32 ued = MAP_BLOCKSIZE;
1793 bool underground_emptiness[ued*ued*ued];
1794 for(s32 i=0; i<ued*ued*ued; i++)
1796 underground_emptiness[i] = 0;
1798 // Generate dungeons
1801 Initialize orp and ors. Try to find if some neighboring
1802 MapBlock has a tunnel ended in its side
1806 (float)(myrand()%ued)+0.5,
1807 (float)(myrand()%ued)+0.5,
1808 (float)(myrand()%ued)+0.5
1815 for(s16 y=0; y<ued; y++)
1816 for(s16 x=0; x<ued; x++)
1818 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1819 if(getNode(ap).d == CONTENT_AIR)
1821 orp = v3f(x+1,y+1,0);
1822 goto continue_generating;
1826 catch(InvalidPositionException &e){}
1832 for(s16 y=0; y<ued; y++)
1833 for(s16 x=0; x<ued; x++)
1835 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1836 if(getNode(ap).d == CONTENT_AIR)
1838 orp = v3f(x+1,y+1,ued-1);
1839 goto continue_generating;
1843 catch(InvalidPositionException &e){}
1849 for(s16 y=0; y<ued; y++)
1850 for(s16 z=0; z<ued; z++)
1852 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1853 if(getNode(ap).d == CONTENT_AIR)
1855 orp = v3f(0,y+1,z+1);
1856 goto continue_generating;
1860 catch(InvalidPositionException &e){}
1866 for(s16 y=0; y<ued; y++)
1867 for(s16 z=0; z<ued; z++)
1869 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1870 if(getNode(ap).d == CONTENT_AIR)
1872 orp = v3f(ued-1,y+1,z+1);
1873 goto continue_generating;
1877 catch(InvalidPositionException &e){}
1879 continue_generating:
1882 Generate some tunnel starting from orp and ors
1884 for(u16 i=0; i<3; i++)
1887 (float)(myrand()%ued)+0.5,
1888 (float)(myrand()%ued)+0.5,
1889 (float)(myrand()%ued)+0.5
1893 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
1897 for(float f=0; f<1.0; f+=0.04)
1899 v3f fp = orp + vec * f;
1900 v3s16 cp(fp.X, fp.Y, fp.Z);
1902 s16 d1 = d0 + rs - 1;
1903 for(s16 z0=d0; z0<=d1; z0++)
1905 s16 si = rs - abs(z0);
1906 for(s16 x0=-si; x0<=si-1; x0++)
1908 s16 si2 = rs - abs(x0);
1909 for(s16 y0=-si2+1; y0<=si2-1; y0++)
1915 if(isInArea(p, ued) == false)
1917 underground_emptiness[ued*ued*z + ued*y + x] = 1;
1927 u8 water_material = CONTENT_WATER;
1928 if(g_settings.getBool("endless_water"))
1929 water_material = CONTENT_OCEAN;
1931 s32 lowest_ground_y = 32767;
1932 s32 highest_ground_y = -32768;
1935 //sector->printHeightmaps();
1937 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1938 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1940 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1942 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1943 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1944 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1946 dstream<<"WARNING: Surface height not found in sector "
1947 "for block that is being emerged"<<std::endl;
1951 s16 surface_y = surface_y_f;
1952 //avg_ground_y += surface_y;
1953 if(surface_y < lowest_ground_y)
1954 lowest_ground_y = surface_y;
1955 if(surface_y > highest_ground_y)
1956 highest_ground_y = surface_y;
1958 s32 surface_depth = 0;
1960 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1962 //float min_slope = 0.45;
1963 //float max_slope = 0.85;
1964 float min_slope = 0.60;
1965 float max_slope = 1.20;
1966 float min_slope_depth = 5.0;
1967 float max_slope_depth = 0;
1968 if(slope < min_slope)
1969 surface_depth = min_slope_depth;
1970 else if(slope > max_slope)
1971 surface_depth = max_slope_depth;
1973 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1975 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1977 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1982 NOTE: If there are some man-made structures above the
1983 newly created block, they won't be taken into account.
1985 if(real_y > surface_y)
1986 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1992 // If node is over heightmap y, it's air or water
1993 if(real_y > surface_y)
1995 // If under water level, it's water
1996 if(real_y < WATER_LEVEL)
1998 n.d = water_material;
1999 n.setLight(LIGHTBANK_DAY,
2000 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2006 // Else it's ground or dungeons (air)
2010 if(underground_emptiness[
2011 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2012 +ued*(y0*ued/MAP_BLOCKSIZE)
2013 +(x0*ued/MAP_BLOCKSIZE)])
2019 // If it's surface_depth under ground, it's stone
2020 if(real_y <= surface_y - surface_depth)
2022 n.d = CONTENT_STONE;
2026 // It is mud if it is under the first ground
2027 // level or under water
2028 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2034 n.d = CONTENT_GRASS;
2037 //n.d = CONTENT_MUD;
2039 /*// If under water level, it's mud
2040 if(real_y < WATER_LEVEL)
2042 // Only the topmost node is grass
2043 else if(real_y <= surface_y - 1)
2046 n.d = CONTENT_GRASS;*/
2051 else if(real_y <= surface_y - surface_depth)
2054 if(underground_emptiness[
2055 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2056 +ued*(y0*ued/MAP_BLOCKSIZE)
2057 +(x0*ued/MAP_BLOCKSIZE)])
2063 n.d = CONTENT_STONE;
2066 // If node is at or under heightmap y
2067 else if(real_y <= surface_y)
2069 // If under water level, it's mud
2070 if(real_y < WATER_LEVEL)
2072 // Only the topmost node is grass
2073 else if(real_y <= surface_y - 1)
2075 // Else it's the main material
2080 block->setNode(v3s16(x0,y0,z0), n);
2085 Calculate is_underground
2087 // Probably underground if the highest part of block is under lowest
2089 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
2090 block->setIsUnderground(is_underground);
2093 Force lighting update if some part of block is underground
2094 This is needed because of caves.
2097 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
2098 if(some_part_underground)
2099 //if(is_underground)
2101 lighting_invalidated_blocks[block->getPos()] = block;
2108 if(some_part_underground)
2110 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2115 for(s16 i=0; i<underground_level*1; i++)
2120 (myrand()%(MAP_BLOCKSIZE-2))+1,
2121 (myrand()%(MAP_BLOCKSIZE-2))+1,
2122 (myrand()%(MAP_BLOCKSIZE-2))+1
2128 //if(is_ground_content(block->getNode(cp).d))
2129 if(block->getNode(cp).d == CONTENT_STONE)
2131 block->setNode(cp, n);
2133 for(u16 i=0; i<26; i++)
2135 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2136 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2138 block->setNode(cp+g_26dirs[i], n);
2146 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2147 u16 coal_rareness = 60 / coal_amount;
2148 if(coal_rareness == 0)
2150 if(myrand()%coal_rareness == 0)
2152 u16 a = myrand() % 16;
2153 u16 amount = coal_amount * a*a*a / 1000;
2154 for(s16 i=0; i<amount; i++)
2157 (myrand()%(MAP_BLOCKSIZE-2))+1,
2158 (myrand()%(MAP_BLOCKSIZE-2))+1,
2159 (myrand()%(MAP_BLOCKSIZE-2))+1
2163 n.d = CONTENT_COALSTONE;
2165 //dstream<<"Adding coalstone"<<std::endl;
2167 //if(is_ground_content(block->getNode(cp).d))
2168 if(block->getNode(cp).d == CONTENT_STONE)
2170 block->setNode(cp, n);
2172 for(u16 i=0; i<26; i++)
2174 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2175 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2177 block->setNode(cp+g_26dirs[i], n);
2184 Create a few rats in empty blocks underground
2188 //for(u16 i=0; i<2; i++)
2191 (myrand()%(MAP_BLOCKSIZE-2))+1,
2192 (myrand()%(MAP_BLOCKSIZE-2))+1,
2193 (myrand()%(MAP_BLOCKSIZE-2))+1
2196 // Check that the place is empty
2197 //if(!is_ground_content(block->getNode(cp).d))
2200 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2201 block->addObject(obj);
2207 Add block to sector.
2209 sector->insertBlock(block);
2215 // An y-wise container of changed blocks
2216 core::map<s16, MapBlock*> changed_blocks_sector;
2219 Check if any sector's objects can be placed now.
2222 core::map<v3s16, u8> *objects = sector->getObjects();
2223 core::list<v3s16> objects_to_remove;
2224 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2225 i.atEnd() == false; i++)
2227 v3s16 p = i.getNode()->getKey();
2229 u8 d = i.getNode()->getValue();
2231 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2236 if(d == SECTOR_OBJECT_TEST)
2238 if(sector->isValidArea(p + v3s16(0,0,0),
2239 p + v3s16(0,0,0), &changed_blocks_sector))
2242 n.d = CONTENT_TORCH;
2243 sector->setNode(p, n);
2244 objects_to_remove.push_back(p);
2247 else if(d == SECTOR_OBJECT_TREE_1)
2249 v3s16 p_min = p + v3s16(-1,0,-1);
2250 v3s16 p_max = p + v3s16(1,4,1);
2251 if(sector->isValidArea(p_min, p_max,
2252 &changed_blocks_sector))
2256 sector->setNode(p+v3s16(0,0,0), n);
2257 sector->setNode(p+v3s16(0,1,0), n);
2258 sector->setNode(p+v3s16(0,2,0), n);
2259 sector->setNode(p+v3s16(0,3,0), n);
2261 n.d = CONTENT_LEAVES;
2263 sector->setNode(p+v3s16(0,4,0), n);
2265 sector->setNode(p+v3s16(-1,4,0), n);
2266 sector->setNode(p+v3s16(1,4,0), n);
2267 sector->setNode(p+v3s16(0,4,-1), n);
2268 sector->setNode(p+v3s16(0,4,1), n);
2269 sector->setNode(p+v3s16(1,4,1), n);
2270 sector->setNode(p+v3s16(-1,4,1), n);
2271 sector->setNode(p+v3s16(-1,4,-1), n);
2272 sector->setNode(p+v3s16(1,4,-1), n);
2274 sector->setNode(p+v3s16(-1,3,0), n);
2275 sector->setNode(p+v3s16(1,3,0), n);
2276 sector->setNode(p+v3s16(0,3,-1), n);
2277 sector->setNode(p+v3s16(0,3,1), n);
2278 sector->setNode(p+v3s16(1,3,1), n);
2279 sector->setNode(p+v3s16(-1,3,1), n);
2280 sector->setNode(p+v3s16(-1,3,-1), n);
2281 sector->setNode(p+v3s16(1,3,-1), n);
2283 objects_to_remove.push_back(p);
2285 // Lighting has to be recalculated for this one.
2286 sector->getBlocksInArea(p_min, p_max,
2287 lighting_invalidated_blocks);
2290 else if(d == SECTOR_OBJECT_BUSH_1)
2292 if(sector->isValidArea(p + v3s16(0,0,0),
2293 p + v3s16(0,0,0), &changed_blocks_sector))
2296 n.d = CONTENT_LEAVES;
2297 sector->setNode(p+v3s16(0,0,0), n);
2299 objects_to_remove.push_back(p);
2302 else if(d == SECTOR_OBJECT_RAVINE)
2305 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2306 v3s16 p_max = p + v3s16(6,6,6);
2307 if(sector->isValidArea(p_min, p_max,
2308 &changed_blocks_sector))
2311 n.d = CONTENT_STONE;
2314 s16 depth = maxdepth + (myrand()%10);
2316 s16 minz = -6 - (-2);
2318 for(s16 x=-6; x<=6; x++)
2320 z += -1 + (myrand()%3);
2325 for(s16 y=depth+(myrand()%2); y<=6; y++)
2327 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2330 v3s16 p2 = p + v3s16(x,y,z-2);
2331 if(is_ground_content(sector->getNode(p2).d)
2332 && !is_mineral(sector->getNode(p2).d))
2333 sector->setNode(p2, n);
2336 v3s16 p2 = p + v3s16(x,y,z-1);
2337 if(is_ground_content(sector->getNode(p2).d)
2338 && !is_mineral(sector->getNode(p2).d))
2339 sector->setNode(p2, n2);
2342 v3s16 p2 = p + v3s16(x,y,z+0);
2343 if(is_ground_content(sector->getNode(p2).d)
2344 && !is_mineral(sector->getNode(p2).d))
2345 sector->setNode(p2, n2);
2348 v3s16 p2 = p + v3s16(x,y,z+1);
2349 if(is_ground_content(sector->getNode(p2).d)
2350 && !is_mineral(sector->getNode(p2).d))
2351 sector->setNode(p2, n);
2354 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2355 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2359 objects_to_remove.push_back(p);
2361 // Lighting has to be recalculated for this one.
2362 sector->getBlocksInArea(p_min, p_max,
2363 lighting_invalidated_blocks);
2368 dstream<<"ServerMap::emergeBlock(): "
2369 "Invalid heightmap object"
2374 catch(InvalidPositionException &e)
2376 dstream<<"WARNING: "<<__FUNCTION_NAME
2377 <<": while inserting object "<<(int)d
2378 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2379 <<" InvalidPositionException.what()="
2380 <<e.what()<<std::endl;
2381 // This is not too fatal and seems to happen sometimes.
2386 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2387 i != objects_to_remove.end(); i++)
2389 objects->remove(*i);
2392 for(core::map<s16, MapBlock*>::Iterator
2393 i = changed_blocks_sector.getIterator();
2394 i.atEnd() == false; i++)
2396 MapBlock *block = i.getNode()->getValue();
2398 changed_blocks.insert(block->getPos(), block);
2404 void ServerMap::createDir(std::string path)
2406 if(fs::CreateDir(path) == false)
2408 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2409 <<"\""<<path<<"\""<<std::endl;
2410 throw BaseException("ServerMap failed to create directory");
2414 std::string ServerMap::getSectorSubDir(v2s16 pos)
2417 snprintf(cc, 9, "%.4x%.4x",
2418 (unsigned int)pos.X&0xffff,
2419 (unsigned int)pos.Y&0xffff);
2421 return std::string(cc);
2424 std::string ServerMap::getSectorDir(v2s16 pos)
2426 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2429 v2s16 ServerMap::getSectorPos(std::string dirname)
2431 if(dirname.size() != 8)
2432 throw InvalidFilenameException("Invalid sector directory name");
2434 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2436 throw InvalidFilenameException("Invalid sector directory name");
2437 v2s16 pos((s16)x, (s16)y);
2441 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2443 v2s16 p2d = getSectorPos(sectordir);
2445 if(blockfile.size() != 4){
2446 throw InvalidFilenameException("Invalid block filename");
2449 int r = sscanf(blockfile.c_str(), "%4x", &y);
2451 throw InvalidFilenameException("Invalid block filename");
2452 return v3s16(p2d.X, y, p2d.Y);
2456 #define ENABLE_SECTOR_SAVING 1
2457 #define ENABLE_SECTOR_LOADING 1
2458 #define ENABLE_BLOCK_SAVING 1
2459 #define ENABLE_BLOCK_LOADING 1
2461 void ServerMap::save(bool only_changed)
2463 DSTACK(__FUNCTION_NAME);
2464 if(m_map_saving_enabled == false)
2466 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2470 if(only_changed == false)
2471 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2474 saveMasterHeightmap();
2476 u32 sector_meta_count = 0;
2477 u32 block_count = 0;
2480 JMutexAutoLock lock(m_sector_mutex);
2482 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2483 for(; i.atEnd() == false; i++)
2485 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2486 assert(sector->getId() == MAPSECTOR_SERVER);
2488 if(ENABLE_SECTOR_SAVING)
2490 if(sector->differs_from_disk || only_changed == false)
2492 saveSectorMeta(sector);
2493 sector_meta_count++;
2496 if(ENABLE_BLOCK_SAVING)
2498 core::list<MapBlock*> blocks;
2499 sector->getBlocks(blocks);
2500 core::list<MapBlock*>::Iterator j;
2501 for(j=blocks.begin(); j!=blocks.end(); j++)
2503 MapBlock *block = *j;
2504 if(block->getChangedFlag() || only_changed == false)
2516 Only print if something happened or saved whole map
2518 if(only_changed == false || sector_meta_count != 0
2519 || block_count != 0)
2521 dstream<<DTIME<<"ServerMap: Written: "
2522 <<sector_meta_count<<" sector metadata files, "
2523 <<block_count<<" block files"
2528 void ServerMap::loadAll()
2530 DSTACK(__FUNCTION_NAME);
2531 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2533 loadMasterHeightmap();
2535 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2537 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2539 JMutexAutoLock lock(m_sector_mutex);
2542 s32 printed_counter = -100000;
2543 s32 count = list.size();
2545 std::vector<fs::DirListNode>::iterator i;
2546 for(i=list.begin(); i!=list.end(); i++)
2548 if(counter > printed_counter + 10)
2550 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2551 printed_counter = counter;
2555 MapSector *sector = NULL;
2557 // We want directories
2561 sector = loadSectorMeta(i->name);
2563 catch(InvalidFilenameException &e)
2565 // This catches unknown crap in directory
2568 if(ENABLE_BLOCK_LOADING)
2570 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2571 (m_savedir+"/sectors/"+i->name);
2572 std::vector<fs::DirListNode>::iterator i2;
2573 for(i2=list2.begin(); i2!=list2.end(); i2++)
2579 loadBlock(i->name, i2->name, sector);
2581 catch(InvalidFilenameException &e)
2583 // This catches unknown crap in directory
2588 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2591 void ServerMap::saveMasterHeightmap()
2593 DSTACK(__FUNCTION_NAME);
2594 createDir(m_savedir);
2596 std::string fullpath = m_savedir + "/master_heightmap";
2597 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2598 if(o.good() == false)
2599 throw FileNotGoodException("Cannot open master heightmap");
2601 // Format used for writing
2602 u8 version = SER_FMT_VER_HIGHEST;
2605 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2607 [0] u8 serialization version
2608 [1] X master heightmap
2610 u32 fullsize = 1 + hmdata.getSize();
2611 SharedBuffer<u8> data(fullsize);
2614 memcpy(&data[1], *hmdata, hmdata.getSize());
2616 o.write((const char*)*data, fullsize);
2619 m_heightmap->serialize(o, version);
2622 void ServerMap::loadMasterHeightmap()
2624 DSTACK(__FUNCTION_NAME);
2625 std::string fullpath = m_savedir + "/master_heightmap";
2626 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2627 if(is.good() == false)
2628 throw FileNotGoodException("Cannot open master heightmap");
2630 if(m_heightmap != NULL)
2633 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2636 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2638 DSTACK(__FUNCTION_NAME);
2639 // Format used for writing
2640 u8 version = SER_FMT_VER_HIGHEST;
2642 v2s16 pos = sector->getPos();
2643 createDir(m_savedir);
2644 createDir(m_savedir+"/sectors");
2645 std::string dir = getSectorDir(pos);
2648 std::string fullpath = dir + "/heightmap";
2649 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2650 if(o.good() == false)
2651 throw FileNotGoodException("Cannot open master heightmap");
2653 sector->serialize(o, version);
2655 sector->differs_from_disk = false;
2658 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2660 DSTACK(__FUNCTION_NAME);
2662 v2s16 p2d = getSectorPos(dirname);
2663 std::string dir = m_savedir + "/sectors/" + dirname;
2665 std::string fullpath = dir + "/heightmap";
2666 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2667 if(is.good() == false)
2668 throw FileNotGoodException("Cannot open sector heightmap");
2670 ServerMapSector *sector = ServerMapSector::deSerialize
2671 (is, this, p2d, &m_hwrapper, m_sectors);
2673 sector->differs_from_disk = false;
2678 bool ServerMap::loadSectorFull(v2s16 p2d)
2680 DSTACK(__FUNCTION_NAME);
2681 std::string sectorsubdir = getSectorSubDir(p2d);
2683 MapSector *sector = NULL;
2685 JMutexAutoLock lock(m_sector_mutex);
2688 sector = loadSectorMeta(sectorsubdir);
2690 catch(InvalidFilenameException &e)
2694 catch(FileNotGoodException &e)
2698 catch(std::exception &e)
2703 if(ENABLE_BLOCK_LOADING)
2705 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2706 (m_savedir+"/sectors/"+sectorsubdir);
2707 std::vector<fs::DirListNode>::iterator i2;
2708 for(i2=list2.begin(); i2!=list2.end(); i2++)
2714 loadBlock(sectorsubdir, i2->name, sector);
2716 catch(InvalidFilenameException &e)
2718 // This catches unknown crap in directory
2726 bool ServerMap::deFlushSector(v2s16 p2d)
2728 DSTACK(__FUNCTION_NAME);
2729 // See if it already exists in memory
2731 MapSector *sector = getSectorNoGenerate(p2d);
2734 catch(InvalidPositionException &e)
2737 Try to load the sector from disk.
2739 if(loadSectorFull(p2d) == true)
2748 void ServerMap::saveBlock(MapBlock *block)
2750 DSTACK(__FUNCTION_NAME);
2752 Dummy blocks are not written
2754 if(block->isDummy())
2756 /*v3s16 p = block->getPos();
2757 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2758 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2762 // Format used for writing
2763 u8 version = SER_FMT_VER_HIGHEST;
2765 v3s16 p3d = block->getPos();
2766 v2s16 p2d(p3d.X, p3d.Z);
2767 createDir(m_savedir);
2768 createDir(m_savedir+"/sectors");
2769 std::string dir = getSectorDir(p2d);
2772 // Block file is map/sectors/xxxxxxxx/xxxx
2774 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2775 std::string fullpath = dir + "/" + cc;
2776 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2777 if(o.good() == false)
2778 throw FileNotGoodException("Cannot open block data");
2781 [0] u8 serialization version
2784 o.write((char*)&version, 1);
2786 block->serialize(o, version);
2789 Versions up from 9 have block objects.
2793 block->serializeObjects(o, version);
2796 // We just wrote it to the disk
2797 block->resetChangedFlag();
2800 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2802 DSTACK(__FUNCTION_NAME);
2806 // Block file is map/sectors/xxxxxxxx/xxxx
2807 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2808 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2809 if(is.good() == false)
2810 throw FileNotGoodException("Cannot open block file");
2812 v3s16 p3d = getBlockPos(sectordir, blockfile);
2813 v2s16 p2d(p3d.X, p3d.Z);
2815 assert(sector->getPos() == p2d);
2817 u8 version = SER_FMT_VER_INVALID;
2818 is.read((char*)&version, 1);
2820 /*u32 block_size = MapBlock::serializedLength(version);
2821 SharedBuffer<u8> data(block_size);
2822 is.read((char*)*data, block_size);*/
2824 // This will always return a sector because we're the server
2825 //MapSector *sector = emergeSector(p2d);
2827 MapBlock *block = NULL;
2828 bool created_new = false;
2830 block = sector->getBlockNoCreate(p3d.Y);
2832 catch(InvalidPositionException &e)
2834 block = sector->createBlankBlockNoInsert(p3d.Y);
2838 // deserialize block data
2839 block->deSerialize(is, version);
2842 Versions up from 9 have block objects.
2846 block->updateObjects(is, version, NULL, 0);
2850 sector->insertBlock(block);
2853 Convert old formats to new and save
2856 // Save old format blocks in new format
2857 if(version < SER_FMT_VER_HIGHEST)
2862 // We just loaded it from the disk, so it's up-to-date.
2863 block->resetChangedFlag();
2866 catch(SerializationError &e)
2868 dstream<<"WARNING: Invalid block data on disk "
2869 "(SerializationError). Ignoring."
2874 // Gets from master heightmap
2875 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2877 assert(m_heightmap != NULL);
2885 corners[0] = m_heightmap->getGroundHeight
2886 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2887 corners[1] = m_heightmap->getGroundHeight
2888 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2889 corners[2] = m_heightmap->getGroundHeight
2890 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2891 corners[3] = m_heightmap->getGroundHeight
2892 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2895 void ServerMap::PrintInfo(std::ostream &out)
2906 ClientMap::ClientMap(
2908 MapDrawControl &control,
2909 scene::ISceneNode* parent,
2910 scene::ISceneManager* mgr,
2914 scene::ISceneNode(parent, mgr, id),
2921 /*m_box = core::aabbox3d<f32>(0,0,0,
2922 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2923 /*m_box = core::aabbox3d<f32>(0,0,0,
2924 map->getSizeNodes().X * BS,
2925 map->getSizeNodes().Y * BS,
2926 map->getSizeNodes().Z * BS);*/
2927 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2928 BS*1000000,BS*1000000,BS*1000000);
2930 //setPosition(v3f(BS,BS,BS));
2933 ClientMap::~ClientMap()
2935 JMutexAutoLock lock(mesh_mutex);
2944 MapSector * ClientMap::emergeSector(v2s16 p2d)
2946 DSTACK(__FUNCTION_NAME);
2947 // Check that it doesn't exist already
2949 return getSectorNoGenerate(p2d);
2951 catch(InvalidPositionException &e)
2955 // Create a sector with no heightmaps
2956 ClientMapSector *sector = new ClientMapSector(this, p2d);
2959 JMutexAutoLock lock(m_sector_mutex);
2960 m_sectors.insert(p2d, sector);
2966 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2968 DSTACK(__FUNCTION_NAME);
2969 ClientMapSector *sector = NULL;
2971 JMutexAutoLock lock(m_sector_mutex);
2973 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2977 sector = (ClientMapSector*)n->getValue();
2978 assert(sector->getId() == MAPSECTOR_CLIENT);
2982 sector = new ClientMapSector(this, p2d);
2984 JMutexAutoLock lock(m_sector_mutex);
2985 m_sectors.insert(p2d, sector);
2989 sector->deSerialize(is);
2992 void ClientMap::OnRegisterSceneNode()
2996 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2997 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3000 ISceneNode::OnRegisterSceneNode();
3003 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3005 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3006 DSTACK(__FUNCTION_NAME);
3008 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3011 Get time for measuring timeout.
3013 Measuring time is very useful for long delays when the
3014 machine is swapping a lot.
3016 int time1 = time(0);
3018 u32 daynight_ratio = m_client->getDayNightRatio();
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 = m_control.wanted_range * 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 u32 blocks_would_have_drawn = 0;
3055 u32 blocks_drawn = 0;
3057 //NOTE: The sectors map should be locked but we're not doing it
3058 // because it'd cause too much delays
3060 int timecheck_counter = 0;
3061 core::map<v2s16, MapSector*>::Iterator si;
3062 si = m_sectors.getIterator();
3063 for(; si.atEnd() == false; si++)
3066 timecheck_counter++;
3067 if(timecheck_counter > 50)
3069 int time2 = time(0);
3070 if(time2 > time1 + 4)
3072 dstream<<"ClientMap::renderMap(): "
3073 "Rendering takes ages, returning."
3080 MapSector *sector = si.getNode()->getValue();
3081 v2s16 sp = sector->getPos();
3083 if(m_control.range_all == false)
3085 if(sp.X < p_blocks_min.X
3086 || sp.X > p_blocks_max.X
3087 || sp.Y < p_blocks_min.Z
3088 || sp.Y > p_blocks_max.Z)
3092 core::list< MapBlock * > sectorblocks;
3093 sector->getBlocks(sectorblocks);
3099 core::list< MapBlock * >::Iterator i;
3100 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3102 MapBlock *block = *i;
3105 Compare block position to camera position, skip
3106 if not seen on display
3109 v3s16 blockpos_nodes = block->getPosRelative();
3111 // Block center position
3113 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3114 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3115 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3118 // Block position relative to camera
3119 v3f blockpos_relative = blockpos - camera_position;
3121 // Distance in camera direction (+=front, -=back)
3122 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3125 f32 d = blockpos_relative.getLength();
3127 if(m_control.range_all == false)
3129 // If block is far away, don't draw it
3130 if(d > m_control.wanted_range * BS)
3131 // This is nicer when fog is used
3132 //if((dforward+d)/2 > m_control.wanted_range * BS)
3136 // Maximum radius of a block
3137 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3139 // If block is (nearly) touching the camera, don't
3140 // bother validating further (that is, render it anyway)
3141 if(d > block_max_radius * 1.5)
3143 // Cosine of the angle between the camera direction
3144 // and the block direction (camera_direction is an unit vector)
3145 f32 cosangle = dforward / d;
3147 // Compensate for the size of the block
3148 // (as the block has to be shown even if it's a bit off FOV)
3149 // This is an estimate.
3150 cosangle += block_max_radius / dforward;
3152 // If block is not in the field of view, skip it
3153 //if(cosangle < cos(FOV_ANGLE/2))
3154 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3159 Draw the faces of the block
3162 bool mesh_expired = false;
3165 JMutexAutoLock lock(block->mesh_mutex);
3167 mesh_expired = block->getMeshExpired();
3169 // Mesh has not been expired and there is no mesh:
3170 // block has no content
3171 if(block->mesh == NULL && mesh_expired == false)
3175 f32 faraway = BS*50;
3176 //f32 faraway = m_control.wanted_range * BS;
3179 This has to be done with the mesh_mutex unlocked
3181 if(mesh_expired && mesh_update_count < 6
3182 && (d < faraway || mesh_update_count < 3))
3183 //if(mesh_expired && mesh_update_count < 4)
3185 mesh_update_count++;
3187 // Mesh has been expired: generate new mesh
3188 //block->updateMeshes(daynight_i);
3189 block->updateMesh(daynight_ratio);
3191 mesh_expired = false;
3195 Don't draw an expired mesh that is far away
3197 /*if(mesh_expired && d >= faraway)
3200 // Instead, delete it
3201 JMutexAutoLock lock(block->mesh_mutex);
3204 block->mesh->drop();
3207 // And continue to next block
3212 JMutexAutoLock lock(block->mesh_mutex);
3214 scene::SMesh *mesh = block->mesh;
3219 blocks_would_have_drawn++;
3220 if(blocks_drawn >= m_control.wanted_max_blocks
3221 && m_control.range_all == false
3222 && d > m_control.wanted_min_range * BS)
3226 u32 c = mesh->getMeshBufferCount();
3228 for(u32 i=0; i<c; i++)
3230 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3231 const video::SMaterial& material = buf->getMaterial();
3232 video::IMaterialRenderer* rnd =
3233 driver->getMaterialRenderer(material.MaterialType);
3234 bool transparent = (rnd && rnd->isTransparent());
3235 // Render transparent on transparent pass and likewise.
3236 if(transparent == is_transparent_pass)
3238 driver->setMaterial(buf->getMaterial());
3239 driver->drawMeshBuffer(buf);
3240 vertex_count += buf->getVertexCount();
3244 } // foreach sectorblocks
3247 m_control.blocks_drawn = blocks_drawn;
3248 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3250 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3251 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3254 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3257 Add it to all blocks touching it
3260 v3s16(0,0,0), // this
3261 v3s16(0,0,1), // back
3262 v3s16(0,1,0), // top
3263 v3s16(1,0,0), // right
3264 v3s16(0,0,-1), // front
3265 v3s16(0,-1,0), // bottom
3266 v3s16(-1,0,0), // left
3268 for(u16 i=0; i<7; i++)
3270 v3s16 p2 = p + dirs[i];
3271 // Block position of neighbor (or requested) node
3272 v3s16 blockpos = getNodeBlockPos(p2);
3273 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3274 if(blockref == NULL)
3276 // Relative position of requested node
3277 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3278 blockref->setTempMod(relpos, mod);
3280 return getNodeBlockPos(p);
3282 v3s16 ClientMap::clearTempMod(v3s16 p)
3285 v3s16(0,0,0), // this
3286 v3s16(0,0,1), // back
3287 v3s16(0,1,0), // top
3288 v3s16(1,0,0), // right
3289 v3s16(0,0,-1), // front
3290 v3s16(0,-1,0), // bottom
3291 v3s16(-1,0,0), // left
3293 for(u16 i=0; i<7; i++)
3295 v3s16 p2 = p + dirs[i];
3296 // Block position of neighbor (or requested) node
3297 v3s16 blockpos = getNodeBlockPos(p2);
3298 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3299 if(blockref == NULL)
3301 // Relative position of requested node
3302 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3303 blockref->clearTempMod(relpos);
3305 return getNodeBlockPos(p);
3308 void ClientMap::PrintInfo(std::ostream &out)
3319 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3324 MapVoxelManipulator::~MapVoxelManipulator()
3326 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3331 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3333 TimeTaker timer1("emerge", &emerge_time);
3335 // Units of these are MapBlocks
3336 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3337 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3339 VoxelArea block_area_nodes
3340 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3342 addArea(block_area_nodes);
3344 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3345 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3346 for(s32 x=p_min.X; x<=p_max.X; x++)
3349 core::map<v3s16, bool>::Node *n;
3350 n = m_loaded_blocks.find(p);
3354 bool block_data_inexistent = false;
3357 TimeTaker timer1("emerge load", &emerge_load_time);
3359 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3360 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3363 dstream<<std::endl;*/
3365 MapBlock *block = m_map->getBlockNoCreate(p);
3366 if(block->isDummy())
3367 block_data_inexistent = true;
3369 block->copyTo(*this);
3371 catch(InvalidPositionException &e)
3373 block_data_inexistent = true;
3376 if(block_data_inexistent)
3378 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3379 // Fill with VOXELFLAG_INEXISTENT
3380 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3381 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3383 s32 i = m_area.index(a.MinEdge.X,y,z);
3384 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3388 m_loaded_blocks.insert(p, true);
3391 //dstream<<"emerge done"<<std::endl;
3396 void MapVoxelManipulator::emerge(VoxelArea a)
3398 TimeTaker timer1("emerge", &emerge_time);
3400 v3s16 size = a.getExtent();
3402 VoxelArea padded = a;
3403 padded.pad(m_area.getExtent() / 4);
3406 for(s16 z=0; z<size.Z; z++)
3407 for(s16 y=0; y<size.Y; y++)
3408 for(s16 x=0; x<size.X; x++)
3411 s32 i = m_area.index(a.MinEdge + p);
3412 // Don't touch nodes that have already been loaded
3413 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3417 TimeTaker timer1("emerge load", &emerge_load_time);
3418 MapNode n = m_map->getNode(a.MinEdge + p);
3422 catch(InvalidPositionException &e)
3424 m_flags[i] = VOXELFLAG_INEXISTENT;
3432 TODO: Add an option to only update eg. water and air nodes.
3433 This will make it interfere less with important stuff if
3436 void MapVoxelManipulator::blitBack
3437 (core::map<v3s16, MapBlock*> & modified_blocks)
3439 if(m_area.getExtent() == v3s16(0,0,0))
3442 //TimeTaker timer1("blitBack");
3445 Initialize block cache
3447 v3s16 blockpos_last;
3448 MapBlock *block = NULL;
3449 bool block_checked_in_modified = false;
3451 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3452 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3453 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3457 u8 f = m_flags[m_area.index(p)];
3458 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3461 MapNode &n = m_data[m_area.index(p)];
3463 v3s16 blockpos = getNodeBlockPos(p);
3468 if(block == NULL || blockpos != blockpos_last){
3469 block = m_map->getBlockNoCreate(blockpos);
3470 blockpos_last = blockpos;
3471 block_checked_in_modified = false;
3474 // Calculate relative position in block
3475 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3477 // Don't continue if nothing has changed here
3478 if(block->getNode(relpos) == n)
3481 //m_map->setNode(m_area.MinEdge + p, n);
3482 block->setNode(relpos, n);
3485 Make sure block is in modified_blocks
3487 if(block_checked_in_modified == false)
3489 modified_blocks[blockpos] = block;
3490 block_checked_in_modified = true;
3493 catch(InvalidPositionException &e)