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.
1696 NOTE: This already sets the map as the parent of the block
1700 block = sector->createBlankBlockNoInsert(block_y);
1704 // Remove the block so that nobody can get a half-generated one.
1705 sector->removeBlock(block);
1706 // Allocate the block to contain the generated data
1710 u8 water_material = CONTENT_WATER;
1711 if(g_settings.getBool("endless_water"))
1712 water_material = CONTENT_OCEAN;
1714 s32 lowest_ground_y = 32767;
1715 s32 highest_ground_y = -32768;
1718 //sector->printHeightmaps();
1720 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1721 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1723 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1725 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1726 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1727 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1729 dstream<<"WARNING: Surface height not found in sector "
1730 "for block that is being emerged"<<std::endl;
1734 s16 surface_y = surface_y_f;
1735 //avg_ground_y += surface_y;
1736 if(surface_y < lowest_ground_y)
1737 lowest_ground_y = surface_y;
1738 if(surface_y > highest_ground_y)
1739 highest_ground_y = surface_y;
1741 s32 surface_depth = 0;
1743 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1745 //float min_slope = 0.45;
1746 //float max_slope = 0.85;
1747 float min_slope = 0.60;
1748 float max_slope = 1.20;
1749 float min_slope_depth = 5.0;
1750 float max_slope_depth = 0;
1752 if(slope < min_slope)
1753 surface_depth = min_slope_depth;
1754 else if(slope > max_slope)
1755 surface_depth = max_slope_depth;
1757 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1759 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1761 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1766 NOTE: If there are some man-made structures above the
1767 newly created block, they won't be taken into account.
1769 if(real_y > surface_y)
1770 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1776 // If node is over heightmap y, it's air or water
1777 if(real_y > surface_y)
1779 // If under water level, it's water
1780 if(real_y < WATER_LEVEL)
1782 n.d = water_material;
1783 n.setLight(LIGHTBANK_DAY,
1784 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1790 // Else it's ground or dungeons (air)
1793 // If it's surface_depth under ground, it's stone
1794 if(real_y <= surface_y - surface_depth)
1796 n.d = CONTENT_STONE;
1800 // It is mud if it is under the first ground
1801 // level or under water
1802 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
1808 n.d = CONTENT_GRASS;
1811 //n.d = CONTENT_MUD;
1813 /*// If under water level, it's mud
1814 if(real_y < WATER_LEVEL)
1816 // Only the topmost node is grass
1817 else if(real_y <= surface_y - 1)
1820 n.d = CONTENT_GRASS;*/
1824 block->setNode(v3s16(x0,y0,z0), n);
1829 Calculate some helper variables
1832 // Completely underground if the highest part of block is under lowest
1834 // This has to be very sure; it's probably one too strict now but
1835 // that's just better.
1836 bool completely_underground =
1837 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
1839 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
1845 // Initialize temporary table
1846 const s32 ued = MAP_BLOCKSIZE;
1847 bool underground_emptiness[ued*ued*ued];
1848 for(s32 i=0; i<ued*ued*ued; i++)
1850 underground_emptiness[i] = 0;
1856 Initialize orp and ors. Try to find if some neighboring
1857 MapBlock has a tunnel ended in its side
1861 (float)(myrand()%ued)+0.5,
1862 (float)(myrand()%ued)+0.5,
1863 (float)(myrand()%ued)+0.5
1866 bool found_existing = false;
1872 for(s16 y=0; y<ued; y++)
1873 for(s16 x=0; x<ued; x++)
1875 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1876 if(getNode(ap).d == CONTENT_AIR)
1878 orp = v3f(x+1,y+1,0);
1879 found_existing = true;
1880 goto continue_generating;
1884 catch(InvalidPositionException &e){}
1890 for(s16 y=0; y<ued; y++)
1891 for(s16 x=0; x<ued; x++)
1893 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1894 if(getNode(ap).d == CONTENT_AIR)
1896 orp = v3f(x+1,y+1,ued-1);
1897 found_existing = true;
1898 goto continue_generating;
1902 catch(InvalidPositionException &e){}
1908 for(s16 y=0; y<ued; y++)
1909 for(s16 z=0; z<ued; z++)
1911 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1912 if(getNode(ap).d == CONTENT_AIR)
1914 orp = v3f(0,y+1,z+1);
1915 found_existing = true;
1916 goto continue_generating;
1920 catch(InvalidPositionException &e){}
1926 for(s16 y=0; y<ued; y++)
1927 for(s16 z=0; z<ued; z++)
1929 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1930 if(getNode(ap).d == CONTENT_AIR)
1932 orp = v3f(ued-1,y+1,z+1);
1933 found_existing = true;
1934 goto continue_generating;
1938 catch(InvalidPositionException &e){}
1940 continue_generating:
1943 Don't always generate dungeon
1945 bool do_generate_dungeons = true;
1946 if(!some_part_underground)
1947 do_generate_dungeons = false;
1948 else if(!completely_underground)
1949 do_generate_dungeons = rand() % 5;
1950 else if(found_existing)
1951 do_generate_dungeons = true;
1953 do_generate_dungeons = rand() % 2;
1955 if(do_generate_dungeons)
1958 Generate some tunnel starting from orp and ors
1960 for(u16 i=0; i<3; i++)
1963 (float)(myrand()%ued)+0.5,
1964 (float)(myrand()%ued)+0.5,
1965 (float)(myrand()%ued)+0.5
1969 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
1973 for(float f=0; f<1.0; f+=0.04)
1975 v3f fp = orp + vec * f;
1976 v3s16 cp(fp.X, fp.Y, fp.Z);
1978 s16 d1 = d0 + rs - 1;
1979 for(s16 z0=d0; z0<=d1; z0++)
1981 s16 si = rs - abs(z0);
1982 for(s16 x0=-si; x0<=si-1; x0++)
1984 s16 si2 = rs - abs(x0);
1985 for(s16 y0=-si2+1; y0<=si2-1; y0++)
1991 if(isInArea(p, ued) == false)
1993 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2004 // Set to true if has caves.
2005 // Set when some non-air is changed to air when making caves.
2006 bool has_caves = false;
2009 Apply temporary cave data to block
2012 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2013 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2015 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2017 MapNode n = block->getNode(v3s16(x0,y0,z0));
2020 if(underground_emptiness[
2021 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2022 +ued*(y0*ued/MAP_BLOCKSIZE)
2023 +(x0*ued/MAP_BLOCKSIZE)])
2025 if(is_ground_content(n.d))
2034 block->setNode(v3s16(x0,y0,z0), n);
2039 This is used for guessing whether or not the block should
2040 receive sunlight from the top if the top block doesn't exist
2042 block->setIsUnderground(completely_underground);
2045 Force lighting update if some part of block is partly
2046 underground and has caves.
2048 /*if(some_part_underground && !completely_underground && has_caves)
2050 //dstream<<"Half-ground caves"<<std::endl;
2051 lighting_invalidated_blocks[block->getPos()] = block;
2054 // DEBUG: Always update lighting
2055 //lighting_invalidated_blocks[block->getPos()] = block;
2061 if(some_part_underground)
2063 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2068 for(s16 i=0; i< underground_level/4 + 1; i++)
2070 if(myrand()%10 == 0)
2073 (myrand()%(MAP_BLOCKSIZE-2))+1,
2074 (myrand()%(MAP_BLOCKSIZE-2))+1,
2075 (myrand()%(MAP_BLOCKSIZE-2))+1
2081 //if(is_ground_content(block->getNode(cp).d))
2082 if(block->getNode(cp).d == CONTENT_STONE)
2084 block->setNode(cp, n);
2086 for(u16 i=0; i<26; i++)
2088 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2089 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2091 block->setNode(cp+g_26dirs[i], n);
2099 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2100 u16 coal_rareness = 60 / coal_amount;
2101 if(coal_rareness == 0)
2103 if(myrand()%coal_rareness == 0)
2105 u16 a = myrand() % 16;
2106 u16 amount = coal_amount * a*a*a / 1000;
2107 for(s16 i=0; i<amount; i++)
2110 (myrand()%(MAP_BLOCKSIZE-2))+1,
2111 (myrand()%(MAP_BLOCKSIZE-2))+1,
2112 (myrand()%(MAP_BLOCKSIZE-2))+1
2116 n.d = CONTENT_COALSTONE;
2118 //dstream<<"Adding coalstone"<<std::endl;
2120 //if(is_ground_content(block->getNode(cp).d))
2121 if(block->getNode(cp).d == CONTENT_STONE)
2123 block->setNode(cp, n);
2125 for(u16 i=0; i<26; i++)
2127 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2128 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2130 block->setNode(cp+g_26dirs[i], n);
2137 Create a few rats in empty blocks underground
2139 if(completely_underground)
2141 //for(u16 i=0; i<2; i++)
2144 (myrand()%(MAP_BLOCKSIZE-2))+1,
2145 (myrand()%(MAP_BLOCKSIZE-2))+1,
2146 (myrand()%(MAP_BLOCKSIZE-2))+1
2149 // Check that the place is empty
2150 //if(!is_ground_content(block->getNode(cp).d))
2153 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2154 block->addObject(obj);
2160 Add block to sector.
2162 sector->insertBlock(block);
2168 // An y-wise container of changed blocks
2169 core::map<s16, MapBlock*> changed_blocks_sector;
2172 Check if any sector's objects can be placed now.
2175 core::map<v3s16, u8> *objects = sector->getObjects();
2176 core::list<v3s16> objects_to_remove;
2177 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2178 i.atEnd() == false; i++)
2180 v3s16 p = i.getNode()->getKey();
2182 u8 d = i.getNode()->getValue();
2184 // Ground level point (user for stuff that is on ground)
2186 bool ground_found = true;
2188 // Search real ground level
2192 MapNode n = sector->getNode(gp);
2194 // If not air, go one up and continue to placing the tree
2195 if(n.d != CONTENT_AIR)
2201 // If air, go one down
2202 gp += v3s16(0,-1,0);
2204 }catch(InvalidPositionException &e)
2206 // Ground not found.
2207 ground_found = false;
2208 // This is most close to ground
2215 if(d == SECTOR_OBJECT_TEST)
2217 if(sector->isValidArea(p + v3s16(0,0,0),
2218 p + v3s16(0,0,0), &changed_blocks_sector))
2221 n.d = CONTENT_TORCH;
2222 sector->setNode(p, n);
2223 objects_to_remove.push_back(p);
2226 else if(d == SECTOR_OBJECT_TREE_1)
2228 if(ground_found == false)
2231 v3s16 p_min = gp + v3s16(-1,0,-1);
2232 v3s16 p_max = gp + v3s16(1,5,1);
2233 if(sector->isValidArea(p_min, p_max,
2234 &changed_blocks_sector))
2238 sector->setNode(gp+v3s16(0,0,0), n);
2239 sector->setNode(gp+v3s16(0,1,0), n);
2240 sector->setNode(gp+v3s16(0,2,0), n);
2241 sector->setNode(gp+v3s16(0,3,0), n);
2243 n.d = CONTENT_LEAVES;
2245 if(rand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2247 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2248 if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2249 if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2250 if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2251 /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2252 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2253 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2254 if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2256 sector->setNode(gp+v3s16(0,4,0), n);
2258 sector->setNode(gp+v3s16(-1,4,0), n);
2259 sector->setNode(gp+v3s16(1,4,0), n);
2260 sector->setNode(gp+v3s16(0,4,-1), n);
2261 sector->setNode(gp+v3s16(0,4,1), n);
2262 sector->setNode(gp+v3s16(1,4,1), n);
2263 sector->setNode(gp+v3s16(-1,4,1), n);
2264 sector->setNode(gp+v3s16(-1,4,-1), n);
2265 sector->setNode(gp+v3s16(1,4,-1), n);
2267 sector->setNode(gp+v3s16(-1,3,0), n);
2268 sector->setNode(gp+v3s16(1,3,0), n);
2269 sector->setNode(gp+v3s16(0,3,-1), n);
2270 sector->setNode(gp+v3s16(0,3,1), n);
2271 sector->setNode(gp+v3s16(1,3,1), n);
2272 sector->setNode(gp+v3s16(-1,3,1), n);
2273 sector->setNode(gp+v3s16(-1,3,-1), n);
2274 sector->setNode(gp+v3s16(1,3,-1), n);
2276 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2277 if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2278 if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2279 if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2280 /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2281 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2282 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2283 if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2285 // Objects are identified by wanted position
2286 objects_to_remove.push_back(p);
2288 // Lighting has to be recalculated for this one.
2289 sector->getBlocksInArea(p_min, p_max,
2290 lighting_invalidated_blocks);
2293 else if(d == SECTOR_OBJECT_BUSH_1)
2295 if(ground_found == false)
2298 if(sector->isValidArea(gp + v3s16(0,0,0),
2299 gp + v3s16(0,0,0), &changed_blocks_sector))
2302 n.d = CONTENT_LEAVES;
2303 sector->setNode(gp+v3s16(0,0,0), n);
2305 // Objects are identified by wanted position
2306 objects_to_remove.push_back(p);
2309 else if(d == SECTOR_OBJECT_RAVINE)
2312 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2313 v3s16 p_max = p + v3s16(6,6,6);
2314 if(sector->isValidArea(p_min, p_max,
2315 &changed_blocks_sector))
2318 n.d = CONTENT_STONE;
2321 s16 depth = maxdepth + (myrand()%10);
2323 s16 minz = -6 - (-2);
2325 for(s16 x=-6; x<=6; x++)
2327 z += -1 + (myrand()%3);
2332 for(s16 y=depth+(myrand()%2); y<=6; y++)
2334 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2337 v3s16 p2 = p + v3s16(x,y,z-2);
2338 if(is_ground_content(sector->getNode(p2).d)
2339 && !is_mineral(sector->getNode(p2).d))
2340 sector->setNode(p2, n);
2343 v3s16 p2 = p + v3s16(x,y,z-1);
2344 if(is_ground_content(sector->getNode(p2).d)
2345 && !is_mineral(sector->getNode(p2).d))
2346 sector->setNode(p2, n2);
2349 v3s16 p2 = p + v3s16(x,y,z+0);
2350 if(is_ground_content(sector->getNode(p2).d)
2351 && !is_mineral(sector->getNode(p2).d))
2352 sector->setNode(p2, n2);
2355 v3s16 p2 = p + v3s16(x,y,z+1);
2356 if(is_ground_content(sector->getNode(p2).d)
2357 && !is_mineral(sector->getNode(p2).d))
2358 sector->setNode(p2, n);
2361 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2362 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2366 objects_to_remove.push_back(p);
2368 // Lighting has to be recalculated for this one.
2369 sector->getBlocksInArea(p_min, p_max,
2370 lighting_invalidated_blocks);
2375 dstream<<"ServerMap::emergeBlock(): "
2376 "Invalid heightmap object"
2381 catch(InvalidPositionException &e)
2383 dstream<<"WARNING: "<<__FUNCTION_NAME
2384 <<": while inserting object "<<(int)d
2385 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2386 <<" InvalidPositionException.what()="
2387 <<e.what()<<std::endl;
2388 // This is not too fatal and seems to happen sometimes.
2393 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2394 i != objects_to_remove.end(); i++)
2396 objects->remove(*i);
2400 Initially update sunlight
2404 core::map<v3s16, bool> light_sources;
2405 bool black_air_left = false;
2406 bool bottom_invalid =
2407 block->propagateSunlight(light_sources, true, &black_air_left);
2409 // If sunlight didn't reach everywhere and part of block is
2410 // above ground, lighting has to be properly updated
2411 if(black_air_left && some_part_underground)
2413 lighting_invalidated_blocks[block->getPos()] = block;
2418 Translate sector's changed blocks to global changed blocks
2421 for(core::map<s16, MapBlock*>::Iterator
2422 i = changed_blocks_sector.getIterator();
2423 i.atEnd() == false; i++)
2425 MapBlock *block = i.getNode()->getValue();
2427 changed_blocks.insert(block->getPos(), block);
2433 void ServerMap::createDir(std::string path)
2435 if(fs::CreateDir(path) == false)
2437 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2438 <<"\""<<path<<"\""<<std::endl;
2439 throw BaseException("ServerMap failed to create directory");
2443 std::string ServerMap::getSectorSubDir(v2s16 pos)
2446 snprintf(cc, 9, "%.4x%.4x",
2447 (unsigned int)pos.X&0xffff,
2448 (unsigned int)pos.Y&0xffff);
2450 return std::string(cc);
2453 std::string ServerMap::getSectorDir(v2s16 pos)
2455 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2458 v2s16 ServerMap::getSectorPos(std::string dirname)
2460 if(dirname.size() != 8)
2461 throw InvalidFilenameException("Invalid sector directory name");
2463 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2465 throw InvalidFilenameException("Invalid sector directory name");
2466 v2s16 pos((s16)x, (s16)y);
2470 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2472 v2s16 p2d = getSectorPos(sectordir);
2474 if(blockfile.size() != 4){
2475 throw InvalidFilenameException("Invalid block filename");
2478 int r = sscanf(blockfile.c_str(), "%4x", &y);
2480 throw InvalidFilenameException("Invalid block filename");
2481 return v3s16(p2d.X, y, p2d.Y);
2485 #define ENABLE_SECTOR_SAVING 1
2486 #define ENABLE_SECTOR_LOADING 1
2487 #define ENABLE_BLOCK_SAVING 1
2488 #define ENABLE_BLOCK_LOADING 1
2490 void ServerMap::save(bool only_changed)
2492 DSTACK(__FUNCTION_NAME);
2493 if(m_map_saving_enabled == false)
2495 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2499 if(only_changed == false)
2500 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2503 saveMasterHeightmap();
2505 u32 sector_meta_count = 0;
2506 u32 block_count = 0;
2509 JMutexAutoLock lock(m_sector_mutex);
2511 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2512 for(; i.atEnd() == false; i++)
2514 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2515 assert(sector->getId() == MAPSECTOR_SERVER);
2517 if(ENABLE_SECTOR_SAVING)
2519 if(sector->differs_from_disk || only_changed == false)
2521 saveSectorMeta(sector);
2522 sector_meta_count++;
2525 if(ENABLE_BLOCK_SAVING)
2527 core::list<MapBlock*> blocks;
2528 sector->getBlocks(blocks);
2529 core::list<MapBlock*>::Iterator j;
2530 for(j=blocks.begin(); j!=blocks.end(); j++)
2532 MapBlock *block = *j;
2533 if(block->getChangedFlag() || only_changed == false)
2545 Only print if something happened or saved whole map
2547 if(only_changed == false || sector_meta_count != 0
2548 || block_count != 0)
2550 dstream<<DTIME<<"ServerMap: Written: "
2551 <<sector_meta_count<<" sector metadata files, "
2552 <<block_count<<" block files"
2557 void ServerMap::loadAll()
2559 DSTACK(__FUNCTION_NAME);
2560 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2562 loadMasterHeightmap();
2564 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2566 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2568 JMutexAutoLock lock(m_sector_mutex);
2571 s32 printed_counter = -100000;
2572 s32 count = list.size();
2574 std::vector<fs::DirListNode>::iterator i;
2575 for(i=list.begin(); i!=list.end(); i++)
2577 if(counter > printed_counter + 10)
2579 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2580 printed_counter = counter;
2584 MapSector *sector = NULL;
2586 // We want directories
2590 sector = loadSectorMeta(i->name);
2592 catch(InvalidFilenameException &e)
2594 // This catches unknown crap in directory
2597 if(ENABLE_BLOCK_LOADING)
2599 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2600 (m_savedir+"/sectors/"+i->name);
2601 std::vector<fs::DirListNode>::iterator i2;
2602 for(i2=list2.begin(); i2!=list2.end(); i2++)
2608 loadBlock(i->name, i2->name, sector);
2610 catch(InvalidFilenameException &e)
2612 // This catches unknown crap in directory
2617 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2620 void ServerMap::saveMasterHeightmap()
2622 DSTACK(__FUNCTION_NAME);
2623 createDir(m_savedir);
2625 std::string fullpath = m_savedir + "/master_heightmap";
2626 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2627 if(o.good() == false)
2628 throw FileNotGoodException("Cannot open master heightmap");
2630 // Format used for writing
2631 u8 version = SER_FMT_VER_HIGHEST;
2634 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2636 [0] u8 serialization version
2637 [1] X master heightmap
2639 u32 fullsize = 1 + hmdata.getSize();
2640 SharedBuffer<u8> data(fullsize);
2643 memcpy(&data[1], *hmdata, hmdata.getSize());
2645 o.write((const char*)*data, fullsize);
2648 m_heightmap->serialize(o, version);
2651 void ServerMap::loadMasterHeightmap()
2653 DSTACK(__FUNCTION_NAME);
2654 std::string fullpath = m_savedir + "/master_heightmap";
2655 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2656 if(is.good() == false)
2657 throw FileNotGoodException("Cannot open master heightmap");
2659 if(m_heightmap != NULL)
2662 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2665 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2667 DSTACK(__FUNCTION_NAME);
2668 // Format used for writing
2669 u8 version = SER_FMT_VER_HIGHEST;
2671 v2s16 pos = sector->getPos();
2672 createDir(m_savedir);
2673 createDir(m_savedir+"/sectors");
2674 std::string dir = getSectorDir(pos);
2677 std::string fullpath = dir + "/heightmap";
2678 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2679 if(o.good() == false)
2680 throw FileNotGoodException("Cannot open master heightmap");
2682 sector->serialize(o, version);
2684 sector->differs_from_disk = false;
2687 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2689 DSTACK(__FUNCTION_NAME);
2691 v2s16 p2d = getSectorPos(dirname);
2692 std::string dir = m_savedir + "/sectors/" + dirname;
2694 std::string fullpath = dir + "/heightmap";
2695 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2696 if(is.good() == false)
2697 throw FileNotGoodException("Cannot open sector heightmap");
2699 ServerMapSector *sector = ServerMapSector::deSerialize
2700 (is, this, p2d, &m_hwrapper, m_sectors);
2702 sector->differs_from_disk = false;
2707 bool ServerMap::loadSectorFull(v2s16 p2d)
2709 DSTACK(__FUNCTION_NAME);
2710 std::string sectorsubdir = getSectorSubDir(p2d);
2712 MapSector *sector = NULL;
2714 JMutexAutoLock lock(m_sector_mutex);
2717 sector = loadSectorMeta(sectorsubdir);
2719 catch(InvalidFilenameException &e)
2723 catch(FileNotGoodException &e)
2727 catch(std::exception &e)
2732 if(ENABLE_BLOCK_LOADING)
2734 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2735 (m_savedir+"/sectors/"+sectorsubdir);
2736 std::vector<fs::DirListNode>::iterator i2;
2737 for(i2=list2.begin(); i2!=list2.end(); i2++)
2743 loadBlock(sectorsubdir, i2->name, sector);
2745 catch(InvalidFilenameException &e)
2747 // This catches unknown crap in directory
2755 bool ServerMap::deFlushSector(v2s16 p2d)
2757 DSTACK(__FUNCTION_NAME);
2758 // See if it already exists in memory
2760 MapSector *sector = getSectorNoGenerate(p2d);
2763 catch(InvalidPositionException &e)
2766 Try to load the sector from disk.
2768 if(loadSectorFull(p2d) == true)
2777 void ServerMap::saveBlock(MapBlock *block)
2779 DSTACK(__FUNCTION_NAME);
2781 Dummy blocks are not written
2783 if(block->isDummy())
2785 /*v3s16 p = block->getPos();
2786 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2787 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2791 // Format used for writing
2792 u8 version = SER_FMT_VER_HIGHEST;
2794 v3s16 p3d = block->getPos();
2795 v2s16 p2d(p3d.X, p3d.Z);
2796 createDir(m_savedir);
2797 createDir(m_savedir+"/sectors");
2798 std::string dir = getSectorDir(p2d);
2801 // Block file is map/sectors/xxxxxxxx/xxxx
2803 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2804 std::string fullpath = dir + "/" + cc;
2805 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2806 if(o.good() == false)
2807 throw FileNotGoodException("Cannot open block data");
2810 [0] u8 serialization version
2813 o.write((char*)&version, 1);
2815 block->serialize(o, version);
2818 Versions up from 9 have block objects.
2822 block->serializeObjects(o, version);
2825 // We just wrote it to the disk
2826 block->resetChangedFlag();
2829 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2831 DSTACK(__FUNCTION_NAME);
2835 // Block file is map/sectors/xxxxxxxx/xxxx
2836 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2837 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2838 if(is.good() == false)
2839 throw FileNotGoodException("Cannot open block file");
2841 v3s16 p3d = getBlockPos(sectordir, blockfile);
2842 v2s16 p2d(p3d.X, p3d.Z);
2844 assert(sector->getPos() == p2d);
2846 u8 version = SER_FMT_VER_INVALID;
2847 is.read((char*)&version, 1);
2849 /*u32 block_size = MapBlock::serializedLength(version);
2850 SharedBuffer<u8> data(block_size);
2851 is.read((char*)*data, block_size);*/
2853 // This will always return a sector because we're the server
2854 //MapSector *sector = emergeSector(p2d);
2856 MapBlock *block = NULL;
2857 bool created_new = false;
2859 block = sector->getBlockNoCreate(p3d.Y);
2861 catch(InvalidPositionException &e)
2863 block = sector->createBlankBlockNoInsert(p3d.Y);
2867 // deserialize block data
2868 block->deSerialize(is, version);
2871 Versions up from 9 have block objects.
2875 block->updateObjects(is, version, NULL, 0);
2879 sector->insertBlock(block);
2882 Convert old formats to new and save
2885 // Save old format blocks in new format
2886 if(version < SER_FMT_VER_HIGHEST)
2891 // We just loaded it from the disk, so it's up-to-date.
2892 block->resetChangedFlag();
2895 catch(SerializationError &e)
2897 dstream<<"WARNING: Invalid block data on disk "
2898 "(SerializationError). Ignoring."
2903 // Gets from master heightmap
2904 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2906 assert(m_heightmap != NULL);
2914 corners[0] = m_heightmap->getGroundHeight
2915 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2916 corners[1] = m_heightmap->getGroundHeight
2917 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2918 corners[2] = m_heightmap->getGroundHeight
2919 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2920 corners[3] = m_heightmap->getGroundHeight
2921 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2924 void ServerMap::PrintInfo(std::ostream &out)
2935 ClientMap::ClientMap(
2937 MapDrawControl &control,
2938 scene::ISceneNode* parent,
2939 scene::ISceneManager* mgr,
2943 scene::ISceneNode(parent, mgr, id),
2950 /*m_box = core::aabbox3d<f32>(0,0,0,
2951 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2952 /*m_box = core::aabbox3d<f32>(0,0,0,
2953 map->getSizeNodes().X * BS,
2954 map->getSizeNodes().Y * BS,
2955 map->getSizeNodes().Z * BS);*/
2956 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2957 BS*1000000,BS*1000000,BS*1000000);
2959 //setPosition(v3f(BS,BS,BS));
2962 ClientMap::~ClientMap()
2964 JMutexAutoLock lock(mesh_mutex);
2973 MapSector * ClientMap::emergeSector(v2s16 p2d)
2975 DSTACK(__FUNCTION_NAME);
2976 // Check that it doesn't exist already
2978 return getSectorNoGenerate(p2d);
2980 catch(InvalidPositionException &e)
2984 // Create a sector with no heightmaps
2985 ClientMapSector *sector = new ClientMapSector(this, p2d);
2988 JMutexAutoLock lock(m_sector_mutex);
2989 m_sectors.insert(p2d, sector);
2995 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2997 DSTACK(__FUNCTION_NAME);
2998 ClientMapSector *sector = NULL;
3000 JMutexAutoLock lock(m_sector_mutex);
3002 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3006 sector = (ClientMapSector*)n->getValue();
3007 assert(sector->getId() == MAPSECTOR_CLIENT);
3011 sector = new ClientMapSector(this, p2d);
3013 JMutexAutoLock lock(m_sector_mutex);
3014 m_sectors.insert(p2d, sector);
3018 sector->deSerialize(is);
3021 void ClientMap::OnRegisterSceneNode()
3025 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3026 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3029 ISceneNode::OnRegisterSceneNode();
3032 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3034 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3035 DSTACK(__FUNCTION_NAME);
3037 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3040 Get time for measuring timeout.
3042 Measuring time is very useful for long delays when the
3043 machine is swapping a lot.
3045 int time1 = time(0);
3047 u32 daynight_ratio = m_client->getDayNightRatio();
3049 m_camera_mutex.Lock();
3050 v3f camera_position = m_camera_position;
3051 v3f camera_direction = m_camera_direction;
3052 m_camera_mutex.Unlock();
3055 Get all blocks and draw all visible ones
3058 v3s16 cam_pos_nodes(
3059 camera_position.X / BS,
3060 camera_position.Y / BS,
3061 camera_position.Z / BS);
3063 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3065 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3066 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3068 // Take a fair amount as we will be dropping more out later
3070 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3071 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3072 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3074 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3075 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3076 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3078 u32 vertex_count = 0;
3080 // For limiting number of mesh updates per frame
3081 u32 mesh_update_count = 0;
3083 u32 blocks_would_have_drawn = 0;
3084 u32 blocks_drawn = 0;
3086 //NOTE: The sectors map should be locked but we're not doing it
3087 // because it'd cause too much delays
3089 int timecheck_counter = 0;
3090 core::map<v2s16, MapSector*>::Iterator si;
3091 si = m_sectors.getIterator();
3092 for(; si.atEnd() == false; si++)
3095 timecheck_counter++;
3096 if(timecheck_counter > 50)
3098 int time2 = time(0);
3099 if(time2 > time1 + 4)
3101 dstream<<"ClientMap::renderMap(): "
3102 "Rendering takes ages, returning."
3109 MapSector *sector = si.getNode()->getValue();
3110 v2s16 sp = sector->getPos();
3112 if(m_control.range_all == false)
3114 if(sp.X < p_blocks_min.X
3115 || sp.X > p_blocks_max.X
3116 || sp.Y < p_blocks_min.Z
3117 || sp.Y > p_blocks_max.Z)
3121 core::list< MapBlock * > sectorblocks;
3122 sector->getBlocks(sectorblocks);
3128 core::list< MapBlock * >::Iterator i;
3129 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3131 MapBlock *block = *i;
3134 Compare block position to camera position, skip
3135 if not seen on display
3138 v3s16 blockpos_nodes = block->getPosRelative();
3140 // Block center position
3142 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3143 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3144 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3147 // Block position relative to camera
3148 v3f blockpos_relative = blockpos - camera_position;
3150 // Distance in camera direction (+=front, -=back)
3151 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3154 f32 d = blockpos_relative.getLength();
3156 if(m_control.range_all == false)
3158 // If block is far away, don't draw it
3159 if(d > m_control.wanted_range * BS)
3160 // This is nicer when fog is used
3161 //if((dforward+d)/2 > m_control.wanted_range * BS)
3165 // Maximum radius of a block
3166 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3168 // If block is (nearly) touching the camera, don't
3169 // bother validating further (that is, render it anyway)
3170 if(d > block_max_radius * 1.5)
3172 // Cosine of the angle between the camera direction
3173 // and the block direction (camera_direction is an unit vector)
3174 f32 cosangle = dforward / d;
3176 // Compensate for the size of the block
3177 // (as the block has to be shown even if it's a bit off FOV)
3178 // This is an estimate.
3179 cosangle += block_max_radius / dforward;
3181 // If block is not in the field of view, skip it
3182 //if(cosangle < cos(FOV_ANGLE/2))
3183 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3188 Draw the faces of the block
3191 bool mesh_expired = false;
3194 JMutexAutoLock lock(block->mesh_mutex);
3196 mesh_expired = block->getMeshExpired();
3198 // Mesh has not been expired and there is no mesh:
3199 // block has no content
3200 if(block->mesh == NULL && mesh_expired == false)
3204 f32 faraway = BS*50;
3205 //f32 faraway = m_control.wanted_range * BS;
3208 This has to be done with the mesh_mutex unlocked
3210 // Pretty random but this should work somewhat nicely
3211 if(mesh_expired && (
3212 (mesh_update_count < 3
3213 && (d < faraway || mesh_update_count < 2)
3216 (m_control.range_all && mesh_update_count < 20)
3219 /*if(mesh_expired && mesh_update_count < 6
3220 && (d < faraway || mesh_update_count < 3))*/
3222 mesh_update_count++;
3224 // Mesh has been expired: generate new mesh
3225 //block->updateMeshes(daynight_i);
3226 block->updateMesh(daynight_ratio);
3228 mesh_expired = false;
3232 Don't draw an expired mesh that is far away
3234 /*if(mesh_expired && d >= faraway)
3237 // Instead, delete it
3238 JMutexAutoLock lock(block->mesh_mutex);
3241 block->mesh->drop();
3244 // And continue to next block
3249 JMutexAutoLock lock(block->mesh_mutex);
3251 scene::SMesh *mesh = block->mesh;
3256 blocks_would_have_drawn++;
3257 if(blocks_drawn >= m_control.wanted_max_blocks
3258 && m_control.range_all == false
3259 && d > m_control.wanted_min_range * BS)
3263 u32 c = mesh->getMeshBufferCount();
3265 for(u32 i=0; i<c; i++)
3267 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3268 const video::SMaterial& material = buf->getMaterial();
3269 video::IMaterialRenderer* rnd =
3270 driver->getMaterialRenderer(material.MaterialType);
3271 bool transparent = (rnd && rnd->isTransparent());
3272 // Render transparent on transparent pass and likewise.
3273 if(transparent == is_transparent_pass)
3275 driver->setMaterial(buf->getMaterial());
3276 driver->drawMeshBuffer(buf);
3277 vertex_count += buf->getVertexCount();
3281 } // foreach sectorblocks
3284 m_control.blocks_drawn = blocks_drawn;
3285 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3287 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3288 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3291 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3294 Add it to all blocks touching it
3297 v3s16(0,0,0), // this
3298 v3s16(0,0,1), // back
3299 v3s16(0,1,0), // top
3300 v3s16(1,0,0), // right
3301 v3s16(0,0,-1), // front
3302 v3s16(0,-1,0), // bottom
3303 v3s16(-1,0,0), // left
3305 for(u16 i=0; i<7; i++)
3307 v3s16 p2 = p + dirs[i];
3308 // Block position of neighbor (or requested) node
3309 v3s16 blockpos = getNodeBlockPos(p2);
3310 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3311 if(blockref == NULL)
3313 // Relative position of requested node
3314 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3315 blockref->setTempMod(relpos, mod);
3317 return getNodeBlockPos(p);
3319 v3s16 ClientMap::clearTempMod(v3s16 p)
3322 v3s16(0,0,0), // this
3323 v3s16(0,0,1), // back
3324 v3s16(0,1,0), // top
3325 v3s16(1,0,0), // right
3326 v3s16(0,0,-1), // front
3327 v3s16(0,-1,0), // bottom
3328 v3s16(-1,0,0), // left
3330 for(u16 i=0; i<7; i++)
3332 v3s16 p2 = p + dirs[i];
3333 // Block position of neighbor (or requested) node
3334 v3s16 blockpos = getNodeBlockPos(p2);
3335 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3336 if(blockref == NULL)
3338 // Relative position of requested node
3339 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3340 blockref->clearTempMod(relpos);
3342 return getNodeBlockPos(p);
3345 void ClientMap::PrintInfo(std::ostream &out)
3356 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3361 MapVoxelManipulator::~MapVoxelManipulator()
3363 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3368 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3370 TimeTaker timer1("emerge", &emerge_time);
3372 // Units of these are MapBlocks
3373 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3374 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3376 VoxelArea block_area_nodes
3377 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3379 addArea(block_area_nodes);
3381 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3382 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3383 for(s32 x=p_min.X; x<=p_max.X; x++)
3386 core::map<v3s16, bool>::Node *n;
3387 n = m_loaded_blocks.find(p);
3391 bool block_data_inexistent = false;
3394 TimeTaker timer1("emerge load", &emerge_load_time);
3396 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3397 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3400 dstream<<std::endl;*/
3402 MapBlock *block = m_map->getBlockNoCreate(p);
3403 if(block->isDummy())
3404 block_data_inexistent = true;
3406 block->copyTo(*this);
3408 catch(InvalidPositionException &e)
3410 block_data_inexistent = true;
3413 if(block_data_inexistent)
3415 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3416 // Fill with VOXELFLAG_INEXISTENT
3417 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3418 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3420 s32 i = m_area.index(a.MinEdge.X,y,z);
3421 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3425 m_loaded_blocks.insert(p, true);
3428 //dstream<<"emerge done"<<std::endl;
3433 void MapVoxelManipulator::emerge(VoxelArea a)
3435 TimeTaker timer1("emerge", &emerge_time);
3437 v3s16 size = a.getExtent();
3439 VoxelArea padded = a;
3440 padded.pad(m_area.getExtent() / 4);
3443 for(s16 z=0; z<size.Z; z++)
3444 for(s16 y=0; y<size.Y; y++)
3445 for(s16 x=0; x<size.X; x++)
3448 s32 i = m_area.index(a.MinEdge + p);
3449 // Don't touch nodes that have already been loaded
3450 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3454 TimeTaker timer1("emerge load", &emerge_load_time);
3455 MapNode n = m_map->getNode(a.MinEdge + p);
3459 catch(InvalidPositionException &e)
3461 m_flags[i] = VOXELFLAG_INEXISTENT;
3469 TODO: Add an option to only update eg. water and air nodes.
3470 This will make it interfere less with important stuff if
3473 void MapVoxelManipulator::blitBack
3474 (core::map<v3s16, MapBlock*> & modified_blocks)
3476 if(m_area.getExtent() == v3s16(0,0,0))
3479 //TimeTaker timer1("blitBack");
3482 Initialize block cache
3484 v3s16 blockpos_last;
3485 MapBlock *block = NULL;
3486 bool block_checked_in_modified = false;
3488 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3489 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3490 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3494 u8 f = m_flags[m_area.index(p)];
3495 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3498 MapNode &n = m_data[m_area.index(p)];
3500 v3s16 blockpos = getNodeBlockPos(p);
3505 if(block == NULL || blockpos != blockpos_last){
3506 block = m_map->getBlockNoCreate(blockpos);
3507 blockpos_last = blockpos;
3508 block_checked_in_modified = false;
3511 // Calculate relative position in block
3512 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3514 // Don't continue if nothing has changed here
3515 if(block->getNode(relpos) == n)
3518 //m_map->setNode(m_area.MinEdge + p, n);
3519 block->setNode(relpos, n);
3522 Make sure block is in modified_blocks
3524 if(block_checked_in_modified == false)
3526 modified_blocks[blockpos] = block;
3527 block_checked_in_modified = true;
3530 catch(InvalidPositionException &e)