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
1486 //hm->generateContinued(0.0, 0.0, corners);
1487 hm->generateContinued(0.25, 0.2, corners);
1488 //hm->generateContinued(0.5, 0.2, corners);
1489 //hm->generateContinued(1.0, 0.2, corners);
1490 //hm->generateContinued(2.0, 0.2, corners);
1500 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1501 sector->setObjects(objects);
1503 v2s16 mhm_p = p2d * hm_split;
1505 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1506 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1507 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1508 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1511 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1512 float avgslope = 0.0;
1513 avgslope += fabs(avgheight - corners[0]);
1514 avgslope += fabs(avgheight - corners[1]);
1515 avgslope += fabs(avgheight - corners[2]);
1516 avgslope += fabs(avgheight - corners[3]);
1518 avgslope /= MAP_BLOCKSIZE;
1519 //dstream<<"avgslope="<<avgslope<<std::endl;
1521 float pitness = 0.0;
1523 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1526 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1529 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1532 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1536 pitness /= MAP_BLOCKSIZE;
1537 //dstream<<"pitness="<<pitness<<std::endl;
1540 Plant some trees if there is not much slope
1543 // Avgslope is the derivative of a hill
1544 float t = avgslope * avgslope;
1545 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1548 tree_max = a / (t/0.03);
1551 u32 count = (myrand()%(tree_max+1));
1552 //u32 count = tree_max;
1553 for(u32 i=0; i<count; i++)
1555 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
1556 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
1557 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1560 objects->insert(v3s16(x, y, z),
1561 SECTOR_OBJECT_TREE_1);
1565 Plant some bushes if sector is pit-like
1568 // Pitness usually goes at around -0.5...0.5
1570 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1572 bush_max = (pitness*a*4);
1575 u32 count = (myrand()%(bush_max+1));
1576 for(u32 i=0; i<count; i++)
1578 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
1579 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
1580 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1583 objects->insert(v3s16(x, y, z),
1584 SECTOR_OBJECT_BUSH_1);
1588 Add ravine (randomly)
1590 if(m_params.ravines_amount != 0)
1592 if(myrand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1595 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1596 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1599 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1600 objects->insert(v3s16(x, y, z),
1601 SECTOR_OBJECT_RAVINE);
1608 JMutexAutoLock lock(m_sector_mutex);
1609 m_sectors.insert(p2d, sector);
1614 MapBlock * ServerMap::emergeBlock(
1616 bool only_from_disk,
1617 core::map<v3s16, MapBlock*> &changed_blocks,
1618 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1621 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1623 p.X, p.Y, p.Z, only_from_disk);
1625 /*dstream<<"ServerMap::emergeBlock(): "
1626 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1627 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1628 v2s16 p2d(p.X, p.Z);
1631 This will create or load a sector if not found in memory.
1632 If block exists on disk, it will be loaded.
1634 NOTE: On old save formats, this will be slow, as it generates
1635 lighting on blocks for them.
1637 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1638 assert(sector->getId() == MAPSECTOR_SERVER);
1640 // Try to get a block from the sector
1641 MapBlock *block = NULL;
1642 bool not_on_disk = false;
1644 block = sector->getBlockNoCreate(block_y);
1645 if(block->isDummy() == true)
1650 catch(InvalidPositionException &e)
1656 If block was not found on disk and not going to generate a
1657 new one, make sure there is a dummy block in place.
1659 if(not_on_disk && only_from_disk)
1663 // Create dummy block
1664 block = new MapBlock(this, p, true);
1666 // Add block to sector
1667 sector->insertBlock(block);
1673 //dstream<<"Not found on disk, generating."<<std::endl;
1674 //TimeTaker("emergeBlock()", g_irrlicht);
1677 Do not generate over-limit
1679 if(blockpos_over_limit(p))
1680 throw InvalidPositionException("emergeBlock(): pos. over limit");
1685 Go on generating the block.
1687 TODO: If a dungeon gets generated so that it's side gets
1688 revealed to the outside air, the lighting should be
1693 If block doesn't exist, create one.
1694 If it exists, it is a dummy. In that case unDummify() it.
1698 block = sector->createBlankBlockNoInsert(block_y);
1702 // Remove the block so that nobody can get a half-generated one.
1703 sector->removeBlock(block);
1704 // Allocate the block to be a proper one.
1709 Create dungeon making table
1711 const s32 ued = MAP_BLOCKSIZE;
1712 bool underground_emptiness[ued*ued*ued];
1713 for(s32 i=0; i<ued*ued*ued; i++)
1715 underground_emptiness[i] = 0;
1718 // Generate dungeons
1721 Initialize orp and ors. Try to find if some neighboring
1722 MapBlock has a tunnel ended in its side
1726 (float)(myrand()%ued)+0.5,
1727 (float)(myrand()%ued)+0.5,
1728 (float)(myrand()%ued)+0.5
1731 bool found_existing = false;
1737 for(s16 y=0; y<ued; y++)
1738 for(s16 x=0; x<ued; x++)
1740 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1741 if(getNode(ap).d == CONTENT_AIR)
1743 orp = v3f(x+1,y+1,0);
1744 found_existing = true;
1745 goto continue_generating;
1749 catch(InvalidPositionException &e){}
1755 for(s16 y=0; y<ued; y++)
1756 for(s16 x=0; x<ued; x++)
1758 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1759 if(getNode(ap).d == CONTENT_AIR)
1761 orp = v3f(x+1,y+1,ued-1);
1762 found_existing = true;
1763 goto continue_generating;
1767 catch(InvalidPositionException &e){}
1773 for(s16 y=0; y<ued; y++)
1774 for(s16 z=0; z<ued; z++)
1776 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1777 if(getNode(ap).d == CONTENT_AIR)
1779 orp = v3f(0,y+1,z+1);
1780 found_existing = true;
1781 goto continue_generating;
1785 catch(InvalidPositionException &e){}
1791 for(s16 y=0; y<ued; y++)
1792 for(s16 z=0; z<ued; z++)
1794 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1795 if(getNode(ap).d == CONTENT_AIR)
1797 orp = v3f(ued-1,y+1,z+1);
1798 found_existing = true;
1799 goto continue_generating;
1803 catch(InvalidPositionException &e){}
1805 continue_generating:
1808 Don't always generate dungeon
1810 if(found_existing || rand() % 2 == 0)
1813 Generate some tunnel starting from orp and ors
1815 for(u16 i=0; i<3; i++)
1818 (float)(myrand()%ued)+0.5,
1819 (float)(myrand()%ued)+0.5,
1820 (float)(myrand()%ued)+0.5
1824 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
1828 for(float f=0; f<1.0; f+=0.04)
1830 v3f fp = orp + vec * f;
1831 v3s16 cp(fp.X, fp.Y, fp.Z);
1833 s16 d1 = d0 + rs - 1;
1834 for(s16 z0=d0; z0<=d1; z0++)
1836 s16 si = rs - abs(z0);
1837 for(s16 x0=-si; x0<=si-1; x0++)
1839 s16 si2 = rs - abs(x0);
1840 for(s16 y0=-si2+1; y0<=si2-1; y0++)
1846 if(isInArea(p, ued) == false)
1848 underground_emptiness[ued*ued*z + ued*y + x] = 1;
1859 u8 water_material = CONTENT_WATER;
1860 if(g_settings.getBool("endless_water"))
1861 water_material = CONTENT_OCEAN;
1863 s32 lowest_ground_y = 32767;
1864 s32 highest_ground_y = -32768;
1867 //sector->printHeightmaps();
1869 // Set to true if has caves.
1870 // Set when some non-air is changed to air when making caves.
1871 bool has_caves = false;
1873 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1874 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1876 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1878 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1879 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1880 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1882 dstream<<"WARNING: Surface height not found in sector "
1883 "for block that is being emerged"<<std::endl;
1887 s16 surface_y = surface_y_f;
1888 //avg_ground_y += surface_y;
1889 if(surface_y < lowest_ground_y)
1890 lowest_ground_y = surface_y;
1891 if(surface_y > highest_ground_y)
1892 highest_ground_y = surface_y;
1894 s32 surface_depth = 0;
1896 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1898 //float min_slope = 0.45;
1899 //float max_slope = 0.85;
1900 float min_slope = 0.60;
1901 float max_slope = 1.20;
1902 float min_slope_depth = 5.0;
1903 float max_slope_depth = 0;
1905 if(slope < min_slope)
1906 surface_depth = min_slope_depth;
1907 else if(slope > max_slope)
1908 surface_depth = max_slope_depth;
1910 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1912 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1914 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1919 NOTE: If there are some man-made structures above the
1920 newly created block, they won't be taken into account.
1922 if(real_y > surface_y)
1923 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1929 // If node is over heightmap y, it's air or water
1930 if(real_y > surface_y)
1932 // If under water level, it's water
1933 if(real_y < WATER_LEVEL)
1935 n.d = water_material;
1936 n.setLight(LIGHTBANK_DAY,
1937 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1943 // Else it's ground or dungeons (air)
1946 // If it's surface_depth under ground, it's stone
1947 if(real_y <= surface_y - surface_depth)
1949 n.d = CONTENT_STONE;
1953 // It is mud if it is under the first ground
1954 // level or under water
1955 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
1961 n.d = CONTENT_GRASS;
1964 //n.d = CONTENT_MUD;
1966 /*// If under water level, it's mud
1967 if(real_y < WATER_LEVEL)
1969 // Only the topmost node is grass
1970 else if(real_y <= surface_y - 1)
1973 n.d = CONTENT_GRASS;*/
1977 if(underground_emptiness[
1978 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1979 +ued*(y0*ued/MAP_BLOCKSIZE)
1980 +(x0*ued/MAP_BLOCKSIZE)])
1982 // Has now caves if previous content is air
1983 if(n.d != CONTENT_AIR)
1992 block->setNode(v3s16(x0,y0,z0), n);
1997 Calculate completely_underground
1999 // Completely underground if the highest part of block is under lowest
2001 // This has to be very sure; it's probably one too strict now but
2002 // that's just better.
2003 bool completely_underground =
2004 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2006 // This isn't used anymore (?) but set it anyway
2007 block->setIsUnderground(completely_underground);
2009 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2012 Force lighting update if some part of block is partly
2013 underground and has caves.
2016 if(some_part_underground && !completely_underground && has_caves)
2018 //dstream<<"Half-ground caves"<<std::endl;
2019 lighting_invalidated_blocks[block->getPos()] = block;
2022 // DEBUG: Always update lighting
2023 //lighting_invalidated_blocks[block->getPos()] = block;
2029 if(some_part_underground)
2031 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2036 for(s16 i=0; i< underground_level/4 + 1; i++)
2038 if(myrand()%10 == 0)
2041 (myrand()%(MAP_BLOCKSIZE-2))+1,
2042 (myrand()%(MAP_BLOCKSIZE-2))+1,
2043 (myrand()%(MAP_BLOCKSIZE-2))+1
2049 //if(is_ground_content(block->getNode(cp).d))
2050 if(block->getNode(cp).d == CONTENT_STONE)
2052 block->setNode(cp, n);
2054 for(u16 i=0; i<26; i++)
2056 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2057 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2059 block->setNode(cp+g_26dirs[i], n);
2067 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2068 u16 coal_rareness = 60 / coal_amount;
2069 if(coal_rareness == 0)
2071 if(myrand()%coal_rareness == 0)
2073 u16 a = myrand() % 16;
2074 u16 amount = coal_amount * a*a*a / 1000;
2075 for(s16 i=0; i<amount; i++)
2078 (myrand()%(MAP_BLOCKSIZE-2))+1,
2079 (myrand()%(MAP_BLOCKSIZE-2))+1,
2080 (myrand()%(MAP_BLOCKSIZE-2))+1
2084 n.d = CONTENT_COALSTONE;
2086 //dstream<<"Adding coalstone"<<std::endl;
2088 //if(is_ground_content(block->getNode(cp).d))
2089 if(block->getNode(cp).d == CONTENT_STONE)
2091 block->setNode(cp, n);
2093 for(u16 i=0; i<26; i++)
2095 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2096 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2098 block->setNode(cp+g_26dirs[i], n);
2105 Create a few rats in empty blocks underground
2107 if(completely_underground)
2109 //for(u16 i=0; i<2; i++)
2112 (myrand()%(MAP_BLOCKSIZE-2))+1,
2113 (myrand()%(MAP_BLOCKSIZE-2))+1,
2114 (myrand()%(MAP_BLOCKSIZE-2))+1
2117 // Check that the place is empty
2118 //if(!is_ground_content(block->getNode(cp).d))
2121 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2122 block->addObject(obj);
2128 Add block to sector.
2130 sector->insertBlock(block);
2136 // An y-wise container of changed blocks
2137 core::map<s16, MapBlock*> changed_blocks_sector;
2140 Check if any sector's objects can be placed now.
2143 core::map<v3s16, u8> *objects = sector->getObjects();
2144 core::list<v3s16> objects_to_remove;
2145 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2146 i.atEnd() == false; i++)
2148 v3s16 p = i.getNode()->getKey();
2150 u8 d = i.getNode()->getValue();
2152 // Ground level point (user for stuff that is on ground)
2154 bool ground_found = true;
2156 // Search real ground level
2160 MapNode n = sector->getNode(gp);
2162 // If not air, go one up and continue to placing the tree
2163 if(n.d != CONTENT_AIR)
2169 // If air, go one down
2170 gp += v3s16(0,-1,0);
2172 }catch(InvalidPositionException &e)
2174 // Ground not found.
2175 ground_found = false;
2176 // This is most close to ground
2183 if(d == SECTOR_OBJECT_TEST)
2185 if(sector->isValidArea(p + v3s16(0,0,0),
2186 p + v3s16(0,0,0), &changed_blocks_sector))
2189 n.d = CONTENT_TORCH;
2190 sector->setNode(p, n);
2191 objects_to_remove.push_back(p);
2194 else if(d == SECTOR_OBJECT_TREE_1)
2196 if(ground_found == false)
2199 v3s16 p_min = gp + v3s16(-1,0,-1);
2200 v3s16 p_max = gp + v3s16(1,5,1);
2201 if(sector->isValidArea(p_min, p_max,
2202 &changed_blocks_sector))
2206 sector->setNode(gp+v3s16(0,0,0), n);
2207 sector->setNode(gp+v3s16(0,1,0), n);
2208 sector->setNode(gp+v3s16(0,2,0), n);
2209 sector->setNode(gp+v3s16(0,3,0), n);
2211 n.d = CONTENT_LEAVES;
2213 if(rand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2215 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2216 if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2217 if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2218 if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2219 /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2220 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2221 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2222 if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2224 sector->setNode(gp+v3s16(0,4,0), n);
2226 sector->setNode(gp+v3s16(-1,4,0), n);
2227 sector->setNode(gp+v3s16(1,4,0), n);
2228 sector->setNode(gp+v3s16(0,4,-1), n);
2229 sector->setNode(gp+v3s16(0,4,1), n);
2230 sector->setNode(gp+v3s16(1,4,1), n);
2231 sector->setNode(gp+v3s16(-1,4,1), n);
2232 sector->setNode(gp+v3s16(-1,4,-1), n);
2233 sector->setNode(gp+v3s16(1,4,-1), n);
2235 sector->setNode(gp+v3s16(-1,3,0), n);
2236 sector->setNode(gp+v3s16(1,3,0), n);
2237 sector->setNode(gp+v3s16(0,3,-1), n);
2238 sector->setNode(gp+v3s16(0,3,1), n);
2239 sector->setNode(gp+v3s16(1,3,1), n);
2240 sector->setNode(gp+v3s16(-1,3,1), n);
2241 sector->setNode(gp+v3s16(-1,3,-1), n);
2242 sector->setNode(gp+v3s16(1,3,-1), n);
2244 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2245 if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2246 if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2247 if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2248 /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2249 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2250 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2251 if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2253 // Objects are identified by wanted position
2254 objects_to_remove.push_back(p);
2256 // Lighting has to be recalculated for this one.
2257 sector->getBlocksInArea(p_min, p_max,
2258 lighting_invalidated_blocks);
2261 else if(d == SECTOR_OBJECT_BUSH_1)
2263 if(ground_found == false)
2266 if(sector->isValidArea(gp + v3s16(0,0,0),
2267 gp + v3s16(0,0,0), &changed_blocks_sector))
2270 n.d = CONTENT_LEAVES;
2271 sector->setNode(gp+v3s16(0,0,0), n);
2273 // Objects are identified by wanted position
2274 objects_to_remove.push_back(p);
2277 else if(d == SECTOR_OBJECT_RAVINE)
2280 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2281 v3s16 p_max = p + v3s16(6,6,6);
2282 if(sector->isValidArea(p_min, p_max,
2283 &changed_blocks_sector))
2286 n.d = CONTENT_STONE;
2289 s16 depth = maxdepth + (myrand()%10);
2291 s16 minz = -6 - (-2);
2293 for(s16 x=-6; x<=6; x++)
2295 z += -1 + (myrand()%3);
2300 for(s16 y=depth+(myrand()%2); y<=6; y++)
2302 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2305 v3s16 p2 = p + v3s16(x,y,z-2);
2306 if(is_ground_content(sector->getNode(p2).d)
2307 && !is_mineral(sector->getNode(p2).d))
2308 sector->setNode(p2, n);
2311 v3s16 p2 = p + v3s16(x,y,z-1);
2312 if(is_ground_content(sector->getNode(p2).d)
2313 && !is_mineral(sector->getNode(p2).d))
2314 sector->setNode(p2, n2);
2317 v3s16 p2 = p + v3s16(x,y,z+0);
2318 if(is_ground_content(sector->getNode(p2).d)
2319 && !is_mineral(sector->getNode(p2).d))
2320 sector->setNode(p2, n2);
2323 v3s16 p2 = p + v3s16(x,y,z+1);
2324 if(is_ground_content(sector->getNode(p2).d)
2325 && !is_mineral(sector->getNode(p2).d))
2326 sector->setNode(p2, n);
2329 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2330 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2334 objects_to_remove.push_back(p);
2336 // Lighting has to be recalculated for this one.
2337 sector->getBlocksInArea(p_min, p_max,
2338 lighting_invalidated_blocks);
2343 dstream<<"ServerMap::emergeBlock(): "
2344 "Invalid heightmap object"
2349 catch(InvalidPositionException &e)
2351 dstream<<"WARNING: "<<__FUNCTION_NAME
2352 <<": while inserting object "<<(int)d
2353 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2354 <<" InvalidPositionException.what()="
2355 <<e.what()<<std::endl;
2356 // This is not too fatal and seems to happen sometimes.
2361 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2362 i != objects_to_remove.end(); i++)
2364 objects->remove(*i);
2367 for(core::map<s16, MapBlock*>::Iterator
2368 i = changed_blocks_sector.getIterator();
2369 i.atEnd() == false; i++)
2371 MapBlock *block = i.getNode()->getValue();
2373 changed_blocks.insert(block->getPos(), block);
2379 void ServerMap::createDir(std::string path)
2381 if(fs::CreateDir(path) == false)
2383 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2384 <<"\""<<path<<"\""<<std::endl;
2385 throw BaseException("ServerMap failed to create directory");
2389 std::string ServerMap::getSectorSubDir(v2s16 pos)
2392 snprintf(cc, 9, "%.4x%.4x",
2393 (unsigned int)pos.X&0xffff,
2394 (unsigned int)pos.Y&0xffff);
2396 return std::string(cc);
2399 std::string ServerMap::getSectorDir(v2s16 pos)
2401 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2404 v2s16 ServerMap::getSectorPos(std::string dirname)
2406 if(dirname.size() != 8)
2407 throw InvalidFilenameException("Invalid sector directory name");
2409 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2411 throw InvalidFilenameException("Invalid sector directory name");
2412 v2s16 pos((s16)x, (s16)y);
2416 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2418 v2s16 p2d = getSectorPos(sectordir);
2420 if(blockfile.size() != 4){
2421 throw InvalidFilenameException("Invalid block filename");
2424 int r = sscanf(blockfile.c_str(), "%4x", &y);
2426 throw InvalidFilenameException("Invalid block filename");
2427 return v3s16(p2d.X, y, p2d.Y);
2431 #define ENABLE_SECTOR_SAVING 1
2432 #define ENABLE_SECTOR_LOADING 1
2433 #define ENABLE_BLOCK_SAVING 1
2434 #define ENABLE_BLOCK_LOADING 1
2436 void ServerMap::save(bool only_changed)
2438 DSTACK(__FUNCTION_NAME);
2439 if(m_map_saving_enabled == false)
2441 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2445 if(only_changed == false)
2446 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2449 saveMasterHeightmap();
2451 u32 sector_meta_count = 0;
2452 u32 block_count = 0;
2455 JMutexAutoLock lock(m_sector_mutex);
2457 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2458 for(; i.atEnd() == false; i++)
2460 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2461 assert(sector->getId() == MAPSECTOR_SERVER);
2463 if(ENABLE_SECTOR_SAVING)
2465 if(sector->differs_from_disk || only_changed == false)
2467 saveSectorMeta(sector);
2468 sector_meta_count++;
2471 if(ENABLE_BLOCK_SAVING)
2473 core::list<MapBlock*> blocks;
2474 sector->getBlocks(blocks);
2475 core::list<MapBlock*>::Iterator j;
2476 for(j=blocks.begin(); j!=blocks.end(); j++)
2478 MapBlock *block = *j;
2479 if(block->getChangedFlag() || only_changed == false)
2491 Only print if something happened or saved whole map
2493 if(only_changed == false || sector_meta_count != 0
2494 || block_count != 0)
2496 dstream<<DTIME<<"ServerMap: Written: "
2497 <<sector_meta_count<<" sector metadata files, "
2498 <<block_count<<" block files"
2503 void ServerMap::loadAll()
2505 DSTACK(__FUNCTION_NAME);
2506 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2508 loadMasterHeightmap();
2510 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2512 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2514 JMutexAutoLock lock(m_sector_mutex);
2517 s32 printed_counter = -100000;
2518 s32 count = list.size();
2520 std::vector<fs::DirListNode>::iterator i;
2521 for(i=list.begin(); i!=list.end(); i++)
2523 if(counter > printed_counter + 10)
2525 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2526 printed_counter = counter;
2530 MapSector *sector = NULL;
2532 // We want directories
2536 sector = loadSectorMeta(i->name);
2538 catch(InvalidFilenameException &e)
2540 // This catches unknown crap in directory
2543 if(ENABLE_BLOCK_LOADING)
2545 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2546 (m_savedir+"/sectors/"+i->name);
2547 std::vector<fs::DirListNode>::iterator i2;
2548 for(i2=list2.begin(); i2!=list2.end(); i2++)
2554 loadBlock(i->name, i2->name, sector);
2556 catch(InvalidFilenameException &e)
2558 // This catches unknown crap in directory
2563 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2566 void ServerMap::saveMasterHeightmap()
2568 DSTACK(__FUNCTION_NAME);
2569 createDir(m_savedir);
2571 std::string fullpath = m_savedir + "/master_heightmap";
2572 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2573 if(o.good() == false)
2574 throw FileNotGoodException("Cannot open master heightmap");
2576 // Format used for writing
2577 u8 version = SER_FMT_VER_HIGHEST;
2580 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2582 [0] u8 serialization version
2583 [1] X master heightmap
2585 u32 fullsize = 1 + hmdata.getSize();
2586 SharedBuffer<u8> data(fullsize);
2589 memcpy(&data[1], *hmdata, hmdata.getSize());
2591 o.write((const char*)*data, fullsize);
2594 m_heightmap->serialize(o, version);
2597 void ServerMap::loadMasterHeightmap()
2599 DSTACK(__FUNCTION_NAME);
2600 std::string fullpath = m_savedir + "/master_heightmap";
2601 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2602 if(is.good() == false)
2603 throw FileNotGoodException("Cannot open master heightmap");
2605 if(m_heightmap != NULL)
2608 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2611 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2613 DSTACK(__FUNCTION_NAME);
2614 // Format used for writing
2615 u8 version = SER_FMT_VER_HIGHEST;
2617 v2s16 pos = sector->getPos();
2618 createDir(m_savedir);
2619 createDir(m_savedir+"/sectors");
2620 std::string dir = getSectorDir(pos);
2623 std::string fullpath = dir + "/heightmap";
2624 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2625 if(o.good() == false)
2626 throw FileNotGoodException("Cannot open master heightmap");
2628 sector->serialize(o, version);
2630 sector->differs_from_disk = false;
2633 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2635 DSTACK(__FUNCTION_NAME);
2637 v2s16 p2d = getSectorPos(dirname);
2638 std::string dir = m_savedir + "/sectors/" + dirname;
2640 std::string fullpath = dir + "/heightmap";
2641 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2642 if(is.good() == false)
2643 throw FileNotGoodException("Cannot open sector heightmap");
2645 ServerMapSector *sector = ServerMapSector::deSerialize
2646 (is, this, p2d, &m_hwrapper, m_sectors);
2648 sector->differs_from_disk = false;
2653 bool ServerMap::loadSectorFull(v2s16 p2d)
2655 DSTACK(__FUNCTION_NAME);
2656 std::string sectorsubdir = getSectorSubDir(p2d);
2658 MapSector *sector = NULL;
2660 JMutexAutoLock lock(m_sector_mutex);
2663 sector = loadSectorMeta(sectorsubdir);
2665 catch(InvalidFilenameException &e)
2669 catch(FileNotGoodException &e)
2673 catch(std::exception &e)
2678 if(ENABLE_BLOCK_LOADING)
2680 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2681 (m_savedir+"/sectors/"+sectorsubdir);
2682 std::vector<fs::DirListNode>::iterator i2;
2683 for(i2=list2.begin(); i2!=list2.end(); i2++)
2689 loadBlock(sectorsubdir, i2->name, sector);
2691 catch(InvalidFilenameException &e)
2693 // This catches unknown crap in directory
2701 bool ServerMap::deFlushSector(v2s16 p2d)
2703 DSTACK(__FUNCTION_NAME);
2704 // See if it already exists in memory
2706 MapSector *sector = getSectorNoGenerate(p2d);
2709 catch(InvalidPositionException &e)
2712 Try to load the sector from disk.
2714 if(loadSectorFull(p2d) == true)
2723 void ServerMap::saveBlock(MapBlock *block)
2725 DSTACK(__FUNCTION_NAME);
2727 Dummy blocks are not written
2729 if(block->isDummy())
2731 /*v3s16 p = block->getPos();
2732 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2733 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2737 // Format used for writing
2738 u8 version = SER_FMT_VER_HIGHEST;
2740 v3s16 p3d = block->getPos();
2741 v2s16 p2d(p3d.X, p3d.Z);
2742 createDir(m_savedir);
2743 createDir(m_savedir+"/sectors");
2744 std::string dir = getSectorDir(p2d);
2747 // Block file is map/sectors/xxxxxxxx/xxxx
2749 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2750 std::string fullpath = dir + "/" + cc;
2751 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2752 if(o.good() == false)
2753 throw FileNotGoodException("Cannot open block data");
2756 [0] u8 serialization version
2759 o.write((char*)&version, 1);
2761 block->serialize(o, version);
2764 Versions up from 9 have block objects.
2768 block->serializeObjects(o, version);
2771 // We just wrote it to the disk
2772 block->resetChangedFlag();
2775 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2777 DSTACK(__FUNCTION_NAME);
2781 // Block file is map/sectors/xxxxxxxx/xxxx
2782 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2783 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2784 if(is.good() == false)
2785 throw FileNotGoodException("Cannot open block file");
2787 v3s16 p3d = getBlockPos(sectordir, blockfile);
2788 v2s16 p2d(p3d.X, p3d.Z);
2790 assert(sector->getPos() == p2d);
2792 u8 version = SER_FMT_VER_INVALID;
2793 is.read((char*)&version, 1);
2795 /*u32 block_size = MapBlock::serializedLength(version);
2796 SharedBuffer<u8> data(block_size);
2797 is.read((char*)*data, block_size);*/
2799 // This will always return a sector because we're the server
2800 //MapSector *sector = emergeSector(p2d);
2802 MapBlock *block = NULL;
2803 bool created_new = false;
2805 block = sector->getBlockNoCreate(p3d.Y);
2807 catch(InvalidPositionException &e)
2809 block = sector->createBlankBlockNoInsert(p3d.Y);
2813 // deserialize block data
2814 block->deSerialize(is, version);
2817 Versions up from 9 have block objects.
2821 block->updateObjects(is, version, NULL, 0);
2825 sector->insertBlock(block);
2828 Convert old formats to new and save
2831 // Save old format blocks in new format
2832 if(version < SER_FMT_VER_HIGHEST)
2837 // We just loaded it from the disk, so it's up-to-date.
2838 block->resetChangedFlag();
2841 catch(SerializationError &e)
2843 dstream<<"WARNING: Invalid block data on disk "
2844 "(SerializationError). Ignoring."
2849 // Gets from master heightmap
2850 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2852 assert(m_heightmap != NULL);
2860 corners[0] = m_heightmap->getGroundHeight
2861 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2862 corners[1] = m_heightmap->getGroundHeight
2863 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2864 corners[2] = m_heightmap->getGroundHeight
2865 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2866 corners[3] = m_heightmap->getGroundHeight
2867 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2870 void ServerMap::PrintInfo(std::ostream &out)
2881 ClientMap::ClientMap(
2883 MapDrawControl &control,
2884 scene::ISceneNode* parent,
2885 scene::ISceneManager* mgr,
2889 scene::ISceneNode(parent, mgr, id),
2896 /*m_box = core::aabbox3d<f32>(0,0,0,
2897 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2898 /*m_box = core::aabbox3d<f32>(0,0,0,
2899 map->getSizeNodes().X * BS,
2900 map->getSizeNodes().Y * BS,
2901 map->getSizeNodes().Z * BS);*/
2902 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2903 BS*1000000,BS*1000000,BS*1000000);
2905 //setPosition(v3f(BS,BS,BS));
2908 ClientMap::~ClientMap()
2910 JMutexAutoLock lock(mesh_mutex);
2919 MapSector * ClientMap::emergeSector(v2s16 p2d)
2921 DSTACK(__FUNCTION_NAME);
2922 // Check that it doesn't exist already
2924 return getSectorNoGenerate(p2d);
2926 catch(InvalidPositionException &e)
2930 // Create a sector with no heightmaps
2931 ClientMapSector *sector = new ClientMapSector(this, p2d);
2934 JMutexAutoLock lock(m_sector_mutex);
2935 m_sectors.insert(p2d, sector);
2941 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2943 DSTACK(__FUNCTION_NAME);
2944 ClientMapSector *sector = NULL;
2946 JMutexAutoLock lock(m_sector_mutex);
2948 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2952 sector = (ClientMapSector*)n->getValue();
2953 assert(sector->getId() == MAPSECTOR_CLIENT);
2957 sector = new ClientMapSector(this, p2d);
2959 JMutexAutoLock lock(m_sector_mutex);
2960 m_sectors.insert(p2d, sector);
2964 sector->deSerialize(is);
2967 void ClientMap::OnRegisterSceneNode()
2971 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2972 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2975 ISceneNode::OnRegisterSceneNode();
2978 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2980 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2981 DSTACK(__FUNCTION_NAME);
2983 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2986 Get time for measuring timeout.
2988 Measuring time is very useful for long delays when the
2989 machine is swapping a lot.
2991 int time1 = time(0);
2993 u32 daynight_ratio = m_client->getDayNightRatio();
2995 m_camera_mutex.Lock();
2996 v3f camera_position = m_camera_position;
2997 v3f camera_direction = m_camera_direction;
2998 m_camera_mutex.Unlock();
3001 Get all blocks and draw all visible ones
3004 v3s16 cam_pos_nodes(
3005 camera_position.X / BS,
3006 camera_position.Y / BS,
3007 camera_position.Z / BS);
3009 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3011 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3012 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3014 // Take a fair amount as we will be dropping more out later
3016 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3017 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3018 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3020 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3021 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3022 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3024 u32 vertex_count = 0;
3026 // For limiting number of mesh updates per frame
3027 u32 mesh_update_count = 0;
3029 u32 blocks_would_have_drawn = 0;
3030 u32 blocks_drawn = 0;
3032 //NOTE: The sectors map should be locked but we're not doing it
3033 // because it'd cause too much delays
3035 int timecheck_counter = 0;
3036 core::map<v2s16, MapSector*>::Iterator si;
3037 si = m_sectors.getIterator();
3038 for(; si.atEnd() == false; si++)
3041 timecheck_counter++;
3042 if(timecheck_counter > 50)
3044 int time2 = time(0);
3045 if(time2 > time1 + 4)
3047 dstream<<"ClientMap::renderMap(): "
3048 "Rendering takes ages, returning."
3055 MapSector *sector = si.getNode()->getValue();
3056 v2s16 sp = sector->getPos();
3058 if(m_control.range_all == false)
3060 if(sp.X < p_blocks_min.X
3061 || sp.X > p_blocks_max.X
3062 || sp.Y < p_blocks_min.Z
3063 || sp.Y > p_blocks_max.Z)
3067 core::list< MapBlock * > sectorblocks;
3068 sector->getBlocks(sectorblocks);
3074 core::list< MapBlock * >::Iterator i;
3075 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3077 MapBlock *block = *i;
3080 Compare block position to camera position, skip
3081 if not seen on display
3084 v3s16 blockpos_nodes = block->getPosRelative();
3086 // Block center position
3088 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3089 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3090 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3093 // Block position relative to camera
3094 v3f blockpos_relative = blockpos - camera_position;
3096 // Distance in camera direction (+=front, -=back)
3097 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3100 f32 d = blockpos_relative.getLength();
3102 if(m_control.range_all == false)
3104 // If block is far away, don't draw it
3105 if(d > m_control.wanted_range * BS)
3106 // This is nicer when fog is used
3107 //if((dforward+d)/2 > m_control.wanted_range * BS)
3111 // Maximum radius of a block
3112 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3114 // If block is (nearly) touching the camera, don't
3115 // bother validating further (that is, render it anyway)
3116 if(d > block_max_radius * 1.5)
3118 // Cosine of the angle between the camera direction
3119 // and the block direction (camera_direction is an unit vector)
3120 f32 cosangle = dforward / d;
3122 // Compensate for the size of the block
3123 // (as the block has to be shown even if it's a bit off FOV)
3124 // This is an estimate.
3125 cosangle += block_max_radius / dforward;
3127 // If block is not in the field of view, skip it
3128 //if(cosangle < cos(FOV_ANGLE/2))
3129 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3134 Draw the faces of the block
3137 bool mesh_expired = false;
3140 JMutexAutoLock lock(block->mesh_mutex);
3142 mesh_expired = block->getMeshExpired();
3144 // Mesh has not been expired and there is no mesh:
3145 // block has no content
3146 if(block->mesh == NULL && mesh_expired == false)
3150 f32 faraway = BS*50;
3151 //f32 faraway = m_control.wanted_range * BS;
3154 This has to be done with the mesh_mutex unlocked
3156 // Pretty random but this should work somewhat nicely
3157 if(mesh_expired && (
3158 (mesh_update_count < 3
3159 && (d < faraway || mesh_update_count < 2)
3162 (m_control.range_all && mesh_update_count < 20)
3165 /*if(mesh_expired && mesh_update_count < 6
3166 && (d < faraway || mesh_update_count < 3))*/
3168 mesh_update_count++;
3170 // Mesh has been expired: generate new mesh
3171 //block->updateMeshes(daynight_i);
3172 block->updateMesh(daynight_ratio);
3174 mesh_expired = false;
3178 Don't draw an expired mesh that is far away
3180 /*if(mesh_expired && d >= faraway)
3183 // Instead, delete it
3184 JMutexAutoLock lock(block->mesh_mutex);
3187 block->mesh->drop();
3190 // And continue to next block
3195 JMutexAutoLock lock(block->mesh_mutex);
3197 scene::SMesh *mesh = block->mesh;
3202 blocks_would_have_drawn++;
3203 if(blocks_drawn >= m_control.wanted_max_blocks
3204 && m_control.range_all == false
3205 && d > m_control.wanted_min_range * BS)
3209 u32 c = mesh->getMeshBufferCount();
3211 for(u32 i=0; i<c; i++)
3213 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3214 const video::SMaterial& material = buf->getMaterial();
3215 video::IMaterialRenderer* rnd =
3216 driver->getMaterialRenderer(material.MaterialType);
3217 bool transparent = (rnd && rnd->isTransparent());
3218 // Render transparent on transparent pass and likewise.
3219 if(transparent == is_transparent_pass)
3221 driver->setMaterial(buf->getMaterial());
3222 driver->drawMeshBuffer(buf);
3223 vertex_count += buf->getVertexCount();
3227 } // foreach sectorblocks
3230 m_control.blocks_drawn = blocks_drawn;
3231 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3233 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3234 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3237 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3240 Add it to all blocks touching it
3243 v3s16(0,0,0), // this
3244 v3s16(0,0,1), // back
3245 v3s16(0,1,0), // top
3246 v3s16(1,0,0), // right
3247 v3s16(0,0,-1), // front
3248 v3s16(0,-1,0), // bottom
3249 v3s16(-1,0,0), // left
3251 for(u16 i=0; i<7; i++)
3253 v3s16 p2 = p + dirs[i];
3254 // Block position of neighbor (or requested) node
3255 v3s16 blockpos = getNodeBlockPos(p2);
3256 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3257 if(blockref == NULL)
3259 // Relative position of requested node
3260 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3261 blockref->setTempMod(relpos, mod);
3263 return getNodeBlockPos(p);
3265 v3s16 ClientMap::clearTempMod(v3s16 p)
3268 v3s16(0,0,0), // this
3269 v3s16(0,0,1), // back
3270 v3s16(0,1,0), // top
3271 v3s16(1,0,0), // right
3272 v3s16(0,0,-1), // front
3273 v3s16(0,-1,0), // bottom
3274 v3s16(-1,0,0), // left
3276 for(u16 i=0; i<7; i++)
3278 v3s16 p2 = p + dirs[i];
3279 // Block position of neighbor (or requested) node
3280 v3s16 blockpos = getNodeBlockPos(p2);
3281 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3282 if(blockref == NULL)
3284 // Relative position of requested node
3285 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3286 blockref->clearTempMod(relpos);
3288 return getNodeBlockPos(p);
3291 void ClientMap::PrintInfo(std::ostream &out)
3302 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3307 MapVoxelManipulator::~MapVoxelManipulator()
3309 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3314 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3316 TimeTaker timer1("emerge", &emerge_time);
3318 // Units of these are MapBlocks
3319 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3320 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3322 VoxelArea block_area_nodes
3323 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3325 addArea(block_area_nodes);
3327 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3328 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3329 for(s32 x=p_min.X; x<=p_max.X; x++)
3332 core::map<v3s16, bool>::Node *n;
3333 n = m_loaded_blocks.find(p);
3337 bool block_data_inexistent = false;
3340 TimeTaker timer1("emerge load", &emerge_load_time);
3342 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3343 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3346 dstream<<std::endl;*/
3348 MapBlock *block = m_map->getBlockNoCreate(p);
3349 if(block->isDummy())
3350 block_data_inexistent = true;
3352 block->copyTo(*this);
3354 catch(InvalidPositionException &e)
3356 block_data_inexistent = true;
3359 if(block_data_inexistent)
3361 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3362 // Fill with VOXELFLAG_INEXISTENT
3363 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3364 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3366 s32 i = m_area.index(a.MinEdge.X,y,z);
3367 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3371 m_loaded_blocks.insert(p, true);
3374 //dstream<<"emerge done"<<std::endl;
3379 void MapVoxelManipulator::emerge(VoxelArea a)
3381 TimeTaker timer1("emerge", &emerge_time);
3383 v3s16 size = a.getExtent();
3385 VoxelArea padded = a;
3386 padded.pad(m_area.getExtent() / 4);
3389 for(s16 z=0; z<size.Z; z++)
3390 for(s16 y=0; y<size.Y; y++)
3391 for(s16 x=0; x<size.X; x++)
3394 s32 i = m_area.index(a.MinEdge + p);
3395 // Don't touch nodes that have already been loaded
3396 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3400 TimeTaker timer1("emerge load", &emerge_load_time);
3401 MapNode n = m_map->getNode(a.MinEdge + p);
3405 catch(InvalidPositionException &e)
3407 m_flags[i] = VOXELFLAG_INEXISTENT;
3415 TODO: Add an option to only update eg. water and air nodes.
3416 This will make it interfere less with important stuff if
3419 void MapVoxelManipulator::blitBack
3420 (core::map<v3s16, MapBlock*> & modified_blocks)
3422 if(m_area.getExtent() == v3s16(0,0,0))
3425 //TimeTaker timer1("blitBack");
3428 Initialize block cache
3430 v3s16 blockpos_last;
3431 MapBlock *block = NULL;
3432 bool block_checked_in_modified = false;
3434 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3435 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3436 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3440 u8 f = m_flags[m_area.index(p)];
3441 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3444 MapNode &n = m_data[m_area.index(p)];
3446 v3s16 blockpos = getNodeBlockPos(p);
3451 if(block == NULL || blockpos != blockpos_last){
3452 block = m_map->getBlockNoCreate(blockpos);
3453 blockpos_last = blockpos;
3454 block_checked_in_modified = false;
3457 // Calculate relative position in block
3458 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3460 // Don't continue if nothing has changed here
3461 if(block->getNode(relpos) == n)
3464 //m_map->setNode(m_area.MinEdge + p, n);
3465 block->setNode(relpos, n);
3468 Make sure block is in modified_blocks
3470 if(block_checked_in_modified == false)
3472 modified_blocks[blockpos] = block;
3473 block_checked_in_modified = true;
3476 catch(InvalidPositionException &e)