3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "jmutexautolock.h"
33 Map::Map(std::ostream &dout):
35 m_camera_position(0,0,0),
36 m_camera_direction(0,0,1),
41 m_sector_mutex.Init();
42 m_camera_mutex.Init();
43 assert(m_sector_mutex.IsInitialized());
44 assert(m_camera_mutex.IsInitialized());
46 // Get this so that the player can stay on it at first
47 //getSector(v2s16(0,0));
55 /*updater.setRun(false);
56 while(updater.IsRunning())
62 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
63 for(; i.atEnd() == false; i++)
65 MapSector *sector = i.getNode()->getValue();
70 MapSector * Map::getSectorNoGenerate(v2s16 p)
72 JMutexAutoLock lock(m_sector_mutex);
74 if(m_sector_cache != NULL && p == m_sector_cache_p){
75 MapSector * sector = m_sector_cache;
76 // Reset inactivity timer
77 sector->usage_timer = 0.0;
81 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
82 // If sector doesn't exist, throw an exception
85 throw InvalidPositionException();
88 MapSector *sector = n->getValue();
90 // Cache the last result
92 m_sector_cache = sector;
94 //MapSector * ref(sector);
96 // Reset inactivity timer
97 sector->usage_timer = 0.0;
101 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
103 v2s16 p2d(p3d.X, p3d.Z);
104 MapSector * sector = getSectorNoGenerate(p2d);
106 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
111 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
115 v2s16 p2d(p3d.X, p3d.Z);
116 MapSector * sector = getSectorNoGenerate(p2d);
117 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
120 catch(InvalidPositionException &e)
126 f32 Map::getGroundHeight(v2s16 p, bool generate)
129 v2s16 sectorpos = getNodeSectorPos(p);
130 MapSector * sref = getSectorNoGenerate(sectorpos);
131 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
132 f32 y = sref->getGroundHeight(relpos);
135 catch(InvalidPositionException &e)
137 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
141 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
143 /*m_dout<<DTIME<<"Map::setGroundHeight(("
145 <<"), "<<y<<")"<<std::endl;*/
146 v2s16 sectorpos = getNodeSectorPos(p);
147 MapSector * sref = getSectorNoGenerate(sectorpos);
148 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
149 //sref->mutex.Lock();
150 sref->setGroundHeight(relpos, y);
151 //sref->mutex.Unlock();
154 bool Map::isNodeUnderground(v3s16 p)
156 v3s16 blockpos = getNodeBlockPos(p);
158 MapBlock * block = getBlockNoCreate(blockpos);
159 return block->getIsUnderground();
161 catch(InvalidPositionException &e)
168 Goes recursively through the neighbours of the node.
170 Alters only transparent nodes.
172 If the lighting of the neighbour is lower than the lighting of
173 the node was (before changing it to 0 at the step before), the
174 lighting of the neighbour is set to 0 and then the same stuff
175 repeats for the neighbour.
177 The ending nodes of the routine are stored in light_sources.
178 This is useful when a light is removed. In such case, this
179 routine can be called for the light node and then again for
180 light_sources to re-light the area without the removed light.
182 values of from_nodes are lighting values.
184 void Map::unspreadLight(enum LightBank bank,
185 core::map<v3s16, u8> & from_nodes,
186 core::map<v3s16, bool> & light_sources,
187 core::map<v3s16, MapBlock*> & modified_blocks)
190 v3s16(0,0,1), // back
192 v3s16(1,0,0), // right
193 v3s16(0,0,-1), // front
194 v3s16(0,-1,0), // bottom
195 v3s16(-1,0,0), // left
198 if(from_nodes.size() == 0)
201 u32 blockchangecount = 0;
203 core::map<v3s16, u8> unlighted_nodes;
204 core::map<v3s16, u8>::Iterator j;
205 j = from_nodes.getIterator();
208 Initialize block cache
211 MapBlock *block = NULL;
212 // Cache this a bit, too
213 bool block_checked_in_modified = false;
215 for(; j.atEnd() == false; j++)
217 v3s16 pos = j.getNode()->getKey();
218 v3s16 blockpos = getNodeBlockPos(pos);
220 // Only fetch a new block if the block position has changed
222 if(block == NULL || blockpos != blockpos_last){
223 block = getBlockNoCreate(blockpos);
224 blockpos_last = blockpos;
226 block_checked_in_modified = false;
230 catch(InvalidPositionException &e)
238 // Calculate relative position in block
239 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
241 // Get node straight from the block
242 MapNode n = block->getNode(relpos);
244 u8 oldlight = j.getNode()->getValue();
246 // Loop through 6 neighbors
247 for(u16 i=0; i<6; i++)
249 // Get the position of the neighbor node
250 v3s16 n2pos = pos + dirs[i];
252 // Get the block where the node is located
253 v3s16 blockpos = getNodeBlockPos(n2pos);
257 // Only fetch a new block if the block position has changed
259 if(block == NULL || blockpos != blockpos_last){
260 block = getBlockNoCreate(blockpos);
261 blockpos_last = blockpos;
263 block_checked_in_modified = false;
267 catch(InvalidPositionException &e)
272 // Calculate relative position in block
273 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
274 // Get node straight from the block
275 MapNode n2 = block->getNode(relpos);
277 bool changed = false;
279 //TODO: Optimize output by optimizing light_sources?
282 If the neighbor is dimmer than what was specified
283 as oldlight (the light of the previous node)
285 if(n2.getLight(bank) < oldlight)
288 And the neighbor is transparent and it has some light
290 if(n2.light_propagates() && n2.getLight(bank) != 0)
293 Set light to 0 and add to queue
296 u8 current_light = n2.getLight(bank);
297 n2.setLight(bank, 0);
298 block->setNode(relpos, n2);
300 unlighted_nodes.insert(n2pos, current_light);
304 Remove from light_sources if it is there
305 NOTE: This doesn't happen nearly at all
307 /*if(light_sources.find(n2pos))
309 std::cout<<"Removed from light_sources"<<std::endl;
310 light_sources.remove(n2pos);
315 if(light_sources.find(n2pos) != NULL)
316 light_sources.remove(n2pos);*/
319 light_sources.insert(n2pos, true);
322 // Add to modified_blocks
323 if(changed == true && block_checked_in_modified == false)
325 // If the block is not found in modified_blocks, add.
326 if(modified_blocks.find(blockpos) == NULL)
328 modified_blocks.insert(blockpos, block);
330 block_checked_in_modified = true;
333 catch(InvalidPositionException &e)
340 /*dstream<<"unspreadLight(): Changed block "
341 <<blockchangecount<<" times"
342 <<" for "<<from_nodes.size()<<" nodes"
345 if(unlighted_nodes.size() > 0)
346 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
350 A single-node wrapper of the above
352 void Map::unLightNeighbors(enum LightBank bank,
353 v3s16 pos, u8 lightwas,
354 core::map<v3s16, bool> & light_sources,
355 core::map<v3s16, MapBlock*> & modified_blocks)
357 core::map<v3s16, u8> from_nodes;
358 from_nodes.insert(pos, lightwas);
360 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
364 Lights neighbors of from_nodes, collects all them and then
367 void Map::spreadLight(enum LightBank bank,
368 core::map<v3s16, bool> & from_nodes,
369 core::map<v3s16, MapBlock*> & modified_blocks)
371 const v3s16 dirs[6] = {
372 v3s16(0,0,1), // back
374 v3s16(1,0,0), // right
375 v3s16(0,0,-1), // front
376 v3s16(0,-1,0), // bottom
377 v3s16(-1,0,0), // left
380 if(from_nodes.size() == 0)
383 u32 blockchangecount = 0;
385 core::map<v3s16, bool> lighted_nodes;
386 core::map<v3s16, bool>::Iterator j;
387 j = from_nodes.getIterator();
390 Initialize block cache
393 MapBlock *block = NULL;
394 // Cache this a bit, too
395 bool block_checked_in_modified = false;
397 for(; j.atEnd() == false; j++)
398 //for(; j != from_nodes.end(); j++)
400 v3s16 pos = j.getNode()->getKey();
402 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
403 v3s16 blockpos = getNodeBlockPos(pos);
405 // Only fetch a new block if the block position has changed
407 if(block == NULL || blockpos != blockpos_last){
408 block = getBlockNoCreate(blockpos);
409 blockpos_last = blockpos;
411 block_checked_in_modified = false;
415 catch(InvalidPositionException &e)
423 // Calculate relative position in block
424 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
426 // Get node straight from the block
427 MapNode n = block->getNode(relpos);
429 u8 oldlight = n.getLight(bank);
430 u8 newlight = diminish_light(oldlight);
432 // Loop through 6 neighbors
433 for(u16 i=0; i<6; i++){
434 // Get the position of the neighbor node
435 v3s16 n2pos = pos + dirs[i];
437 // Get the block where the node is located
438 v3s16 blockpos = getNodeBlockPos(n2pos);
442 // Only fetch a new block if the block position has changed
444 if(block == NULL || blockpos != blockpos_last){
445 block = getBlockNoCreate(blockpos);
446 blockpos_last = blockpos;
448 block_checked_in_modified = false;
452 catch(InvalidPositionException &e)
457 // Calculate relative position in block
458 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
459 // Get node straight from the block
460 MapNode n2 = block->getNode(relpos);
462 bool changed = false;
464 If the neighbor is brighter than the current node,
465 add to list (it will light up this node on its turn)
467 if(n2.getLight(bank) > undiminish_light(oldlight))
469 lighted_nodes.insert(n2pos, true);
470 //lighted_nodes.push_back(n2pos);
474 If the neighbor is dimmer than how much light this node
475 would spread on it, add to list
477 if(n2.getLight(bank) < newlight)
479 if(n2.light_propagates())
481 n2.setLight(bank, newlight);
482 block->setNode(relpos, n2);
483 lighted_nodes.insert(n2pos, true);
484 //lighted_nodes.push_back(n2pos);
489 // Add to modified_blocks
490 if(changed == true && block_checked_in_modified == false)
492 // If the block is not found in modified_blocks, add.
493 if(modified_blocks.find(blockpos) == NULL)
495 modified_blocks.insert(blockpos, block);
497 block_checked_in_modified = true;
500 catch(InvalidPositionException &e)
507 /*dstream<<"spreadLight(): Changed block "
508 <<blockchangecount<<" times"
509 <<" for "<<from_nodes.size()<<" nodes"
512 if(lighted_nodes.size() > 0)
513 spreadLight(bank, lighted_nodes, modified_blocks);
517 A single-node source variation of the above.
519 void Map::lightNeighbors(enum LightBank bank,
521 core::map<v3s16, MapBlock*> & modified_blocks)
523 core::map<v3s16, bool> from_nodes;
524 from_nodes.insert(pos, true);
525 spreadLight(bank, from_nodes, modified_blocks);
528 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
531 v3s16(0,0,1), // back
533 v3s16(1,0,0), // right
534 v3s16(0,0,-1), // front
535 v3s16(0,-1,0), // bottom
536 v3s16(-1,0,0), // left
539 u8 brightest_light = 0;
540 v3s16 brightest_pos(0,0,0);
541 bool found_something = false;
543 // Loop through 6 neighbors
544 for(u16 i=0; i<6; i++){
545 // Get the position of the neighbor node
546 v3s16 n2pos = p + dirs[i];
551 catch(InvalidPositionException &e)
555 if(n2.getLight(bank) > brightest_light || found_something == false){
556 brightest_light = n2.getLight(bank);
557 brightest_pos = n2pos;
558 found_something = true;
562 if(found_something == false)
563 throw InvalidPositionException();
565 return brightest_pos;
569 Propagates sunlight down from a node.
570 Starting point gets sunlight.
572 Returns the lowest y value of where the sunlight went.
574 Mud is turned into grass in where the sunlight stops.
576 s16 Map::propagateSunlight(v3s16 start,
577 core::map<v3s16, MapBlock*> & modified_blocks)
582 v3s16 pos(start.X, y, start.Z);
584 v3s16 blockpos = getNodeBlockPos(pos);
587 block = getBlockNoCreate(blockpos);
589 catch(InvalidPositionException &e)
594 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
595 MapNode n = block->getNode(relpos);
597 if(n.sunlight_propagates())
599 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
600 block->setNode(relpos, n);
602 modified_blocks.insert(blockpos, block);
606 // Turn mud into grass
607 if(n.d == CONTENT_MUD)
610 block->setNode(relpos, n);
611 modified_blocks.insert(blockpos, block);
614 // Sunlight goes no further
621 void Map::updateLighting(enum LightBank bank,
622 core::map<v3s16, MapBlock*> & a_blocks,
623 core::map<v3s16, MapBlock*> & modified_blocks)
625 /*m_dout<<DTIME<<"Map::updateLighting(): "
626 <<a_blocks.getSize()<<" blocks... ";*/
630 u32 count_was = modified_blocks.size();
632 core::map<v3s16, bool> light_sources;
634 core::map<v3s16, u8> unlight_from;
636 core::map<v3s16, MapBlock*>::Iterator i;
637 i = a_blocks.getIterator();
638 for(; i.atEnd() == false; i++)
640 MapBlock *block = i.getNode()->getValue();
644 // Don't bother with dummy blocks.
648 v3s16 pos = block->getPos();
649 modified_blocks.insert(pos, block);
652 Clear all light from block
654 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
655 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
656 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
661 MapNode n = block->getNode(v3s16(x,y,z));
662 u8 oldlight = n.getLight(bank);
664 block->setNode(v3s16(x,y,z), n);
666 // Collect borders for unlighting
667 if(x==0 || x == MAP_BLOCKSIZE-1
668 || y==0 || y == MAP_BLOCKSIZE-1
669 || z==0 || z == MAP_BLOCKSIZE-1)
671 v3s16 p_map = p + v3s16(
674 MAP_BLOCKSIZE*pos.Z);
675 unlight_from.insert(p_map, oldlight);
678 catch(InvalidPositionException &e)
681 This would happen when dealing with a
685 dstream<<"updateLighting(): InvalidPositionException"
690 if(bank == LIGHTBANK_DAY)
692 bool bottom_valid = block->propagateSunlight(light_sources);
694 // If bottom is valid, we're done.
698 else if(bank == LIGHTBANK_NIGHT)
707 /*dstream<<"Bottom for sunlight-propagated block ("
708 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
711 // Else get the block below and loop to it
715 block = getBlockNoCreate(pos);
717 catch(InvalidPositionException &e)
726 //TimeTaker timer("unspreadLight");
727 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
732 u32 diff = modified_blocks.size() - count_was;
733 count_was = modified_blocks.size();
734 dstream<<"unspreadLight modified "<<diff<<std::endl;
737 // TODO: Spread light from propagated sunlight?
738 // Yes, add it to light_sources... somehow.
739 // It has to be added at somewhere above, in the loop.
741 // NOTE: This actually works fine without doing so
742 // - Find out why it works
745 //TimeTaker timer("spreadLight");
746 spreadLight(bank, light_sources, modified_blocks);
751 u32 diff = modified_blocks.size() - count_was;
752 count_was = modified_blocks.size();
753 dstream<<"spreadLight modified "<<diff<<std::endl;
756 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
759 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
760 core::map<v3s16, MapBlock*> & modified_blocks)
762 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
763 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
766 Update information about whether day and night light differ
768 for(core::map<v3s16, MapBlock*>::Iterator
769 i = modified_blocks.getIterator();
770 i.atEnd() == false; i++)
772 MapBlock *block = i.getNode()->getValue();
773 block->updateDayNightDiff();
778 This is called after changing a node from transparent to opaque.
779 The lighting value of the node should be left as-is after changing
780 other values. This sets the lighting value to 0.
782 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
783 core::map<v3s16, MapBlock*> &modified_blocks)*/
784 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
785 core::map<v3s16, MapBlock*> &modified_blocks)
788 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
789 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
792 From this node to nodes underneath:
793 If lighting is sunlight (1.0), unlight neighbours and
798 v3s16 toppos = p + v3s16(0,1,0);
799 v3s16 bottompos = p + v3s16(0,-1,0);
801 bool node_under_sunlight = true;
802 core::map<v3s16, bool> light_sources;
805 If there is a node at top and it doesn't have sunlight,
806 there has not been any sunlight going down.
808 Otherwise there probably is.
811 MapNode topnode = getNode(toppos);
813 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
814 node_under_sunlight = false;
816 catch(InvalidPositionException &e)
820 if(n.d != CONTENT_TORCH)
823 If there is grass below, change it to mud
826 MapNode bottomnode = getNode(bottompos);
828 if(bottomnode.d == CONTENT_GRASS
829 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
831 bottomnode.d = CONTENT_MUD;
832 setNode(bottompos, bottomnode);
835 catch(InvalidPositionException &e)
840 enum LightBank banks[] =
845 for(s32 i=0; i<2; i++)
847 enum LightBank bank = banks[i];
849 u8 lightwas = getNode(p).getLight(bank);
851 // Add the block of the added node to modified_blocks
852 v3s16 blockpos = getNodeBlockPos(p);
853 MapBlock * block = getBlockNoCreate(blockpos);
854 assert(block != NULL);
855 modified_blocks.insert(blockpos, block);
857 if(isValidPosition(p) == false)
860 // Unlight neighbours of node.
861 // This means setting light of all consequent dimmer nodes
863 // This also collects the nodes at the border which will spread
864 // light again into this.
865 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
873 If node is under sunlight, take all sunlighted nodes under
874 it and clear light from them and from where the light has
876 TODO: This could be optimized by mass-unlighting instead
879 if(node_under_sunlight)
883 //m_dout<<DTIME<<"y="<<y<<std::endl;
884 v3s16 n2pos(p.X, y, p.Z);
890 catch(InvalidPositionException &e)
895 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
897 //m_dout<<DTIME<<"doing"<<std::endl;
898 unLightNeighbors(LIGHTBANK_DAY,
899 n2pos, n2.getLight(LIGHTBANK_DAY),
900 light_sources, modified_blocks);
901 n2.setLight(LIGHTBANK_DAY, 0);
909 for(s32 i=0; i<2; i++)
911 enum LightBank bank = banks[i];
914 Spread light from all nodes that might be capable of doing so
915 TODO: Convert to spreadLight
917 spreadLight(bank, light_sources, modified_blocks);
921 Update information about whether day and night light differ
923 for(core::map<v3s16, MapBlock*>::Iterator
924 i = modified_blocks.getIterator();
925 i.atEnd() == false; i++)
927 MapBlock *block = i.getNode()->getValue();
928 block->updateDayNightDiff();
934 void Map::removeNodeAndUpdate(v3s16 p,
935 core::map<v3s16, MapBlock*> &modified_blocks)
938 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
939 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
941 bool node_under_sunlight = true;
943 v3s16 toppos = p + v3s16(0,1,0);
945 // Node will be replaced with this
946 u8 replace_material = CONTENT_AIR;
949 If there is a node at top and it doesn't have sunlight,
950 there will be no sunlight going down.
953 MapNode topnode = getNode(toppos);
955 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
956 node_under_sunlight = false;
958 catch(InvalidPositionException &e)
962 core::map<v3s16, bool> light_sources;
964 enum LightBank banks[] =
969 for(s32 i=0; i<2; i++)
971 enum LightBank bank = banks[i];
974 Unlight neighbors (in case the node is a light source)
976 unLightNeighbors(bank, p,
977 getNode(p).getLight(bank),
978 light_sources, modified_blocks);
983 This also clears the lighting.
987 n.d = replace_material;
990 for(s32 i=0; i<2; i++)
992 enum LightBank bank = banks[i];
997 spreadLight(bank, light_sources, modified_blocks);
1000 // Add the block of the removed node to modified_blocks
1001 v3s16 blockpos = getNodeBlockPos(p);
1002 MapBlock * block = getBlockNoCreate(blockpos);
1003 assert(block != NULL);
1004 modified_blocks.insert(blockpos, block);
1007 If the removed node was under sunlight, propagate the
1008 sunlight down from it and then light all neighbors
1009 of the propagated blocks.
1011 if(node_under_sunlight)
1013 s16 ybottom = propagateSunlight(p, modified_blocks);
1014 /*m_dout<<DTIME<<"Node was under sunlight. "
1015 "Propagating sunlight";
1016 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1018 for(; y >= ybottom; y--)
1020 v3s16 p2(p.X, y, p.Z);
1021 /*m_dout<<DTIME<<"lighting neighbors of node ("
1022 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1024 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1029 // Set the lighting of this node to 0
1030 // TODO: Is this needed? Lighting is cleared up there already.
1032 MapNode n = getNode(p);
1033 n.setLight(LIGHTBANK_DAY, 0);
1036 catch(InvalidPositionException &e)
1042 for(s32 i=0; i<2; i++)
1044 enum LightBank bank = banks[i];
1046 // Get the brightest neighbour node and propagate light from it
1047 v3s16 n2p = getBrightestNeighbour(bank, p);
1049 MapNode n2 = getNode(n2p);
1050 lightNeighbors(bank, n2p, modified_blocks);
1052 catch(InvalidPositionException &e)
1058 Update information about whether day and night light differ
1060 for(core::map<v3s16, MapBlock*>::Iterator
1061 i = modified_blocks.getIterator();
1062 i.atEnd() == false; i++)
1064 MapBlock *block = i.getNode()->getValue();
1065 block->updateDayNightDiff();
1070 void Map::expireMeshes(bool only_daynight_diffed)
1072 TimeTaker timer("expireMeshes()");
1074 core::map<v2s16, MapSector*>::Iterator si;
1075 si = m_sectors.getIterator();
1076 for(; si.atEnd() == false; si++)
1078 MapSector *sector = si.getNode()->getValue();
1080 core::list< MapBlock * > sectorblocks;
1081 sector->getBlocks(sectorblocks);
1083 core::list< MapBlock * >::Iterator i;
1084 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1086 MapBlock *block = *i;
1088 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1094 JMutexAutoLock lock(block->mesh_mutex);
1095 if(block->mesh != NULL)
1097 /*block->mesh->drop();
1098 block->mesh = NULL;*/
1099 block->setMeshExpired(true);
1106 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1108 assert(mapType() == MAPTYPE_CLIENT);
1111 v3s16 p = blockpos + v3s16(0,0,0);
1112 MapBlock *b = getBlockNoCreate(p);
1113 b->updateMesh(daynight_ratio);
1115 catch(InvalidPositionException &e){}
1118 v3s16 p = blockpos + v3s16(-1,0,0);
1119 MapBlock *b = getBlockNoCreate(p);
1120 b->updateMesh(daynight_ratio);
1122 catch(InvalidPositionException &e){}
1124 v3s16 p = blockpos + v3s16(0,-1,0);
1125 MapBlock *b = getBlockNoCreate(p);
1126 b->updateMesh(daynight_ratio);
1128 catch(InvalidPositionException &e){}
1130 v3s16 p = blockpos + v3s16(0,0,-1);
1131 MapBlock *b = getBlockNoCreate(p);
1132 b->updateMesh(daynight_ratio);
1134 catch(InvalidPositionException &e){}
1137 v3s16 p = blockpos + v3s16(1,0,0);
1138 MapBlock *b = getBlockNoCreate(p);
1139 b->updateMesh(daynight_ratio);
1141 catch(InvalidPositionException &e){}
1143 v3s16 p = blockpos + v3s16(0,1,0);
1144 MapBlock *b = getBlockNoCreate(p);
1145 b->updateMesh(daynight_ratio);
1147 catch(InvalidPositionException &e){}
1149 v3s16 p = blockpos + v3s16(0,0,1);
1150 MapBlock *b = getBlockNoCreate(p);
1151 b->updateMesh(daynight_ratio);
1153 catch(InvalidPositionException &e){}*/
1158 bool Map::dayNightDiffed(v3s16 blockpos)
1161 v3s16 p = blockpos + v3s16(0,0,0);
1162 MapBlock *b = getBlockNoCreate(p);
1163 if(b->dayNightDiffed())
1166 catch(InvalidPositionException &e){}
1169 v3s16 p = blockpos + v3s16(-1,0,0);
1170 MapBlock *b = getBlockNoCreate(p);
1171 if(b->dayNightDiffed())
1174 catch(InvalidPositionException &e){}
1176 v3s16 p = blockpos + v3s16(0,-1,0);
1177 MapBlock *b = getBlockNoCreate(p);
1178 if(b->dayNightDiffed())
1181 catch(InvalidPositionException &e){}
1183 v3s16 p = blockpos + v3s16(0,0,-1);
1184 MapBlock *b = getBlockNoCreate(p);
1185 if(b->dayNightDiffed())
1188 catch(InvalidPositionException &e){}
1191 v3s16 p = blockpos + v3s16(1,0,0);
1192 MapBlock *b = getBlockNoCreate(p);
1193 if(b->dayNightDiffed())
1196 catch(InvalidPositionException &e){}
1198 v3s16 p = blockpos + v3s16(0,1,0);
1199 MapBlock *b = getBlockNoCreate(p);
1200 if(b->dayNightDiffed())
1203 catch(InvalidPositionException &e){}
1205 v3s16 p = blockpos + v3s16(0,0,1);
1206 MapBlock *b = getBlockNoCreate(p);
1207 if(b->dayNightDiffed())
1210 catch(InvalidPositionException &e){}
1216 Updates usage timers
1218 void Map::timerUpdate(float dtime)
1220 JMutexAutoLock lock(m_sector_mutex);
1222 core::map<v2s16, MapSector*>::Iterator si;
1224 si = m_sectors.getIterator();
1225 for(; si.atEnd() == false; si++)
1227 MapSector *sector = si.getNode()->getValue();
1228 sector->usage_timer += dtime;
1232 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1235 Wait for caches to be removed before continuing.
1237 This disables the existence of caches while locked
1239 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1241 core::list<v2s16>::Iterator j;
1242 for(j=list.begin(); j!=list.end(); j++)
1244 MapSector *sector = m_sectors[*j];
1247 sector->deleteBlocks();
1252 If sector is in sector cache, remove it from there
1254 if(m_sector_cache == sector)
1256 m_sector_cache = NULL;
1259 Remove from map and delete
1261 m_sectors.remove(*j);
1267 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1268 core::list<v3s16> *deleted_blocks)
1270 JMutexAutoLock lock(m_sector_mutex);
1272 core::list<v2s16> sector_deletion_queue;
1273 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1274 for(; i.atEnd() == false; i++)
1276 MapSector *sector = i.getNode()->getValue();
1278 Delete sector from memory if it hasn't been used in a long time
1280 if(sector->usage_timer > timeout)
1282 sector_deletion_queue.push_back(i.getNode()->getKey());
1284 if(deleted_blocks != NULL)
1286 // Collect positions of blocks of sector
1287 MapSector *sector = i.getNode()->getValue();
1288 core::list<MapBlock*> blocks;
1289 sector->getBlocks(blocks);
1290 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1291 i != blocks.end(); i++)
1293 deleted_blocks->push_back((*i)->getPos());
1298 deleteSectors(sector_deletion_queue, only_blocks);
1299 return sector_deletion_queue.getSize();
1302 void Map::PrintInfo(std::ostream &out)
1311 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1315 m_savedir = savedir;
1316 m_map_saving_enabled = false;
1320 // If directory exists, check contents and load if possible
1321 if(fs::PathExists(m_savedir))
1323 // If directory is empty, it is safe to save into it.
1324 if(fs::GetDirListing(m_savedir).size() == 0)
1326 dstream<<DTIME<<"Server: Empty save directory is valid."
1328 m_map_saving_enabled = true;
1332 // Load master heightmap
1333 loadMasterHeightmap();
1335 // Load sector (0,0) and throw and exception on fail
1336 if(loadSectorFull(v2s16(0,0)) == false)
1337 throw LoadError("Failed to load sector (0,0)");
1339 dstream<<DTIME<<"Server: Successfully loaded master "
1340 "heightmap and sector (0,0) from "<<savedir<<
1341 ", assuming valid save directory."
1344 m_map_saving_enabled = true;
1345 // Map loaded, not creating new one
1349 // If directory doesn't exist, it is safe to save to it
1351 m_map_saving_enabled = true;
1354 catch(std::exception &e)
1356 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1357 <<", exception: "<<e.what()<<std::endl;
1358 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1359 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1362 dstream<<DTIME<<"Initializing new map."<<std::endl;
1364 // Create master heightmap
1365 ValueGenerator *maxgen =
1366 ValueGenerator::deSerialize(hmp.randmax);
1367 ValueGenerator *factorgen =
1368 ValueGenerator::deSerialize(hmp.randfactor);
1369 ValueGenerator *basegen =
1370 ValueGenerator::deSerialize(hmp.base);
1371 m_heightmap = new UnlimitedHeightmap
1372 (hmp.blocksize, maxgen, factorgen, basegen);
1374 // Set map parameters
1377 // Create zero sector
1378 emergeSector(v2s16(0,0));
1380 // Initially write whole map
1384 ServerMap::~ServerMap()
1388 if(m_map_saving_enabled)
1391 // Save only changed parts
1393 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1397 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1400 catch(std::exception &e)
1402 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1403 <<", exception: "<<e.what()<<std::endl;
1406 if(m_heightmap != NULL)
1410 MapSector * ServerMap::emergeSector(v2s16 p2d)
1412 DSTACK("%s: p2d=(%d,%d)",
1415 // Check that it doesn't exist already
1417 return getSectorNoGenerate(p2d);
1419 catch(InvalidPositionException &e)
1424 Try to load the sector from disk.
1426 if(loadSectorFull(p2d) == true)
1428 return getSectorNoGenerate(p2d);
1432 If there is no master heightmap, throw.
1434 if(m_heightmap == NULL)
1436 throw InvalidPositionException("emergeSector(): no heightmap");
1440 Do not generate over-limit
1442 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1443 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1444 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1445 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1446 throw InvalidPositionException("emergeSector(): pos. over limit");
1449 Generate sector and heightmaps
1452 // Number of heightmaps in sector in each direction
1453 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1455 // Heightmap side width
1456 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1458 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1460 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1461 " heightmaps and objects"<<std::endl;*/
1463 // Loop through sub-heightmaps
1464 for(s16 y=0; y<hm_split; y++)
1465 for(s16 x=0; x<hm_split; x++)
1467 v2s16 p_in_sector = v2s16(x,y);
1468 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1470 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1471 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1472 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1473 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1476 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1477 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1480 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1482 sector->setHeightmap(p_in_sector, hm);
1484 //TODO: Make these values configurable
1485 //hm->generateContinued(0.0, 0.0, corners);
1486 hm->generateContinued(0.5, 0.2, corners);
1487 //hm->generateContinued(1.0, 0.2, corners);
1488 //hm->generateContinued(2.0, 0.2, corners);
1498 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1499 sector->setObjects(objects);
1501 v2s16 mhm_p = p2d * hm_split;
1503 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1504 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1505 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1506 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1509 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1510 float avgslope = 0.0;
1511 avgslope += fabs(avgheight - corners[0]);
1512 avgslope += fabs(avgheight - corners[1]);
1513 avgslope += fabs(avgheight - corners[2]);
1514 avgslope += fabs(avgheight - corners[3]);
1516 avgslope /= MAP_BLOCKSIZE;
1517 //dstream<<"avgslope="<<avgslope<<std::endl;
1519 float pitness = 0.0;
1521 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1524 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1527 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1530 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1534 pitness /= MAP_BLOCKSIZE;
1535 //dstream<<"pitness="<<pitness<<std::endl;
1538 Plant some trees if there is not much slope
1541 // Avgslope is the derivative of a hill
1542 float t = avgslope * avgslope;
1543 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1546 tree_max = a / (t/0.03);
1549 u32 count = (myrand()%(tree_max+1));
1550 //u32 count = tree_max;
1551 for(u32 i=0; i<count; i++)
1553 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
1554 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
1555 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1558 objects->insert(v3s16(x, y, z),
1559 SECTOR_OBJECT_TREE_1);
1563 Plant some bushes if sector is pit-like
1566 // Pitness usually goes at around -0.5...0.5
1568 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1570 bush_max = (pitness*a*4);
1573 u32 count = (myrand()%(bush_max+1));
1574 for(u32 i=0; i<count; i++)
1576 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
1577 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
1578 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1581 objects->insert(v3s16(x, y, z),
1582 SECTOR_OBJECT_BUSH_1);
1586 Add ravine (randomly)
1588 if(m_params.ravines_amount != 0)
1590 if(myrand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1593 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1594 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
1597 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1598 objects->insert(v3s16(x, y, z),
1599 SECTOR_OBJECT_RAVINE);
1606 JMutexAutoLock lock(m_sector_mutex);
1607 m_sectors.insert(p2d, sector);
1612 MapBlock * ServerMap::emergeBlock(
1614 bool only_from_disk,
1615 core::map<v3s16, MapBlock*> &changed_blocks,
1616 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1619 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1621 p.X, p.Y, p.Z, only_from_disk);
1623 /*dstream<<"ServerMap::emergeBlock(): "
1624 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1625 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1626 v2s16 p2d(p.X, p.Z);
1629 This will create or load a sector if not found in memory.
1630 If block exists on disk, it will be loaded.
1632 NOTE: On old save formats, this will be slow, as it generates
1633 lighting on blocks for them.
1635 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1636 assert(sector->getId() == MAPSECTOR_SERVER);
1638 // Try to get a block from the sector
1639 MapBlock *block = NULL;
1640 bool not_on_disk = false;
1642 block = sector->getBlockNoCreate(block_y);
1643 if(block->isDummy() == true)
1648 catch(InvalidPositionException &e)
1654 If block was not found on disk and not going to generate a
1655 new one, make sure there is a dummy block in place.
1657 if(not_on_disk && only_from_disk)
1661 // Create dummy block
1662 block = new MapBlock(this, p, true);
1664 // Add block to sector
1665 sector->insertBlock(block);
1671 //dstream<<"Not found on disk, generating."<<std::endl;
1672 //TimeTaker("emergeBlock()", g_irrlicht);
1675 Do not generate over-limit
1677 if(blockpos_over_limit(p))
1678 throw InvalidPositionException("emergeBlock(): pos. over limit");
1683 Go on generating the block.
1685 TODO: If a dungeon gets generated so that it's side gets
1686 revealed to the outside air, the lighting should be
1691 If block doesn't exist, create one.
1692 If it exists, it is a dummy. In that case unDummify() it.
1696 block = sector->createBlankBlockNoInsert(block_y);
1700 // Remove the block so that nobody can get a half-generated one.
1701 sector->removeBlock(block);
1702 // Allocate the block to be a proper one.
1707 Create dungeon making table
1709 const s32 ued = MAP_BLOCKSIZE;
1710 bool underground_emptiness[ued*ued*ued];
1711 for(s32 i=0; i<ued*ued*ued; i++)
1713 underground_emptiness[i] = 0;
1715 // Generate dungeons
1718 Initialize orp and ors. Try to find if some neighboring
1719 MapBlock has a tunnel ended in its side
1723 (float)(myrand()%ued)+0.5,
1724 (float)(myrand()%ued)+0.5,
1725 (float)(myrand()%ued)+0.5
1728 bool found_existing = false;
1734 for(s16 y=0; y<ued; y++)
1735 for(s16 x=0; x<ued; x++)
1737 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1738 if(getNode(ap).d == CONTENT_AIR)
1740 orp = v3f(x+1,y+1,0);
1741 found_existing = true;
1742 goto continue_generating;
1746 catch(InvalidPositionException &e){}
1752 for(s16 y=0; y<ued; y++)
1753 for(s16 x=0; x<ued; x++)
1755 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1756 if(getNode(ap).d == CONTENT_AIR)
1758 orp = v3f(x+1,y+1,ued-1);
1759 found_existing = true;
1760 goto continue_generating;
1764 catch(InvalidPositionException &e){}
1770 for(s16 y=0; y<ued; y++)
1771 for(s16 z=0; z<ued; z++)
1773 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1774 if(getNode(ap).d == CONTENT_AIR)
1776 orp = v3f(0,y+1,z+1);
1777 found_existing = true;
1778 goto continue_generating;
1782 catch(InvalidPositionException &e){}
1788 for(s16 y=0; y<ued; y++)
1789 for(s16 z=0; z<ued; z++)
1791 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
1792 if(getNode(ap).d == CONTENT_AIR)
1794 orp = v3f(ued-1,y+1,z+1);
1795 found_existing = true;
1796 goto continue_generating;
1800 catch(InvalidPositionException &e){}
1802 continue_generating:
1805 Don't always generate dungeon
1807 if(found_existing || rand() % 2 == 0)
1810 Generate some tunnel starting from orp and ors
1812 for(u16 i=0; i<3; i++)
1815 (float)(myrand()%ued)+0.5,
1816 (float)(myrand()%ued)+0.5,
1817 (float)(myrand()%ued)+0.5
1821 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
1825 for(float f=0; f<1.0; f+=0.04)
1827 v3f fp = orp + vec * f;
1828 v3s16 cp(fp.X, fp.Y, fp.Z);
1830 s16 d1 = d0 + rs - 1;
1831 for(s16 z0=d0; z0<=d1; z0++)
1833 s16 si = rs - abs(z0);
1834 for(s16 x0=-si; x0<=si-1; x0++)
1836 s16 si2 = rs - abs(x0);
1837 for(s16 y0=-si2+1; y0<=si2-1; y0++)
1843 if(isInArea(p, ued) == false)
1845 underground_emptiness[ued*ued*z + ued*y + x] = 1;
1856 u8 water_material = CONTENT_WATER;
1857 if(g_settings.getBool("endless_water"))
1858 water_material = CONTENT_OCEAN;
1860 s32 lowest_ground_y = 32767;
1861 s32 highest_ground_y = -32768;
1864 //sector->printHeightmaps();
1866 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1867 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1869 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1871 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1872 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1873 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1875 dstream<<"WARNING: Surface height not found in sector "
1876 "for block that is being emerged"<<std::endl;
1880 s16 surface_y = surface_y_f;
1881 //avg_ground_y += surface_y;
1882 if(surface_y < lowest_ground_y)
1883 lowest_ground_y = surface_y;
1884 if(surface_y > highest_ground_y)
1885 highest_ground_y = surface_y;
1887 s32 surface_depth = 0;
1889 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1891 //float min_slope = 0.45;
1892 //float max_slope = 0.85;
1893 float min_slope = 0.60;
1894 float max_slope = 1.20;
1895 float min_slope_depth = 5.0;
1896 float max_slope_depth = 0;
1897 if(slope < min_slope)
1898 surface_depth = min_slope_depth;
1899 else if(slope > max_slope)
1900 surface_depth = max_slope_depth;
1902 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1904 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1906 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1911 NOTE: If there are some man-made structures above the
1912 newly created block, they won't be taken into account.
1914 if(real_y > surface_y)
1915 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1921 // If node is over heightmap y, it's air or water
1922 if(real_y > surface_y)
1924 // If under water level, it's water
1925 if(real_y < WATER_LEVEL)
1927 n.d = water_material;
1928 n.setLight(LIGHTBANK_DAY,
1929 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1935 // Else it's ground or dungeons (air)
1939 if(underground_emptiness[
1940 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1941 +ued*(y0*ued/MAP_BLOCKSIZE)
1942 +(x0*ued/MAP_BLOCKSIZE)])
1948 // If it's surface_depth under ground, it's stone
1949 if(real_y <= surface_y - surface_depth)
1951 n.d = CONTENT_STONE;
1955 // It is mud if it is under the first ground
1956 // level or under water
1957 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
1963 n.d = CONTENT_GRASS;
1966 //n.d = CONTENT_MUD;
1968 /*// If under water level, it's mud
1969 if(real_y < WATER_LEVEL)
1971 // Only the topmost node is grass
1972 else if(real_y <= surface_y - 1)
1975 n.d = CONTENT_GRASS;*/
1980 else if(real_y <= surface_y - surface_depth)
1983 if(underground_emptiness[
1984 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1985 +ued*(y0*ued/MAP_BLOCKSIZE)
1986 +(x0*ued/MAP_BLOCKSIZE)])
1992 n.d = CONTENT_STONE;
1995 // If node is at or under heightmap y
1996 else if(real_y <= surface_y)
1998 // If under water level, it's mud
1999 if(real_y < WATER_LEVEL)
2001 // Only the topmost node is grass
2002 else if(real_y <= surface_y - 1)
2004 // Else it's the main material
2009 block->setNode(v3s16(x0,y0,z0), n);
2014 Calculate is_underground
2016 // Probably underground if the highest part of block is under lowest
2018 bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
2019 block->setIsUnderground(is_underground);
2022 Force lighting update if some part of block is underground
2023 This is needed because of caves.
2026 bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
2027 if(some_part_underground)
2028 //if(is_underground)
2030 lighting_invalidated_blocks[block->getPos()] = block;
2037 if(some_part_underground)
2039 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2044 for(s16 i=0; i<underground_level*1; i++)
2049 (myrand()%(MAP_BLOCKSIZE-2))+1,
2050 (myrand()%(MAP_BLOCKSIZE-2))+1,
2051 (myrand()%(MAP_BLOCKSIZE-2))+1
2057 //if(is_ground_content(block->getNode(cp).d))
2058 if(block->getNode(cp).d == CONTENT_STONE)
2060 block->setNode(cp, n);
2062 for(u16 i=0; i<26; i++)
2064 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2065 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2067 block->setNode(cp+g_26dirs[i], n);
2075 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2076 u16 coal_rareness = 60 / coal_amount;
2077 if(coal_rareness == 0)
2079 if(myrand()%coal_rareness == 0)
2081 u16 a = myrand() % 16;
2082 u16 amount = coal_amount * a*a*a / 1000;
2083 for(s16 i=0; i<amount; i++)
2086 (myrand()%(MAP_BLOCKSIZE-2))+1,
2087 (myrand()%(MAP_BLOCKSIZE-2))+1,
2088 (myrand()%(MAP_BLOCKSIZE-2))+1
2092 n.d = CONTENT_COALSTONE;
2094 //dstream<<"Adding coalstone"<<std::endl;
2096 //if(is_ground_content(block->getNode(cp).d))
2097 if(block->getNode(cp).d == CONTENT_STONE)
2099 block->setNode(cp, n);
2101 for(u16 i=0; i<26; i++)
2103 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2104 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2106 block->setNode(cp+g_26dirs[i], n);
2113 Create a few rats in empty blocks underground
2117 //for(u16 i=0; i<2; i++)
2120 (myrand()%(MAP_BLOCKSIZE-2))+1,
2121 (myrand()%(MAP_BLOCKSIZE-2))+1,
2122 (myrand()%(MAP_BLOCKSIZE-2))+1
2125 // Check that the place is empty
2126 //if(!is_ground_content(block->getNode(cp).d))
2129 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2130 block->addObject(obj);
2136 Add block to sector.
2138 sector->insertBlock(block);
2144 // An y-wise container of changed blocks
2145 core::map<s16, MapBlock*> changed_blocks_sector;
2148 Check if any sector's objects can be placed now.
2151 core::map<v3s16, u8> *objects = sector->getObjects();
2152 core::list<v3s16> objects_to_remove;
2153 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2154 i.atEnd() == false; i++)
2156 v3s16 p = i.getNode()->getKey();
2158 u8 d = i.getNode()->getValue();
2160 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
2165 if(d == SECTOR_OBJECT_TEST)
2167 if(sector->isValidArea(p + v3s16(0,0,0),
2168 p + v3s16(0,0,0), &changed_blocks_sector))
2171 n.d = CONTENT_TORCH;
2172 sector->setNode(p, n);
2173 objects_to_remove.push_back(p);
2176 else if(d == SECTOR_OBJECT_TREE_1)
2178 v3s16 p_min = p + v3s16(-1,0,-1);
2179 v3s16 p_max = p + v3s16(1,4,1);
2180 if(sector->isValidArea(p_min, p_max,
2181 &changed_blocks_sector))
2185 sector->setNode(p+v3s16(0,0,0), n);
2186 sector->setNode(p+v3s16(0,1,0), n);
2187 sector->setNode(p+v3s16(0,2,0), n);
2188 sector->setNode(p+v3s16(0,3,0), n);
2190 n.d = CONTENT_LEAVES;
2192 sector->setNode(p+v3s16(0,4,0), n);
2194 sector->setNode(p+v3s16(-1,4,0), n);
2195 sector->setNode(p+v3s16(1,4,0), n);
2196 sector->setNode(p+v3s16(0,4,-1), n);
2197 sector->setNode(p+v3s16(0,4,1), n);
2198 sector->setNode(p+v3s16(1,4,1), n);
2199 sector->setNode(p+v3s16(-1,4,1), n);
2200 sector->setNode(p+v3s16(-1,4,-1), n);
2201 sector->setNode(p+v3s16(1,4,-1), n);
2203 sector->setNode(p+v3s16(-1,3,0), n);
2204 sector->setNode(p+v3s16(1,3,0), n);
2205 sector->setNode(p+v3s16(0,3,-1), n);
2206 sector->setNode(p+v3s16(0,3,1), n);
2207 sector->setNode(p+v3s16(1,3,1), n);
2208 sector->setNode(p+v3s16(-1,3,1), n);
2209 sector->setNode(p+v3s16(-1,3,-1), n);
2210 sector->setNode(p+v3s16(1,3,-1), n);
2212 objects_to_remove.push_back(p);
2214 // Lighting has to be recalculated for this one.
2215 sector->getBlocksInArea(p_min, p_max,
2216 lighting_invalidated_blocks);
2219 else if(d == SECTOR_OBJECT_BUSH_1)
2221 if(sector->isValidArea(p + v3s16(0,0,0),
2222 p + v3s16(0,0,0), &changed_blocks_sector))
2225 n.d = CONTENT_LEAVES;
2226 sector->setNode(p+v3s16(0,0,0), n);
2228 objects_to_remove.push_back(p);
2231 else if(d == SECTOR_OBJECT_RAVINE)
2234 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2235 v3s16 p_max = p + v3s16(6,6,6);
2236 if(sector->isValidArea(p_min, p_max,
2237 &changed_blocks_sector))
2240 n.d = CONTENT_STONE;
2243 s16 depth = maxdepth + (myrand()%10);
2245 s16 minz = -6 - (-2);
2247 for(s16 x=-6; x<=6; x++)
2249 z += -1 + (myrand()%3);
2254 for(s16 y=depth+(myrand()%2); y<=6; y++)
2256 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2259 v3s16 p2 = p + v3s16(x,y,z-2);
2260 if(is_ground_content(sector->getNode(p2).d)
2261 && !is_mineral(sector->getNode(p2).d))
2262 sector->setNode(p2, n);
2265 v3s16 p2 = p + v3s16(x,y,z-1);
2266 if(is_ground_content(sector->getNode(p2).d)
2267 && !is_mineral(sector->getNode(p2).d))
2268 sector->setNode(p2, n2);
2271 v3s16 p2 = p + v3s16(x,y,z+0);
2272 if(is_ground_content(sector->getNode(p2).d)
2273 && !is_mineral(sector->getNode(p2).d))
2274 sector->setNode(p2, n2);
2277 v3s16 p2 = p + v3s16(x,y,z+1);
2278 if(is_ground_content(sector->getNode(p2).d)
2279 && !is_mineral(sector->getNode(p2).d))
2280 sector->setNode(p2, n);
2283 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2284 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2288 objects_to_remove.push_back(p);
2290 // Lighting has to be recalculated for this one.
2291 sector->getBlocksInArea(p_min, p_max,
2292 lighting_invalidated_blocks);
2297 dstream<<"ServerMap::emergeBlock(): "
2298 "Invalid heightmap object"
2303 catch(InvalidPositionException &e)
2305 dstream<<"WARNING: "<<__FUNCTION_NAME
2306 <<": while inserting object "<<(int)d
2307 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2308 <<" InvalidPositionException.what()="
2309 <<e.what()<<std::endl;
2310 // This is not too fatal and seems to happen sometimes.
2315 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2316 i != objects_to_remove.end(); i++)
2318 objects->remove(*i);
2321 for(core::map<s16, MapBlock*>::Iterator
2322 i = changed_blocks_sector.getIterator();
2323 i.atEnd() == false; i++)
2325 MapBlock *block = i.getNode()->getValue();
2327 changed_blocks.insert(block->getPos(), block);
2333 void ServerMap::createDir(std::string path)
2335 if(fs::CreateDir(path) == false)
2337 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2338 <<"\""<<path<<"\""<<std::endl;
2339 throw BaseException("ServerMap failed to create directory");
2343 std::string ServerMap::getSectorSubDir(v2s16 pos)
2346 snprintf(cc, 9, "%.4x%.4x",
2347 (unsigned int)pos.X&0xffff,
2348 (unsigned int)pos.Y&0xffff);
2350 return std::string(cc);
2353 std::string ServerMap::getSectorDir(v2s16 pos)
2355 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2358 v2s16 ServerMap::getSectorPos(std::string dirname)
2360 if(dirname.size() != 8)
2361 throw InvalidFilenameException("Invalid sector directory name");
2363 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2365 throw InvalidFilenameException("Invalid sector directory name");
2366 v2s16 pos((s16)x, (s16)y);
2370 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2372 v2s16 p2d = getSectorPos(sectordir);
2374 if(blockfile.size() != 4){
2375 throw InvalidFilenameException("Invalid block filename");
2378 int r = sscanf(blockfile.c_str(), "%4x", &y);
2380 throw InvalidFilenameException("Invalid block filename");
2381 return v3s16(p2d.X, y, p2d.Y);
2385 #define ENABLE_SECTOR_SAVING 1
2386 #define ENABLE_SECTOR_LOADING 1
2387 #define ENABLE_BLOCK_SAVING 1
2388 #define ENABLE_BLOCK_LOADING 1
2390 void ServerMap::save(bool only_changed)
2392 DSTACK(__FUNCTION_NAME);
2393 if(m_map_saving_enabled == false)
2395 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2399 if(only_changed == false)
2400 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2403 saveMasterHeightmap();
2405 u32 sector_meta_count = 0;
2406 u32 block_count = 0;
2409 JMutexAutoLock lock(m_sector_mutex);
2411 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2412 for(; i.atEnd() == false; i++)
2414 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2415 assert(sector->getId() == MAPSECTOR_SERVER);
2417 if(ENABLE_SECTOR_SAVING)
2419 if(sector->differs_from_disk || only_changed == false)
2421 saveSectorMeta(sector);
2422 sector_meta_count++;
2425 if(ENABLE_BLOCK_SAVING)
2427 core::list<MapBlock*> blocks;
2428 sector->getBlocks(blocks);
2429 core::list<MapBlock*>::Iterator j;
2430 for(j=blocks.begin(); j!=blocks.end(); j++)
2432 MapBlock *block = *j;
2433 if(block->getChangedFlag() || only_changed == false)
2445 Only print if something happened or saved whole map
2447 if(only_changed == false || sector_meta_count != 0
2448 || block_count != 0)
2450 dstream<<DTIME<<"ServerMap: Written: "
2451 <<sector_meta_count<<" sector metadata files, "
2452 <<block_count<<" block files"
2457 void ServerMap::loadAll()
2459 DSTACK(__FUNCTION_NAME);
2460 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2462 loadMasterHeightmap();
2464 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2466 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2468 JMutexAutoLock lock(m_sector_mutex);
2471 s32 printed_counter = -100000;
2472 s32 count = list.size();
2474 std::vector<fs::DirListNode>::iterator i;
2475 for(i=list.begin(); i!=list.end(); i++)
2477 if(counter > printed_counter + 10)
2479 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2480 printed_counter = counter;
2484 MapSector *sector = NULL;
2486 // We want directories
2490 sector = loadSectorMeta(i->name);
2492 catch(InvalidFilenameException &e)
2494 // This catches unknown crap in directory
2497 if(ENABLE_BLOCK_LOADING)
2499 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2500 (m_savedir+"/sectors/"+i->name);
2501 std::vector<fs::DirListNode>::iterator i2;
2502 for(i2=list2.begin(); i2!=list2.end(); i2++)
2508 loadBlock(i->name, i2->name, sector);
2510 catch(InvalidFilenameException &e)
2512 // This catches unknown crap in directory
2517 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2520 void ServerMap::saveMasterHeightmap()
2522 DSTACK(__FUNCTION_NAME);
2523 createDir(m_savedir);
2525 std::string fullpath = m_savedir + "/master_heightmap";
2526 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2527 if(o.good() == false)
2528 throw FileNotGoodException("Cannot open master heightmap");
2530 // Format used for writing
2531 u8 version = SER_FMT_VER_HIGHEST;
2534 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2536 [0] u8 serialization version
2537 [1] X master heightmap
2539 u32 fullsize = 1 + hmdata.getSize();
2540 SharedBuffer<u8> data(fullsize);
2543 memcpy(&data[1], *hmdata, hmdata.getSize());
2545 o.write((const char*)*data, fullsize);
2548 m_heightmap->serialize(o, version);
2551 void ServerMap::loadMasterHeightmap()
2553 DSTACK(__FUNCTION_NAME);
2554 std::string fullpath = m_savedir + "/master_heightmap";
2555 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2556 if(is.good() == false)
2557 throw FileNotGoodException("Cannot open master heightmap");
2559 if(m_heightmap != NULL)
2562 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2565 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2567 DSTACK(__FUNCTION_NAME);
2568 // Format used for writing
2569 u8 version = SER_FMT_VER_HIGHEST;
2571 v2s16 pos = sector->getPos();
2572 createDir(m_savedir);
2573 createDir(m_savedir+"/sectors");
2574 std::string dir = getSectorDir(pos);
2577 std::string fullpath = dir + "/heightmap";
2578 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2579 if(o.good() == false)
2580 throw FileNotGoodException("Cannot open master heightmap");
2582 sector->serialize(o, version);
2584 sector->differs_from_disk = false;
2587 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2589 DSTACK(__FUNCTION_NAME);
2591 v2s16 p2d = getSectorPos(dirname);
2592 std::string dir = m_savedir + "/sectors/" + dirname;
2594 std::string fullpath = dir + "/heightmap";
2595 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2596 if(is.good() == false)
2597 throw FileNotGoodException("Cannot open sector heightmap");
2599 ServerMapSector *sector = ServerMapSector::deSerialize
2600 (is, this, p2d, &m_hwrapper, m_sectors);
2602 sector->differs_from_disk = false;
2607 bool ServerMap::loadSectorFull(v2s16 p2d)
2609 DSTACK(__FUNCTION_NAME);
2610 std::string sectorsubdir = getSectorSubDir(p2d);
2612 MapSector *sector = NULL;
2614 JMutexAutoLock lock(m_sector_mutex);
2617 sector = loadSectorMeta(sectorsubdir);
2619 catch(InvalidFilenameException &e)
2623 catch(FileNotGoodException &e)
2627 catch(std::exception &e)
2632 if(ENABLE_BLOCK_LOADING)
2634 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2635 (m_savedir+"/sectors/"+sectorsubdir);
2636 std::vector<fs::DirListNode>::iterator i2;
2637 for(i2=list2.begin(); i2!=list2.end(); i2++)
2643 loadBlock(sectorsubdir, i2->name, sector);
2645 catch(InvalidFilenameException &e)
2647 // This catches unknown crap in directory
2655 bool ServerMap::deFlushSector(v2s16 p2d)
2657 DSTACK(__FUNCTION_NAME);
2658 // See if it already exists in memory
2660 MapSector *sector = getSectorNoGenerate(p2d);
2663 catch(InvalidPositionException &e)
2666 Try to load the sector from disk.
2668 if(loadSectorFull(p2d) == true)
2677 void ServerMap::saveBlock(MapBlock *block)
2679 DSTACK(__FUNCTION_NAME);
2681 Dummy blocks are not written
2683 if(block->isDummy())
2685 /*v3s16 p = block->getPos();
2686 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2687 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2691 // Format used for writing
2692 u8 version = SER_FMT_VER_HIGHEST;
2694 v3s16 p3d = block->getPos();
2695 v2s16 p2d(p3d.X, p3d.Z);
2696 createDir(m_savedir);
2697 createDir(m_savedir+"/sectors");
2698 std::string dir = getSectorDir(p2d);
2701 // Block file is map/sectors/xxxxxxxx/xxxx
2703 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2704 std::string fullpath = dir + "/" + cc;
2705 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2706 if(o.good() == false)
2707 throw FileNotGoodException("Cannot open block data");
2710 [0] u8 serialization version
2713 o.write((char*)&version, 1);
2715 block->serialize(o, version);
2718 Versions up from 9 have block objects.
2722 block->serializeObjects(o, version);
2725 // We just wrote it to the disk
2726 block->resetChangedFlag();
2729 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2731 DSTACK(__FUNCTION_NAME);
2735 // Block file is map/sectors/xxxxxxxx/xxxx
2736 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2737 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2738 if(is.good() == false)
2739 throw FileNotGoodException("Cannot open block file");
2741 v3s16 p3d = getBlockPos(sectordir, blockfile);
2742 v2s16 p2d(p3d.X, p3d.Z);
2744 assert(sector->getPos() == p2d);
2746 u8 version = SER_FMT_VER_INVALID;
2747 is.read((char*)&version, 1);
2749 /*u32 block_size = MapBlock::serializedLength(version);
2750 SharedBuffer<u8> data(block_size);
2751 is.read((char*)*data, block_size);*/
2753 // This will always return a sector because we're the server
2754 //MapSector *sector = emergeSector(p2d);
2756 MapBlock *block = NULL;
2757 bool created_new = false;
2759 block = sector->getBlockNoCreate(p3d.Y);
2761 catch(InvalidPositionException &e)
2763 block = sector->createBlankBlockNoInsert(p3d.Y);
2767 // deserialize block data
2768 block->deSerialize(is, version);
2771 Versions up from 9 have block objects.
2775 block->updateObjects(is, version, NULL, 0);
2779 sector->insertBlock(block);
2782 Convert old formats to new and save
2785 // Save old format blocks in new format
2786 if(version < SER_FMT_VER_HIGHEST)
2791 // We just loaded it from the disk, so it's up-to-date.
2792 block->resetChangedFlag();
2795 catch(SerializationError &e)
2797 dstream<<"WARNING: Invalid block data on disk "
2798 "(SerializationError). Ignoring."
2803 // Gets from master heightmap
2804 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2806 assert(m_heightmap != NULL);
2814 corners[0] = m_heightmap->getGroundHeight
2815 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2816 corners[1] = m_heightmap->getGroundHeight
2817 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2818 corners[2] = m_heightmap->getGroundHeight
2819 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2820 corners[3] = m_heightmap->getGroundHeight
2821 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2824 void ServerMap::PrintInfo(std::ostream &out)
2835 ClientMap::ClientMap(
2837 MapDrawControl &control,
2838 scene::ISceneNode* parent,
2839 scene::ISceneManager* mgr,
2843 scene::ISceneNode(parent, mgr, id),
2850 /*m_box = core::aabbox3d<f32>(0,0,0,
2851 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2852 /*m_box = core::aabbox3d<f32>(0,0,0,
2853 map->getSizeNodes().X * BS,
2854 map->getSizeNodes().Y * BS,
2855 map->getSizeNodes().Z * BS);*/
2856 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2857 BS*1000000,BS*1000000,BS*1000000);
2859 //setPosition(v3f(BS,BS,BS));
2862 ClientMap::~ClientMap()
2864 JMutexAutoLock lock(mesh_mutex);
2873 MapSector * ClientMap::emergeSector(v2s16 p2d)
2875 DSTACK(__FUNCTION_NAME);
2876 // Check that it doesn't exist already
2878 return getSectorNoGenerate(p2d);
2880 catch(InvalidPositionException &e)
2884 // Create a sector with no heightmaps
2885 ClientMapSector *sector = new ClientMapSector(this, p2d);
2888 JMutexAutoLock lock(m_sector_mutex);
2889 m_sectors.insert(p2d, sector);
2895 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2897 DSTACK(__FUNCTION_NAME);
2898 ClientMapSector *sector = NULL;
2900 JMutexAutoLock lock(m_sector_mutex);
2902 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2906 sector = (ClientMapSector*)n->getValue();
2907 assert(sector->getId() == MAPSECTOR_CLIENT);
2911 sector = new ClientMapSector(this, p2d);
2913 JMutexAutoLock lock(m_sector_mutex);
2914 m_sectors.insert(p2d, sector);
2918 sector->deSerialize(is);
2921 void ClientMap::OnRegisterSceneNode()
2925 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
2926 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
2929 ISceneNode::OnRegisterSceneNode();
2932 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
2934 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2935 DSTACK(__FUNCTION_NAME);
2937 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2940 Get time for measuring timeout.
2942 Measuring time is very useful for long delays when the
2943 machine is swapping a lot.
2945 int time1 = time(0);
2947 u32 daynight_ratio = m_client->getDayNightRatio();
2949 m_camera_mutex.Lock();
2950 v3f camera_position = m_camera_position;
2951 v3f camera_direction = m_camera_direction;
2952 m_camera_mutex.Unlock();
2955 Get all blocks and draw all visible ones
2958 v3s16 cam_pos_nodes(
2959 camera_position.X / BS,
2960 camera_position.Y / BS,
2961 camera_position.Z / BS);
2963 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
2965 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2966 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2968 // Take a fair amount as we will be dropping more out later
2970 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2971 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2972 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2974 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2975 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2976 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2978 u32 vertex_count = 0;
2980 // For limiting number of mesh updates per frame
2981 u32 mesh_update_count = 0;
2983 u32 blocks_would_have_drawn = 0;
2984 u32 blocks_drawn = 0;
2986 //NOTE: The sectors map should be locked but we're not doing it
2987 // because it'd cause too much delays
2989 int timecheck_counter = 0;
2990 core::map<v2s16, MapSector*>::Iterator si;
2991 si = m_sectors.getIterator();
2992 for(; si.atEnd() == false; si++)
2995 timecheck_counter++;
2996 if(timecheck_counter > 50)
2998 int time2 = time(0);
2999 if(time2 > time1 + 4)
3001 dstream<<"ClientMap::renderMap(): "
3002 "Rendering takes ages, returning."
3009 MapSector *sector = si.getNode()->getValue();
3010 v2s16 sp = sector->getPos();
3012 if(m_control.range_all == false)
3014 if(sp.X < p_blocks_min.X
3015 || sp.X > p_blocks_max.X
3016 || sp.Y < p_blocks_min.Z
3017 || sp.Y > p_blocks_max.Z)
3021 core::list< MapBlock * > sectorblocks;
3022 sector->getBlocks(sectorblocks);
3028 core::list< MapBlock * >::Iterator i;
3029 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3031 MapBlock *block = *i;
3034 Compare block position to camera position, skip
3035 if not seen on display
3038 v3s16 blockpos_nodes = block->getPosRelative();
3040 // Block center position
3042 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3043 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3044 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3047 // Block position relative to camera
3048 v3f blockpos_relative = blockpos - camera_position;
3050 // Distance in camera direction (+=front, -=back)
3051 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3054 f32 d = blockpos_relative.getLength();
3056 if(m_control.range_all == false)
3058 // If block is far away, don't draw it
3059 if(d > m_control.wanted_range * BS)
3060 // This is nicer when fog is used
3061 //if((dforward+d)/2 > m_control.wanted_range * BS)
3065 // Maximum radius of a block
3066 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3068 // If block is (nearly) touching the camera, don't
3069 // bother validating further (that is, render it anyway)
3070 if(d > block_max_radius * 1.5)
3072 // Cosine of the angle between the camera direction
3073 // and the block direction (camera_direction is an unit vector)
3074 f32 cosangle = dforward / d;
3076 // Compensate for the size of the block
3077 // (as the block has to be shown even if it's a bit off FOV)
3078 // This is an estimate.
3079 cosangle += block_max_radius / dforward;
3081 // If block is not in the field of view, skip it
3082 //if(cosangle < cos(FOV_ANGLE/2))
3083 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3088 Draw the faces of the block
3091 bool mesh_expired = false;
3094 JMutexAutoLock lock(block->mesh_mutex);
3096 mesh_expired = block->getMeshExpired();
3098 // Mesh has not been expired and there is no mesh:
3099 // block has no content
3100 if(block->mesh == NULL && mesh_expired == false)
3104 f32 faraway = BS*50;
3105 //f32 faraway = m_control.wanted_range * BS;
3108 This has to be done with the mesh_mutex unlocked
3110 // Pretty random but this should work somewhat nicely
3111 if(mesh_expired && (
3112 (mesh_update_count < 3
3113 && (d < faraway || mesh_update_count < 2)
3116 (m_control.range_all && mesh_update_count < 20)
3119 /*if(mesh_expired && mesh_update_count < 6
3120 && (d < faraway || mesh_update_count < 3))*/
3122 mesh_update_count++;
3124 // Mesh has been expired: generate new mesh
3125 //block->updateMeshes(daynight_i);
3126 block->updateMesh(daynight_ratio);
3128 mesh_expired = false;
3132 Don't draw an expired mesh that is far away
3134 /*if(mesh_expired && d >= faraway)
3137 // Instead, delete it
3138 JMutexAutoLock lock(block->mesh_mutex);
3141 block->mesh->drop();
3144 // And continue to next block
3149 JMutexAutoLock lock(block->mesh_mutex);
3151 scene::SMesh *mesh = block->mesh;
3156 blocks_would_have_drawn++;
3157 if(blocks_drawn >= m_control.wanted_max_blocks
3158 && m_control.range_all == false
3159 && d > m_control.wanted_min_range * BS)
3163 u32 c = mesh->getMeshBufferCount();
3165 for(u32 i=0; i<c; i++)
3167 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3168 const video::SMaterial& material = buf->getMaterial();
3169 video::IMaterialRenderer* rnd =
3170 driver->getMaterialRenderer(material.MaterialType);
3171 bool transparent = (rnd && rnd->isTransparent());
3172 // Render transparent on transparent pass and likewise.
3173 if(transparent == is_transparent_pass)
3175 driver->setMaterial(buf->getMaterial());
3176 driver->drawMeshBuffer(buf);
3177 vertex_count += buf->getVertexCount();
3181 } // foreach sectorblocks
3184 m_control.blocks_drawn = blocks_drawn;
3185 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3187 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3188 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3191 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
3194 Add it to all blocks touching it
3197 v3s16(0,0,0), // this
3198 v3s16(0,0,1), // back
3199 v3s16(0,1,0), // top
3200 v3s16(1,0,0), // right
3201 v3s16(0,0,-1), // front
3202 v3s16(0,-1,0), // bottom
3203 v3s16(-1,0,0), // left
3205 for(u16 i=0; i<7; i++)
3207 v3s16 p2 = p + dirs[i];
3208 // Block position of neighbor (or requested) node
3209 v3s16 blockpos = getNodeBlockPos(p2);
3210 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3211 if(blockref == NULL)
3213 // Relative position of requested node
3214 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3215 blockref->setTempMod(relpos, mod);
3217 return getNodeBlockPos(p);
3219 v3s16 ClientMap::clearTempMod(v3s16 p)
3222 v3s16(0,0,0), // this
3223 v3s16(0,0,1), // back
3224 v3s16(0,1,0), // top
3225 v3s16(1,0,0), // right
3226 v3s16(0,0,-1), // front
3227 v3s16(0,-1,0), // bottom
3228 v3s16(-1,0,0), // left
3230 for(u16 i=0; i<7; i++)
3232 v3s16 p2 = p + dirs[i];
3233 // Block position of neighbor (or requested) node
3234 v3s16 blockpos = getNodeBlockPos(p2);
3235 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3236 if(blockref == NULL)
3238 // Relative position of requested node
3239 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3240 blockref->clearTempMod(relpos);
3242 return getNodeBlockPos(p);
3245 void ClientMap::PrintInfo(std::ostream &out)
3256 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3261 MapVoxelManipulator::~MapVoxelManipulator()
3263 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3268 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3270 TimeTaker timer1("emerge", &emerge_time);
3272 // Units of these are MapBlocks
3273 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3274 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3276 VoxelArea block_area_nodes
3277 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3279 addArea(block_area_nodes);
3281 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3282 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3283 for(s32 x=p_min.X; x<=p_max.X; x++)
3286 core::map<v3s16, bool>::Node *n;
3287 n = m_loaded_blocks.find(p);
3291 bool block_data_inexistent = false;
3294 TimeTaker timer1("emerge load", &emerge_load_time);
3296 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3297 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3300 dstream<<std::endl;*/
3302 MapBlock *block = m_map->getBlockNoCreate(p);
3303 if(block->isDummy())
3304 block_data_inexistent = true;
3306 block->copyTo(*this);
3308 catch(InvalidPositionException &e)
3310 block_data_inexistent = true;
3313 if(block_data_inexistent)
3315 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3316 // Fill with VOXELFLAG_INEXISTENT
3317 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3318 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3320 s32 i = m_area.index(a.MinEdge.X,y,z);
3321 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3325 m_loaded_blocks.insert(p, true);
3328 //dstream<<"emerge done"<<std::endl;
3333 void MapVoxelManipulator::emerge(VoxelArea a)
3335 TimeTaker timer1("emerge", &emerge_time);
3337 v3s16 size = a.getExtent();
3339 VoxelArea padded = a;
3340 padded.pad(m_area.getExtent() / 4);
3343 for(s16 z=0; z<size.Z; z++)
3344 for(s16 y=0; y<size.Y; y++)
3345 for(s16 x=0; x<size.X; x++)
3348 s32 i = m_area.index(a.MinEdge + p);
3349 // Don't touch nodes that have already been loaded
3350 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
3354 TimeTaker timer1("emerge load", &emerge_load_time);
3355 MapNode n = m_map->getNode(a.MinEdge + p);
3359 catch(InvalidPositionException &e)
3361 m_flags[i] = VOXELFLAG_INEXISTENT;
3369 TODO: Add an option to only update eg. water and air nodes.
3370 This will make it interfere less with important stuff if
3373 void MapVoxelManipulator::blitBack
3374 (core::map<v3s16, MapBlock*> & modified_blocks)
3376 if(m_area.getExtent() == v3s16(0,0,0))
3379 //TimeTaker timer1("blitBack");
3382 Initialize block cache
3384 v3s16 blockpos_last;
3385 MapBlock *block = NULL;
3386 bool block_checked_in_modified = false;
3388 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3389 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3390 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3394 u8 f = m_flags[m_area.index(p)];
3395 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3398 MapNode &n = m_data[m_area.index(p)];
3400 v3s16 blockpos = getNodeBlockPos(p);
3405 if(block == NULL || blockpos != blockpos_last){
3406 block = m_map->getBlockNoCreate(blockpos);
3407 blockpos_last = blockpos;
3408 block_checked_in_modified = false;
3411 // Calculate relative position in block
3412 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3414 // Don't continue if nothing has changed here
3415 if(block->getNode(relpos) == n)
3418 //m_map->setNode(m_area.MinEdge + p, n);
3419 block->setNode(relpos, n);
3422 Make sure block is in modified_blocks
3424 if(block_checked_in_modified == false)
3426 modified_blocks[blockpos] = block;
3427 block_checked_in_modified = true;
3430 catch(InvalidPositionException &e)