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):
1316 Experimental and debug stuff
1320 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1321 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1322 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1323 PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1324 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1326 for(u32 i=0; i<3000; i++)
1328 u32 lim = MAP_GENERATION_LIMIT;
1333 -lim + myrand()%(lim*2),
1335 -lim + myrand()%(lim*2)
1337 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1338 plants_amount = pow(plants_amount, 5);
1339 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1341 float plants_amount = 0;
1344 plants_amount = 1.5;
1346 else if(myrand()%4 == 0)
1348 plants_amount = 0.5;
1350 else if(myrand()%2 == 0)
1352 plants_amount = 0.03;
1356 plants_amount = 0.0;
1359 float caves_amount = 0;
1364 else if(myrand()%3 == 0)
1370 caves_amount = 0.05;
1373 list_plants_amount->addPoint(p, Attribute(plants_amount));
1374 list_caves_amount->addPoint(p, Attribute(caves_amount));
1377 for(u32 i=0; i<3000; i++)
1379 u32 lim = MAP_GENERATION_LIMIT;
1384 -lim + myrand()%(lim*2),
1386 -lim + myrand()%(lim*2)
1389 /*s32 bh_i = (myrand()%200) - 50;
1390 float baseheight = (float)bh_i;
1394 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1395 randmax = pow(randmax, e);
1397 //float randmax = (float)(myrand()%60);
1398 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1400 float baseheight = 0;
1402 float randfactor = 0;
1410 else if(myrand()%5 == 0)
1416 else if(myrand()%4 == 0)
1422 else if(myrand()%3 == 0)
1435 list_baseheight->addPoint(p, Attribute(baseheight));
1436 list_randmax->addPoint(p, Attribute(randmax));
1437 list_randfactor->addPoint(p, Attribute(randfactor));
1441 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
1442 list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
1443 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
1448 PointAttributeList *palist = m_padb.getList("hm_baseheight");
1454 palist->addPoint(p, attr);
1461 palist->addPoint(p, attr);
1468 palist->addPoint(p, attr);
1474 PointAttributeList *palist = m_padb.getList("plants_amount");
1481 palist->addPoint(p, attr);
1489 palist->addPoint(p, attr);
1494 v3s16 p(-100,0,100);
1497 palist->addPoint(p, attr);
1503 Try to load map; if not found, create a new one.
1506 m_savedir = savedir;
1507 m_map_saving_enabled = false;
1511 // If directory exists, check contents and load if possible
1512 if(fs::PathExists(m_savedir))
1514 // If directory is empty, it is safe to save into it.
1515 if(fs::GetDirListing(m_savedir).size() == 0)
1517 dstream<<DTIME<<"Server: Empty save directory is valid."
1519 m_map_saving_enabled = true;
1523 // Load master heightmap
1524 loadMasterHeightmap();
1526 // Load sector (0,0) and throw and exception on fail
1527 if(loadSectorFull(v2s16(0,0)) == false)
1528 throw LoadError("Failed to load sector (0,0)");
1530 dstream<<DTIME<<"Server: Successfully loaded master "
1531 "heightmap and sector (0,0) from "<<savedir<<
1532 ", assuming valid save directory."
1535 m_map_saving_enabled = true;
1536 // Map loaded, not creating new one
1540 // If directory doesn't exist, it is safe to save to it
1542 m_map_saving_enabled = true;
1545 catch(std::exception &e)
1547 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1548 <<", exception: "<<e.what()<<std::endl;
1549 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1550 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1553 dstream<<DTIME<<"Initializing new map."<<std::endl;
1555 // Create master heightmap
1556 /*ValueGenerator *maxgen =
1557 ValueGenerator::deSerialize(hmp.randmax);
1558 ValueGenerator *factorgen =
1559 ValueGenerator::deSerialize(hmp.randfactor);
1560 ValueGenerator *basegen =
1561 ValueGenerator::deSerialize(hmp.base);
1562 m_heightmap = new UnlimitedHeightmap
1563 (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
1565 /*m_heightmap = new UnlimitedHeightmap
1566 (hmp.blocksize, &m_padb);*/
1568 m_heightmap = new UnlimitedHeightmap
1571 // Set map parameters
1574 // Create zero sector
1575 emergeSector(v2s16(0,0));
1577 // Initially write whole map
1581 ServerMap::~ServerMap()
1585 if(m_map_saving_enabled)
1588 // Save only changed parts
1590 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1594 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1597 catch(std::exception &e)
1599 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1600 <<", exception: "<<e.what()<<std::endl;
1603 if(m_heightmap != NULL)
1607 MapSector * ServerMap::emergeSector(v2s16 p2d)
1609 DSTACK("%s: p2d=(%d,%d)",
1612 // Check that it doesn't exist already
1614 return getSectorNoGenerate(p2d);
1616 catch(InvalidPositionException &e)
1621 Try to load the sector from disk.
1623 if(loadSectorFull(p2d) == true)
1625 return getSectorNoGenerate(p2d);
1629 If there is no master heightmap, throw.
1631 if(m_heightmap == NULL)
1633 throw InvalidPositionException("emergeSector(): no heightmap");
1637 Do not generate over-limit
1639 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1640 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1641 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1642 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1643 throw InvalidPositionException("emergeSector(): pos. over limit");
1646 Generate sector and heightmaps
1649 // Number of heightmaps in sector in each direction
1650 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1652 // Heightmap side width
1653 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1655 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1657 // Sector position on map in nodes
1658 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1660 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1661 " heightmaps and objects"<<std::endl;*/
1664 Calculate some information about local properties
1667 v2s16 mhm_p = p2d * hm_split;
1669 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1670 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1671 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1672 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1675 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1676 float avgslope = 0.0;
1677 avgslope += fabs(avgheight - corners[0]);
1678 avgslope += fabs(avgheight - corners[1]);
1679 avgslope += fabs(avgheight - corners[2]);
1680 avgslope += fabs(avgheight - corners[3]);
1682 avgslope /= MAP_BLOCKSIZE;
1683 //dstream<<"avgslope="<<avgslope<<std::endl;
1685 float pitness = 0.0;
1687 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1690 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1693 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1696 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1700 pitness /= MAP_BLOCKSIZE;
1701 //dstream<<"pitness="<<pitness<<std::endl;
1704 Get local attributes
1707 // Get plant amount from attributes
1708 PointAttributeList *palist = m_padb.getList("plants_amount");
1710 /*float local_plants_amount =
1711 palist->getNearAttr(nodepos2d).getFloat();*/
1712 float local_plants_amount =
1713 palist->getInterpolatedFloat(nodepos2d);
1716 Generate sector heightmap
1719 // Loop through sub-heightmaps
1720 for(s16 y=0; y<hm_split; y++)
1721 for(s16 x=0; x<hm_split; x++)
1723 v2s16 p_in_sector = v2s16(x,y);
1724 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1726 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1727 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1728 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1729 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1732 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1733 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1736 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1738 sector->setHeightmap(p_in_sector, hm);
1740 //TODO: Make these values configurable
1741 //hm->generateContinued(0.0, 0.0, corners);
1742 //hm->generateContinued(0.25, 0.2, corners);
1743 //hm->generateContinued(0.5, 0.2, corners);
1744 //hm->generateContinued(1.0, 0.2, corners);
1745 //hm->generateContinued(2.0, 0.2, corners);
1746 hm->generateContinued(2.0 * avgslope, 0.5, corners);
1755 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1756 sector->setObjects(objects);
1759 Plant some trees if there is not much slope
1762 // Avgslope is the derivative of a hill
1763 //float t = avgslope * avgslope;
1765 float a = MAP_BLOCKSIZE * m_params.plants_amount * local_plants_amount;
1767 //float something = 0.17*0.17;
1768 float something = 0.3;
1770 tree_max = a / (t/something);
1774 u32 count = (myrand()%(tree_max+1));
1775 //u32 count = tree_max;
1776 for(u32 i=0; i<count; i++)
1778 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
1779 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
1780 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1783 objects->insert(v3s16(x, y, z),
1784 SECTOR_OBJECT_TREE_1);
1788 Plant some bushes if sector is pit-like
1791 // Pitness usually goes at around -0.5...0.5
1793 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount * local_plants_amount;
1795 bush_max = (pitness*a*4);
1798 u32 count = (myrand()%(bush_max+1));
1799 for(u32 i=0; i<count; i++)
1801 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
1802 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
1803 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1806 objects->insert(v3s16(x, y, z),
1807 SECTOR_OBJECT_BUSH_1);
1811 Add ravine (randomly)
1813 if(m_params.ravines_amount != 0)
1815 if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
1818 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1819 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1822 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1823 objects->insert(v3s16(x, y, z),
1824 SECTOR_OBJECT_RAVINE);
1831 JMutexAutoLock lock(m_sector_mutex);
1832 m_sectors.insert(p2d, sector);
1837 MapBlock * ServerMap::emergeBlock(
1839 bool only_from_disk,
1840 core::map<v3s16, MapBlock*> &changed_blocks,
1841 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1844 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1846 p.X, p.Y, p.Z, only_from_disk);
1848 /*dstream<<"ServerMap::emergeBlock(): "
1849 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1850 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1851 v2s16 p2d(p.X, p.Z);
1854 This will create or load a sector if not found in memory.
1855 If block exists on disk, it will be loaded.
1857 NOTE: On old save formats, this will be slow, as it generates
1858 lighting on blocks for them.
1860 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1861 assert(sector->getId() == MAPSECTOR_SERVER);
1863 // Try to get a block from the sector
1864 MapBlock *block = NULL;
1865 bool not_on_disk = false;
1867 block = sector->getBlockNoCreate(block_y);
1868 if(block->isDummy() == true)
1873 catch(InvalidPositionException &e)
1879 If block was not found on disk and not going to generate a
1880 new one, make sure there is a dummy block in place.
1882 if(not_on_disk && only_from_disk)
1886 // Create dummy block
1887 block = new MapBlock(this, p, true);
1889 // Add block to sector
1890 sector->insertBlock(block);
1896 //dstream<<"Not found on disk, generating."<<std::endl;
1897 //TimeTaker("emergeBlock()", g_irrlicht);
1900 Do not generate over-limit
1902 if(blockpos_over_limit(p))
1903 throw InvalidPositionException("emergeBlock(): pos. over limit");
1908 Go on generating the block.
1910 TODO: If a dungeon gets generated so that it's side gets
1911 revealed to the outside air, the lighting should be
1916 If block doesn't exist, create one.
1917 If it exists, it is a dummy. In that case unDummify() it.
1919 NOTE: This already sets the map as the parent of the block
1923 block = sector->createBlankBlockNoInsert(block_y);
1927 // Remove the block so that nobody can get a half-generated one.
1928 sector->removeBlock(block);
1929 // Allocate the block to contain the generated data
1933 u8 water_material = CONTENT_WATER;
1934 if(g_settings.getBool("endless_water"))
1935 water_material = CONTENT_OCEAN;
1937 s32 lowest_ground_y = 32767;
1938 s32 highest_ground_y = -32768;
1941 //sector->printHeightmaps();
1943 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1944 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1946 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1948 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1949 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1950 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1952 dstream<<"WARNING: Surface height not found in sector "
1953 "for block that is being emerged"<<std::endl;
1957 s16 surface_y = surface_y_f;
1958 //avg_ground_y += surface_y;
1959 if(surface_y < lowest_ground_y)
1960 lowest_ground_y = surface_y;
1961 if(surface_y > highest_ground_y)
1962 highest_ground_y = surface_y;
1964 s32 surface_depth = 0;
1966 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1968 //float min_slope = 0.45;
1969 //float max_slope = 0.85;
1970 float min_slope = 0.60;
1971 float max_slope = 1.20;
1972 float min_slope_depth = 5.0;
1973 float max_slope_depth = 0;
1975 if(slope < min_slope)
1976 surface_depth = min_slope_depth;
1977 else if(slope > max_slope)
1978 surface_depth = max_slope_depth;
1980 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1982 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1984 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1989 NOTE: If there are some man-made structures above the
1990 newly created block, they won't be taken into account.
1992 if(real_y > surface_y)
1993 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1999 // If node is over heightmap y, it's air or water
2000 if(real_y > surface_y)
2002 // If under water level, it's water
2003 if(real_y < WATER_LEVEL)
2005 n.d = water_material;
2006 n.setLight(LIGHTBANK_DAY,
2007 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2013 // Else it's ground or dungeons (air)
2016 // If it's surface_depth under ground, it's stone
2017 if(real_y <= surface_y - surface_depth)
2019 n.d = CONTENT_STONE;
2023 // It is mud if it is under the first ground
2024 // level or under water
2025 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2031 n.d = CONTENT_GRASS;
2034 //n.d = CONTENT_MUD;
2036 /*// If under water level, it's mud
2037 if(real_y < WATER_LEVEL)
2039 // Only the topmost node is grass
2040 else if(real_y <= surface_y - 1)
2043 n.d = CONTENT_GRASS;*/
2047 block->setNode(v3s16(x0,y0,z0), n);
2052 Calculate some helper variables
2055 // Completely underground if the highest part of block is under lowest
2057 // This has to be very sure; it's probably one too strict now but
2058 // that's just better.
2059 bool completely_underground =
2060 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2062 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2065 Get local attributes
2068 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2069 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
2070 float caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
2076 // Initialize temporary table
2077 const s32 ued = MAP_BLOCKSIZE;
2078 bool underground_emptiness[ued*ued*ued];
2079 for(s32 i=0; i<ued*ued*ued; i++)
2081 underground_emptiness[i] = 0;
2087 Initialize orp and ors. Try to find if some neighboring
2088 MapBlock has a tunnel ended in its side
2092 (float)(myrand()%ued)+0.5,
2093 (float)(myrand()%ued)+0.5,
2094 (float)(myrand()%ued)+0.5
2097 bool found_existing = false;
2103 for(s16 y=0; y<ued; y++)
2104 for(s16 x=0; x<ued; x++)
2106 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2107 if(getNode(ap).d == CONTENT_AIR)
2109 orp = v3f(x+1,y+1,0);
2110 found_existing = true;
2111 goto continue_generating;
2115 catch(InvalidPositionException &e){}
2121 for(s16 y=0; y<ued; y++)
2122 for(s16 x=0; x<ued; x++)
2124 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2125 if(getNode(ap).d == CONTENT_AIR)
2127 orp = v3f(x+1,y+1,ued-1);
2128 found_existing = true;
2129 goto continue_generating;
2133 catch(InvalidPositionException &e){}
2139 for(s16 y=0; y<ued; y++)
2140 for(s16 z=0; z<ued; z++)
2142 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2143 if(getNode(ap).d == CONTENT_AIR)
2145 orp = v3f(0,y+1,z+1);
2146 found_existing = true;
2147 goto continue_generating;
2151 catch(InvalidPositionException &e){}
2157 for(s16 y=0; y<ued; y++)
2158 for(s16 z=0; z<ued; z++)
2160 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2161 if(getNode(ap).d == CONTENT_AIR)
2163 orp = v3f(ued-1,y+1,z+1);
2164 found_existing = true;
2165 goto continue_generating;
2169 catch(InvalidPositionException &e){}
2175 for(s16 x=0; x<ued; x++)
2176 for(s16 z=0; z<ued; z++)
2178 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2179 if(getNode(ap).d == CONTENT_AIR)
2181 orp = v3f(x+1,0,z+1);
2182 found_existing = true;
2183 goto continue_generating;
2187 catch(InvalidPositionException &e){}
2193 for(s16 x=0; x<ued; x++)
2194 for(s16 z=0; z<ued; z++)
2196 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2197 if(getNode(ap).d == CONTENT_AIR)
2199 orp = v3f(x+1,ued-1,z+1);
2200 found_existing = true;
2201 goto continue_generating;
2205 catch(InvalidPositionException &e){}
2207 continue_generating:
2210 Don't always generate dungeon
2212 bool do_generate_dungeons = true;
2213 // Don't generate if no part is underground
2214 if(!some_part_underground)
2215 do_generate_dungeons = false;
2216 // If block is partly underground, caves are generated.
2217 else if(!completely_underground)
2218 do_generate_dungeons = (rand() % 100 <= (u32)(caves_amount*100));
2219 // Always continue if found existing dungeons underground
2220 else if(found_existing && completely_underground)
2221 do_generate_dungeons = true;
2222 // If underground and no dungeons found
2224 do_generate_dungeons = (rand() % 2 == 0);
2226 if(do_generate_dungeons)
2229 Generate some tunnel starting from orp and ors
2231 for(u16 i=0; i<3; i++)
2234 (float)(myrand()%ued)+0.5,
2235 (float)(myrand()%ued)+0.5,
2236 (float)(myrand()%ued)+0.5
2240 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
2244 for(float f=0; f<1.0; f+=0.04)
2246 v3f fp = orp + vec * f;
2247 v3s16 cp(fp.X, fp.Y, fp.Z);
2249 s16 d1 = d0 + rs - 1;
2250 for(s16 z0=d0; z0<=d1; z0++)
2252 s16 si = rs - abs(z0);
2253 for(s16 x0=-si; x0<=si-1; x0++)
2255 s16 si2 = rs - abs(x0);
2256 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2262 if(isInArea(p, ued) == false)
2264 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2275 // Set to true if has caves.
2276 // Set when some non-air is changed to air when making caves.
2277 bool has_caves = false;
2280 Apply temporary cave data to block
2283 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2284 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2286 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2288 MapNode n = block->getNode(v3s16(x0,y0,z0));
2291 if(underground_emptiness[
2292 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2293 +ued*(y0*ued/MAP_BLOCKSIZE)
2294 +(x0*ued/MAP_BLOCKSIZE)])
2296 if(is_ground_content(n.d))
2305 block->setNode(v3s16(x0,y0,z0), n);
2310 This is used for guessing whether or not the block should
2311 receive sunlight from the top if the top block doesn't exist
2313 block->setIsUnderground(completely_underground);
2316 Force lighting update if some part of block is partly
2317 underground and has caves.
2319 /*if(some_part_underground && !completely_underground && has_caves)
2321 //dstream<<"Half-ground caves"<<std::endl;
2322 lighting_invalidated_blocks[block->getPos()] = block;
2325 // DEBUG: Always update lighting
2326 //lighting_invalidated_blocks[block->getPos()] = block;
2332 if(some_part_underground)
2334 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2339 for(s16 i=0; i< underground_level/4 + 1; i++)
2341 if(myrand()%10 == 0)
2344 (myrand()%(MAP_BLOCKSIZE-2))+1,
2345 (myrand()%(MAP_BLOCKSIZE-2))+1,
2346 (myrand()%(MAP_BLOCKSIZE-2))+1
2352 //if(is_ground_content(block->getNode(cp).d))
2353 if(block->getNode(cp).d == CONTENT_STONE)
2355 block->setNode(cp, n);
2357 for(u16 i=0; i<26; i++)
2359 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2360 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2362 block->setNode(cp+g_26dirs[i], n);
2370 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2371 u16 coal_rareness = 60 / coal_amount;
2372 if(coal_rareness == 0)
2374 if(myrand()%coal_rareness == 0)
2376 u16 a = myrand() % 16;
2377 u16 amount = coal_amount * a*a*a / 1000;
2378 for(s16 i=0; i<amount; i++)
2381 (myrand()%(MAP_BLOCKSIZE-2))+1,
2382 (myrand()%(MAP_BLOCKSIZE-2))+1,
2383 (myrand()%(MAP_BLOCKSIZE-2))+1
2387 n.d = CONTENT_COALSTONE;
2389 //dstream<<"Adding coalstone"<<std::endl;
2391 //if(is_ground_content(block->getNode(cp).d))
2392 if(block->getNode(cp).d == CONTENT_STONE)
2394 block->setNode(cp, n);
2396 for(u16 i=0; i<26; i++)
2398 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2399 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2401 block->setNode(cp+g_26dirs[i], n);
2408 Create a few rats in empty blocks underground
2410 if(completely_underground)
2412 //for(u16 i=0; i<2; i++)
2415 (myrand()%(MAP_BLOCKSIZE-2))+1,
2416 (myrand()%(MAP_BLOCKSIZE-2))+1,
2417 (myrand()%(MAP_BLOCKSIZE-2))+1
2420 // Check that the place is empty
2421 //if(!is_ground_content(block->getNode(cp).d))
2424 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2425 block->addObject(obj);
2431 Add block to sector.
2433 sector->insertBlock(block);
2439 // An y-wise container of changed blocks
2440 core::map<s16, MapBlock*> changed_blocks_sector;
2443 Check if any sector's objects can be placed now.
2446 core::map<v3s16, u8> *objects = sector->getObjects();
2447 core::list<v3s16> objects_to_remove;
2448 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2449 i.atEnd() == false; i++)
2451 v3s16 p = i.getNode()->getKey();
2453 u8 d = i.getNode()->getValue();
2455 // Ground level point (user for stuff that is on ground)
2457 bool ground_found = true;
2459 // Search real ground level
2463 MapNode n = sector->getNode(gp);
2465 // If not air, go one up and continue to placing the tree
2466 if(n.d != CONTENT_AIR)
2472 // If air, go one down
2473 gp += v3s16(0,-1,0);
2475 }catch(InvalidPositionException &e)
2477 // Ground not found.
2478 ground_found = false;
2479 // This is most close to ground
2486 if(d == SECTOR_OBJECT_TEST)
2488 if(sector->isValidArea(p + v3s16(0,0,0),
2489 p + v3s16(0,0,0), &changed_blocks_sector))
2492 n.d = CONTENT_TORCH;
2493 sector->setNode(p, n);
2494 objects_to_remove.push_back(p);
2497 else if(d == SECTOR_OBJECT_TREE_1)
2499 if(ground_found == false)
2502 v3s16 p_min = gp + v3s16(-1,0,-1);
2503 v3s16 p_max = gp + v3s16(1,5,1);
2504 if(sector->isValidArea(p_min, p_max,
2505 &changed_blocks_sector))
2509 sector->setNode(gp+v3s16(0,0,0), n);
2510 sector->setNode(gp+v3s16(0,1,0), n);
2511 sector->setNode(gp+v3s16(0,2,0), n);
2512 sector->setNode(gp+v3s16(0,3,0), n);
2514 n.d = CONTENT_LEAVES;
2516 if(rand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2518 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2519 if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2520 if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2521 if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2522 /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2523 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2524 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2525 if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2527 sector->setNode(gp+v3s16(0,4,0), n);
2529 sector->setNode(gp+v3s16(-1,4,0), n);
2530 sector->setNode(gp+v3s16(1,4,0), n);
2531 sector->setNode(gp+v3s16(0,4,-1), n);
2532 sector->setNode(gp+v3s16(0,4,1), n);
2533 sector->setNode(gp+v3s16(1,4,1), n);
2534 sector->setNode(gp+v3s16(-1,4,1), n);
2535 sector->setNode(gp+v3s16(-1,4,-1), n);
2536 sector->setNode(gp+v3s16(1,4,-1), n);
2538 sector->setNode(gp+v3s16(-1,3,0), n);
2539 sector->setNode(gp+v3s16(1,3,0), n);
2540 sector->setNode(gp+v3s16(0,3,-1), n);
2541 sector->setNode(gp+v3s16(0,3,1), n);
2542 sector->setNode(gp+v3s16(1,3,1), n);
2543 sector->setNode(gp+v3s16(-1,3,1), n);
2544 sector->setNode(gp+v3s16(-1,3,-1), n);
2545 sector->setNode(gp+v3s16(1,3,-1), n);
2547 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2548 if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2549 if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2550 if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2551 /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2552 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2553 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2554 if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2556 // Objects are identified by wanted position
2557 objects_to_remove.push_back(p);
2559 // Lighting has to be recalculated for this one.
2560 sector->getBlocksInArea(p_min, p_max,
2561 lighting_invalidated_blocks);
2564 else if(d == SECTOR_OBJECT_BUSH_1)
2566 if(ground_found == false)
2569 if(sector->isValidArea(gp + v3s16(0,0,0),
2570 gp + v3s16(0,0,0), &changed_blocks_sector))
2573 n.d = CONTENT_LEAVES;
2574 sector->setNode(gp+v3s16(0,0,0), n);
2576 // Objects are identified by wanted position
2577 objects_to_remove.push_back(p);
2580 else if(d == SECTOR_OBJECT_RAVINE)
2583 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2584 v3s16 p_max = p + v3s16(6,6,6);
2585 if(sector->isValidArea(p_min, p_max,
2586 &changed_blocks_sector))
2589 n.d = CONTENT_STONE;
2592 s16 depth = maxdepth + (myrand()%10);
2594 s16 minz = -6 - (-2);
2596 for(s16 x=-6; x<=6; x++)
2598 z += -1 + (myrand()%3);
2603 for(s16 y=depth+(myrand()%2); y<=6; y++)
2605 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2608 v3s16 p2 = p + v3s16(x,y,z-2);
2609 if(is_ground_content(sector->getNode(p2).d)
2610 && !is_mineral(sector->getNode(p2).d))
2611 sector->setNode(p2, n);
2614 v3s16 p2 = p + v3s16(x,y,z-1);
2615 if(is_ground_content(sector->getNode(p2).d)
2616 && !is_mineral(sector->getNode(p2).d))
2617 sector->setNode(p2, n2);
2620 v3s16 p2 = p + v3s16(x,y,z+0);
2621 if(is_ground_content(sector->getNode(p2).d)
2622 && !is_mineral(sector->getNode(p2).d))
2623 sector->setNode(p2, n2);
2626 v3s16 p2 = p + v3s16(x,y,z+1);
2627 if(is_ground_content(sector->getNode(p2).d)
2628 && !is_mineral(sector->getNode(p2).d))
2629 sector->setNode(p2, n);
2632 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2633 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2637 objects_to_remove.push_back(p);
2639 // Lighting has to be recalculated for this one.
2640 sector->getBlocksInArea(p_min, p_max,
2641 lighting_invalidated_blocks);
2646 dstream<<"ServerMap::emergeBlock(): "
2647 "Invalid heightmap object"
2652 catch(InvalidPositionException &e)
2654 dstream<<"WARNING: "<<__FUNCTION_NAME
2655 <<": while inserting object "<<(int)d
2656 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2657 <<" InvalidPositionException.what()="
2658 <<e.what()<<std::endl;
2659 // This is not too fatal and seems to happen sometimes.
2664 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2665 i != objects_to_remove.end(); i++)
2667 objects->remove(*i);
2671 Initially update sunlight
2675 core::map<v3s16, bool> light_sources;
2676 bool black_air_left = false;
2677 bool bottom_invalid =
2678 block->propagateSunlight(light_sources, true, &black_air_left);
2680 // If sunlight didn't reach everywhere and part of block is
2681 // above ground, lighting has to be properly updated
2682 if(black_air_left && some_part_underground)
2684 lighting_invalidated_blocks[block->getPos()] = block;
2689 Translate sector's changed blocks to global changed blocks
2692 for(core::map<s16, MapBlock*>::Iterator
2693 i = changed_blocks_sector.getIterator();
2694 i.atEnd() == false; i++)
2696 MapBlock *block = i.getNode()->getValue();
2698 changed_blocks.insert(block->getPos(), block);
2703 // Don't calculate lighting at all
2704 lighting_invalidated_blocks.clear();
2710 void ServerMap::createDir(std::string path)
2712 if(fs::CreateDir(path) == false)
2714 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2715 <<"\""<<path<<"\""<<std::endl;
2716 throw BaseException("ServerMap failed to create directory");
2720 std::string ServerMap::getSectorSubDir(v2s16 pos)
2723 snprintf(cc, 9, "%.4x%.4x",
2724 (unsigned int)pos.X&0xffff,
2725 (unsigned int)pos.Y&0xffff);
2727 return std::string(cc);
2730 std::string ServerMap::getSectorDir(v2s16 pos)
2732 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2735 v2s16 ServerMap::getSectorPos(std::string dirname)
2737 if(dirname.size() != 8)
2738 throw InvalidFilenameException("Invalid sector directory name");
2740 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2742 throw InvalidFilenameException("Invalid sector directory name");
2743 v2s16 pos((s16)x, (s16)y);
2747 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2749 v2s16 p2d = getSectorPos(sectordir);
2751 if(blockfile.size() != 4){
2752 throw InvalidFilenameException("Invalid block filename");
2755 int r = sscanf(blockfile.c_str(), "%4x", &y);
2757 throw InvalidFilenameException("Invalid block filename");
2758 return v3s16(p2d.X, y, p2d.Y);
2762 #define ENABLE_SECTOR_SAVING 1
2763 #define ENABLE_SECTOR_LOADING 1
2764 #define ENABLE_BLOCK_SAVING 1
2765 #define ENABLE_BLOCK_LOADING 1
2767 void ServerMap::save(bool only_changed)
2769 DSTACK(__FUNCTION_NAME);
2770 if(m_map_saving_enabled == false)
2772 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2776 if(only_changed == false)
2777 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2780 saveMasterHeightmap();
2782 u32 sector_meta_count = 0;
2783 u32 block_count = 0;
2786 JMutexAutoLock lock(m_sector_mutex);
2788 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2789 for(; i.atEnd() == false; i++)
2791 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2792 assert(sector->getId() == MAPSECTOR_SERVER);
2794 if(ENABLE_SECTOR_SAVING)
2796 if(sector->differs_from_disk || only_changed == false)
2798 saveSectorMeta(sector);
2799 sector_meta_count++;
2802 if(ENABLE_BLOCK_SAVING)
2804 core::list<MapBlock*> blocks;
2805 sector->getBlocks(blocks);
2806 core::list<MapBlock*>::Iterator j;
2807 for(j=blocks.begin(); j!=blocks.end(); j++)
2809 MapBlock *block = *j;
2810 if(block->getChangedFlag() || only_changed == false)
2822 Only print if something happened or saved whole map
2824 if(only_changed == false || sector_meta_count != 0
2825 || block_count != 0)
2827 dstream<<DTIME<<"ServerMap: Written: "
2828 <<sector_meta_count<<" sector metadata files, "
2829 <<block_count<<" block files"
2834 void ServerMap::loadAll()
2836 DSTACK(__FUNCTION_NAME);
2837 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2839 loadMasterHeightmap();
2841 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2843 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2845 JMutexAutoLock lock(m_sector_mutex);
2848 s32 printed_counter = -100000;
2849 s32 count = list.size();
2851 std::vector<fs::DirListNode>::iterator i;
2852 for(i=list.begin(); i!=list.end(); i++)
2854 if(counter > printed_counter + 10)
2856 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2857 printed_counter = counter;
2861 MapSector *sector = NULL;
2863 // We want directories
2867 sector = loadSectorMeta(i->name);
2869 catch(InvalidFilenameException &e)
2871 // This catches unknown crap in directory
2874 if(ENABLE_BLOCK_LOADING)
2876 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2877 (m_savedir+"/sectors/"+i->name);
2878 std::vector<fs::DirListNode>::iterator i2;
2879 for(i2=list2.begin(); i2!=list2.end(); i2++)
2885 loadBlock(i->name, i2->name, sector);
2887 catch(InvalidFilenameException &e)
2889 // This catches unknown crap in directory
2894 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2897 void ServerMap::saveMasterHeightmap()
2899 DSTACK(__FUNCTION_NAME);
2900 createDir(m_savedir);
2902 std::string fullpath = m_savedir + "/master_heightmap";
2903 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2904 if(o.good() == false)
2905 throw FileNotGoodException("Cannot open master heightmap");
2907 // Format used for writing
2908 u8 version = SER_FMT_VER_HIGHEST;
2911 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2913 [0] u8 serialization version
2914 [1] X master heightmap
2916 u32 fullsize = 1 + hmdata.getSize();
2917 SharedBuffer<u8> data(fullsize);
2920 memcpy(&data[1], *hmdata, hmdata.getSize());
2922 o.write((const char*)*data, fullsize);
2925 m_heightmap->serialize(o, version);
2928 void ServerMap::loadMasterHeightmap()
2930 DSTACK(__FUNCTION_NAME);
2931 std::string fullpath = m_savedir + "/master_heightmap";
2932 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2933 if(is.good() == false)
2934 throw FileNotGoodException("Cannot open master heightmap");
2936 if(m_heightmap != NULL)
2939 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
2942 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2944 DSTACK(__FUNCTION_NAME);
2945 // Format used for writing
2946 u8 version = SER_FMT_VER_HIGHEST;
2948 v2s16 pos = sector->getPos();
2949 createDir(m_savedir);
2950 createDir(m_savedir+"/sectors");
2951 std::string dir = getSectorDir(pos);
2954 std::string fullpath = dir + "/heightmap";
2955 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2956 if(o.good() == false)
2957 throw FileNotGoodException("Cannot open master heightmap");
2959 sector->serialize(o, version);
2961 sector->differs_from_disk = false;
2964 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2966 DSTACK(__FUNCTION_NAME);
2968 v2s16 p2d = getSectorPos(dirname);
2969 std::string dir = m_savedir + "/sectors/" + dirname;
2971 std::string fullpath = dir + "/heightmap";
2972 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2973 if(is.good() == false)
2974 throw FileNotGoodException("Cannot open sector heightmap");
2976 ServerMapSector *sector = ServerMapSector::deSerialize
2977 (is, this, p2d, &m_hwrapper, m_sectors);
2979 sector->differs_from_disk = false;
2984 bool ServerMap::loadSectorFull(v2s16 p2d)
2986 DSTACK(__FUNCTION_NAME);
2987 std::string sectorsubdir = getSectorSubDir(p2d);
2989 MapSector *sector = NULL;
2991 JMutexAutoLock lock(m_sector_mutex);
2994 sector = loadSectorMeta(sectorsubdir);
2996 catch(InvalidFilenameException &e)
3000 catch(FileNotGoodException &e)
3004 catch(std::exception &e)
3009 if(ENABLE_BLOCK_LOADING)
3011 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3012 (m_savedir+"/sectors/"+sectorsubdir);
3013 std::vector<fs::DirListNode>::iterator i2;
3014 for(i2=list2.begin(); i2!=list2.end(); i2++)
3020 loadBlock(sectorsubdir, i2->name, sector);
3022 catch(InvalidFilenameException &e)
3024 // This catches unknown crap in directory
3032 bool ServerMap::deFlushSector(v2s16 p2d)
3034 DSTACK(__FUNCTION_NAME);
3035 // See if it already exists in memory
3037 MapSector *sector = getSectorNoGenerate(p2d);
3040 catch(InvalidPositionException &e)
3043 Try to load the sector from disk.
3045 if(loadSectorFull(p2d) == true)
3054 void ServerMap::saveBlock(MapBlock *block)
3056 DSTACK(__FUNCTION_NAME);
3058 Dummy blocks are not written
3060 if(block->isDummy())
3062 /*v3s16 p = block->getPos();
3063 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3064 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3068 // Format used for writing
3069 u8 version = SER_FMT_VER_HIGHEST;
3071 v3s16 p3d = block->getPos();
3072 v2s16 p2d(p3d.X, p3d.Z);
3073 createDir(m_savedir);
3074 createDir(m_savedir+"/sectors");
3075 std::string dir = getSectorDir(p2d);
3078 // Block file is map/sectors/xxxxxxxx/xxxx
3080 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
3081 std::string fullpath = dir + "/" + cc;
3082 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3083 if(o.good() == false)
3084 throw FileNotGoodException("Cannot open block data");
3087 [0] u8 serialization version
3090 o.write((char*)&version, 1);
3092 block->serialize(o, version);
3095 Versions up from 9 have block objects.
3099 block->serializeObjects(o, version);
3102 // We just wrote it to the disk
3103 block->resetChangedFlag();
3106 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
3108 DSTACK(__FUNCTION_NAME);
3112 // Block file is map/sectors/xxxxxxxx/xxxx
3113 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
3114 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3115 if(is.good() == false)
3116 throw FileNotGoodException("Cannot open block file");
3118 v3s16 p3d = getBlockPos(sectordir, blockfile);
3119 v2s16 p2d(p3d.X, p3d.Z);
3121 assert(sector->getPos() == p2d);
3123 u8 version = SER_FMT_VER_INVALID;
3124 is.read((char*)&version, 1);
3126 /*u32 block_size = MapBlock::serializedLength(version);
3127 SharedBuffer<u8> data(block_size);
3128 is.read((char*)*data, block_size);*/
3130 // This will always return a sector because we're the server
3131 //MapSector *sector = emergeSector(p2d);
3133 MapBlock *block = NULL;
3134 bool created_new = false;
3136 block = sector->getBlockNoCreate(p3d.Y);
3138 catch(InvalidPositionException &e)
3140 block = sector->createBlankBlockNoInsert(p3d.Y);
3144 // deserialize block data
3145 block->deSerialize(is, version);
3148 Versions up from 9 have block objects.
3152 block->updateObjects(is, version, NULL, 0);
3156 sector->insertBlock(block);
3159 Convert old formats to new and save
3162 // Save old format blocks in new format
3163 if(version < SER_FMT_VER_HIGHEST)
3168 // We just loaded it from the disk, so it's up-to-date.
3169 block->resetChangedFlag();
3172 catch(SerializationError &e)
3174 dstream<<"WARNING: Invalid block data on disk "
3175 "(SerializationError). Ignoring."
3180 // Gets from master heightmap
3181 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
3183 assert(m_heightmap != NULL);
3191 corners[0] = m_heightmap->getGroundHeight
3192 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
3193 corners[1] = m_heightmap->getGroundHeight
3194 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
3195 corners[2] = m_heightmap->getGroundHeight
3196 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
3197 corners[3] = m_heightmap->getGroundHeight
3198 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
3201 void ServerMap::PrintInfo(std::ostream &out)
3212 ClientMap::ClientMap(
3214 MapDrawControl &control,
3215 scene::ISceneNode* parent,
3216 scene::ISceneManager* mgr,
3220 scene::ISceneNode(parent, mgr, id),
3227 /*m_box = core::aabbox3d<f32>(0,0,0,
3228 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
3229 /*m_box = core::aabbox3d<f32>(0,0,0,
3230 map->getSizeNodes().X * BS,
3231 map->getSizeNodes().Y * BS,
3232 map->getSizeNodes().Z * BS);*/
3233 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3234 BS*1000000,BS*1000000,BS*1000000);
3236 //setPosition(v3f(BS,BS,BS));
3239 ClientMap::~ClientMap()
3241 JMutexAutoLock lock(mesh_mutex);
3250 MapSector * ClientMap::emergeSector(v2s16 p2d)
3252 DSTACK(__FUNCTION_NAME);
3253 // Check that it doesn't exist already
3255 return getSectorNoGenerate(p2d);
3257 catch(InvalidPositionException &e)
3261 // Create a sector with no heightmaps
3262 ClientMapSector *sector = new ClientMapSector(this, p2d);
3265 JMutexAutoLock lock(m_sector_mutex);
3266 m_sectors.insert(p2d, sector);
3272 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3274 DSTACK(__FUNCTION_NAME);
3275 ClientMapSector *sector = NULL;
3277 JMutexAutoLock lock(m_sector_mutex);
3279 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3283 sector = (ClientMapSector*)n->getValue();
3284 assert(sector->getId() == MAPSECTOR_CLIENT);
3288 sector = new ClientMapSector(this, p2d);
3290 JMutexAutoLock lock(m_sector_mutex);
3291 m_sectors.insert(p2d, sector);
3295 sector->deSerialize(is);
3298 void ClientMap::OnRegisterSceneNode()
3302 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3303 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3306 ISceneNode::OnRegisterSceneNode();
3309 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3311 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3312 DSTACK(__FUNCTION_NAME);
3314 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3317 Get time for measuring timeout.
3319 Measuring time is very useful for long delays when the
3320 machine is swapping a lot.
3322 int time1 = time(0);
3324 u32 daynight_ratio = m_client->getDayNightRatio();
3326 m_camera_mutex.Lock();
3327 v3f camera_position = m_camera_position;
3328 v3f camera_direction = m_camera_direction;
3329 m_camera_mutex.Unlock();
3332 Get all blocks and draw all visible ones
3335 v3s16 cam_pos_nodes(
3336 camera_position.X / BS,
3337 camera_position.Y / BS,
3338 camera_position.Z / BS);
3340 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3342 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3343 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3345 // Take a fair amount as we will be dropping more out later
3347 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3348 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3349 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3351 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3352 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3353 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3355 u32 vertex_count = 0;
3357 // For limiting number of mesh updates per frame
3358 u32 mesh_update_count = 0;
3360 u32 blocks_would_have_drawn = 0;
3361 u32 blocks_drawn = 0;
3363 //NOTE: The sectors map should be locked but we're not doing it
3364 // because it'd cause too much delays
3366 int timecheck_counter = 0;
3367 core::map<v2s16, MapSector*>::Iterator si;
3368 si = m_sectors.getIterator();
3369 for(; si.atEnd() == false; si++)
3372 timecheck_counter++;
3373 if(timecheck_counter > 50)
3375 int time2 = time(0);
3376 if(time2 > time1 + 4)
3378 dstream<<"ClientMap::renderMap(): "
3379 "Rendering takes ages, returning."
3386 MapSector *sector = si.getNode()->getValue();
3387 v2s16 sp = sector->getPos();
3389 if(m_control.range_all == false)
3391 if(sp.X < p_blocks_min.X
3392 || sp.X > p_blocks_max.X
3393 || sp.Y < p_blocks_min.Z
3394 || sp.Y > p_blocks_max.Z)
3398 core::list< MapBlock * > sectorblocks;
3399 sector->getBlocks(sectorblocks);
3405 core::list< MapBlock * >::Iterator i;
3406 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3408 MapBlock *block = *i;
3411 Compare block position to camera position, skip
3412 if not seen on display
3415 v3s16 blockpos_nodes = block->getPosRelative();
3417 // Block center position
3419 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3420 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3421 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3424 // Block position relative to camera
3425 v3f blockpos_relative = blockpos - camera_position;
3427 // Distance in camera direction (+=front, -=back)
3428 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3431 f32 d = blockpos_relative.getLength();
3433 if(m_control.range_all == false)
3435 // If block is far away, don't draw it
3436 if(d > m_control.wanted_range * BS)
3437 // This is nicer when fog is used
3438 //if((dforward+d)/2 > m_control.wanted_range * BS)
3442 // Maximum radius of a block
3443 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3445 // If block is (nearly) touching the camera, don't
3446 // bother validating further (that is, render it anyway)
3447 if(d > block_max_radius * 1.5)
3449 // Cosine of the angle between the camera direction
3450 // and the block direction (camera_direction is an unit vector)
3451 f32 cosangle = dforward / d;
3453 // Compensate for the size of the block
3454 // (as the block has to be shown even if it's a bit off FOV)
3455 // This is an estimate.
3456 cosangle += block_max_radius / dforward;
3458 // If block is not in the field of view, skip it
3459 //if(cosangle < cos(FOV_ANGLE/2))
3460 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3465 Draw the faces of the block
3468 bool mesh_expired = false;
3471 JMutexAutoLock lock(block->mesh_mutex);
3473 mesh_expired = block->getMeshExpired();
3475 // Mesh has not been expired and there is no mesh:
3476 // block has no content
3477 if(block->mesh == NULL && mesh_expired == false)
3481 f32 faraway = BS*50;
3482 //f32 faraway = m_control.wanted_range * BS;
3485 This has to be done with the mesh_mutex unlocked
3487 // Pretty random but this should work somewhat nicely
3488 if(mesh_expired && (
3489 (mesh_update_count < 3
3490 && (d < faraway || mesh_update_count < 2)
3493 (m_control.range_all && mesh_update_count < 20)
3496 /*if(mesh_expired && mesh_update_count < 6
3497 && (d < faraway || mesh_update_count < 3))*/
3499 mesh_update_count++;
3501 // Mesh has been expired: generate new mesh
3502 //block->updateMeshes(daynight_i);
3503 block->updateMesh(daynight_ratio);
3505 mesh_expired = false;
3509 Don't draw an expired mesh that is far away
3511 /*if(mesh_expired && d >= faraway)
3514 // Instead, delete it
3515 JMutexAutoLock lock(block->mesh_mutex);
3518 block->mesh->drop();
3521 // And continue to next block
3526 JMutexAutoLock lock(block->mesh_mutex);
3528 scene::SMesh *mesh = block->mesh;
3533 blocks_would_have_drawn++;
3534 if(blocks_drawn >= m_control.wanted_max_blocks
3535 && m_control.range_all == false
3536 && d > m_control.wanted_min_range * BS)
3540 u32 c = mesh->getMeshBufferCount();
3542 for(u32 i=0; i<c; i++)
3544 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3545 const video::SMaterial& material = buf->getMaterial();
3546 video::IMaterialRenderer* rnd =
3547 driver->getMaterialRenderer(material.MaterialType);
3548 bool transparent = (rnd && rnd->isTransparent());
3549 // Render transparent on transparent pass and likewise.
3550 if(transparent == is_transparent_pass)
3552 driver->setMaterial(buf->getMaterial());
3553 driver->drawMeshBuffer(buf);
3554 vertex_count += buf->getVertexCount();
3558 } // foreach sectorblocks
3561 m_control.blocks_drawn = blocks_drawn;
3562 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3564 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3565 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3568 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3571 Add it to all blocks touching it
3574 v3s16(0,0,0), // this
3575 v3s16(0,0,1), // back
3576 v3s16(0,1,0), // top
3577 v3s16(1,0,0), // right
3578 v3s16(0,0,-1), // front
3579 v3s16(0,-1,0), // bottom
3580 v3s16(-1,0,0), // left
3582 for(u16 i=0; i<7; i++)
3584 v3s16 p2 = p + dirs[i];
3585 // Block position of neighbor (or requested) node
3586 v3s16 blockpos = getNodeBlockPos(p2);
3587 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3588 if(blockref == NULL)
3590 // Relative position of requested node
3591 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3592 blockref->setTempMod(relpos, mod);
3594 return getNodeBlockPos(p);
3596 v3s16 ClientMap::clearTempMod(v3s16 p)
3599 v3s16(0,0,0), // this
3600 v3s16(0,0,1), // back
3601 v3s16(0,1,0), // top
3602 v3s16(1,0,0), // right
3603 v3s16(0,0,-1), // front
3604 v3s16(0,-1,0), // bottom
3605 v3s16(-1,0,0), // left
3607 for(u16 i=0; i<7; i++)
3609 v3s16 p2 = p + dirs[i];
3610 // Block position of neighbor (or requested) node
3611 v3s16 blockpos = getNodeBlockPos(p2);
3612 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3613 if(blockref == NULL)
3615 // Relative position of requested node
3616 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3617 blockref->clearTempMod(relpos);
3619 return getNodeBlockPos(p);
3622 void ClientMap::PrintInfo(std::ostream &out)
3633 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3638 MapVoxelManipulator::~MapVoxelManipulator()
3640 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3645 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3647 TimeTaker timer1("emerge", &emerge_time);
3649 // Units of these are MapBlocks
3650 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3651 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3653 VoxelArea block_area_nodes
3654 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3656 addArea(block_area_nodes);
3658 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3659 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3660 for(s32 x=p_min.X; x<=p_max.X; x++)
3663 core::map<v3s16, bool>::Node *n;
3664 n = m_loaded_blocks.find(p);
3668 bool block_data_inexistent = false;
3671 TimeTaker timer1("emerge load", &emerge_load_time);
3673 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3674 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3677 dstream<<std::endl;*/
3679 MapBlock *block = m_map->getBlockNoCreate(p);
3680 if(block->isDummy())
3681 block_data_inexistent = true;
3683 block->copyTo(*this);
3685 catch(InvalidPositionException &e)
3687 block_data_inexistent = true;
3690 if(block_data_inexistent)
3692 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3693 // Fill with VOXELFLAG_INEXISTENT
3694 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3695 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3697 s32 i = m_area.index(a.MinEdge.X,y,z);
3698 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3702 m_loaded_blocks.insert(p, true);
3705 //dstream<<"emerge done"<<std::endl;
3710 void MapVoxelManipulator::emerge(VoxelArea a)
3712 TimeTaker timer1("emerge", &emerge_time);
3714 v3s16 size = a.getExtent();
3716 VoxelArea padded = a;
3717 padded.pad(m_area.getExtent() / 4);
3720 for(s16 z=0; z<size.Z; z++)
3721 for(s16 y=0; y<size.Y; y++)
3722 for(s16 x=0; x<size.X; x++)
3725 s32 i = m_area.index(a.MinEdge + p);
3726 // Don't touch nodes that have already been loaded
3727 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3731 TimeTaker timer1("emerge load", &emerge_load_time);
3732 MapNode n = m_map->getNode(a.MinEdge + p);
3736 catch(InvalidPositionException &e)
3738 m_flags[i] = VOXELFLAG_INEXISTENT;
3746 TODO: Add an option to only update eg. water and air nodes.
3747 This will make it interfere less with important stuff if
3750 void MapVoxelManipulator::blitBack
3751 (core::map<v3s16, MapBlock*> & modified_blocks)
3753 if(m_area.getExtent() == v3s16(0,0,0))
3756 //TimeTaker timer1("blitBack");
3759 Initialize block cache
3761 v3s16 blockpos_last;
3762 MapBlock *block = NULL;
3763 bool block_checked_in_modified = false;
3765 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3766 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3767 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3771 u8 f = m_flags[m_area.index(p)];
3772 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3775 MapNode &n = m_data[m_area.index(p)];
3777 v3s16 blockpos = getNodeBlockPos(p);
3782 if(block == NULL || blockpos != blockpos_last){
3783 block = m_map->getBlockNoCreate(blockpos);
3784 blockpos_last = blockpos;
3785 block_checked_in_modified = false;
3788 // Calculate relative position in block
3789 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3791 // Don't continue if nothing has changed here
3792 if(block->getNode(relpos) == n)
3795 //m_map->setNode(m_area.MinEdge + p, n);
3796 block->setNode(relpos, n);
3799 Make sure block is in modified_blocks
3801 if(block_checked_in_modified == false)
3803 modified_blocks[blockpos] = block;
3804 block_checked_in_modified = true;
3807 catch(InvalidPositionException &e)