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"
34 Map::Map(std::ostream &dout):
36 m_camera_position(0,0,0),
37 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.size()<<" blocks."<<std::endl;*/
628 //TimeTaker timer("updateLighting");
632 //u32 count_was = modified_blocks.size();
634 core::map<v3s16, bool> light_sources;
636 core::map<v3s16, u8> unlight_from;
638 core::map<v3s16, MapBlock*>::Iterator i;
639 i = a_blocks.getIterator();
640 for(; i.atEnd() == false; i++)
642 MapBlock *block = i.getNode()->getValue();
646 // Don't bother with dummy blocks.
650 v3s16 pos = block->getPos();
651 modified_blocks.insert(pos, block);
654 Clear all light from block
656 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
657 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
658 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
663 MapNode n = block->getNode(v3s16(x,y,z));
664 u8 oldlight = n.getLight(bank);
666 block->setNode(v3s16(x,y,z), n);
668 // Collect borders for unlighting
669 if(x==0 || x == MAP_BLOCKSIZE-1
670 || y==0 || y == MAP_BLOCKSIZE-1
671 || z==0 || z == MAP_BLOCKSIZE-1)
673 v3s16 p_map = p + v3s16(
676 MAP_BLOCKSIZE*pos.Z);
677 unlight_from.insert(p_map, oldlight);
680 catch(InvalidPositionException &e)
683 This would happen when dealing with a
687 dstream<<"updateLighting(): InvalidPositionException"
692 if(bank == LIGHTBANK_DAY)
694 bool bottom_valid = block->propagateSunlight(light_sources);
696 // If bottom is valid, we're done.
700 else if(bank == LIGHTBANK_NIGHT)
709 /*dstream<<"Bottom for sunlight-propagated block ("
710 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
713 // Else get the block below and loop to it
717 block = getBlockNoCreate(pos);
719 catch(InvalidPositionException &e)
729 TimeTaker timer("unspreadLight");
730 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
735 u32 diff = modified_blocks.size() - count_was;
736 count_was = modified_blocks.size();
737 dstream<<"unspreadLight modified "<<diff<<std::endl;
740 // TODO: Spread light from propagated sunlight?
741 // Yes, add it to light_sources... somehow.
742 // It has to be added at somewhere above, in the loop.
744 // NOTE: This actually works fine without doing so
745 // - Find out why it works
748 TimeTaker timer("spreadLight");
749 spreadLight(bank, light_sources, modified_blocks);
754 u32 diff = modified_blocks.size() - count_was;
755 count_was = modified_blocks.size();
756 dstream<<"spreadLight modified "<<diff<<std::endl;
761 //MapVoxelManipulator vmanip(this);
763 ManualMapVoxelManipulator vmanip(this);
765 core::map<v3s16, MapBlock*>::Iterator i;
766 i = a_blocks.getIterator();
767 for(; i.atEnd() == false; i++)
769 MapBlock *block = i.getNode()->getValue();
770 v3s16 p = block->getPos();
771 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
774 //TimeTaker timer("unSpreadLight");
775 vmanip.unspreadLight(bank, unlight_from, light_sources);
778 //TimeTaker timer("spreadLight");
779 vmanip.spreadLight(bank, light_sources);
782 //TimeTaker timer("blitBack");
783 vmanip.blitBack(modified_blocks);
785 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
789 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
792 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
793 core::map<v3s16, MapBlock*> & modified_blocks)
795 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
796 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
799 Update information about whether day and night light differ
801 for(core::map<v3s16, MapBlock*>::Iterator
802 i = modified_blocks.getIterator();
803 i.atEnd() == false; i++)
805 MapBlock *block = i.getNode()->getValue();
806 block->updateDayNightDiff();
811 This is called after changing a node from transparent to opaque.
812 The lighting value of the node should be left as-is after changing
813 other values. This sets the lighting value to 0.
815 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
816 core::map<v3s16, MapBlock*> &modified_blocks)*/
817 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
818 core::map<v3s16, MapBlock*> &modified_blocks)
821 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
822 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
825 From this node to nodes underneath:
826 If lighting is sunlight (1.0), unlight neighbours and
831 v3s16 toppos = p + v3s16(0,1,0);
832 v3s16 bottompos = p + v3s16(0,-1,0);
834 bool node_under_sunlight = true;
835 core::map<v3s16, bool> light_sources;
838 If there is a node at top and it doesn't have sunlight,
839 there has not been any sunlight going down.
841 Otherwise there probably is.
844 MapNode topnode = getNode(toppos);
846 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
847 node_under_sunlight = false;
849 catch(InvalidPositionException &e)
853 if(n.d != CONTENT_TORCH)
856 If there is grass below, change it to mud
859 MapNode bottomnode = getNode(bottompos);
861 if(bottomnode.d == CONTENT_GRASS
862 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
864 bottomnode.d = CONTENT_MUD;
865 setNode(bottompos, bottomnode);
868 catch(InvalidPositionException &e)
873 enum LightBank banks[] =
878 for(s32 i=0; i<2; i++)
880 enum LightBank bank = banks[i];
882 u8 lightwas = getNode(p).getLight(bank);
884 // Add the block of the added node to modified_blocks
885 v3s16 blockpos = getNodeBlockPos(p);
886 MapBlock * block = getBlockNoCreate(blockpos);
887 assert(block != NULL);
888 modified_blocks.insert(blockpos, block);
890 if(isValidPosition(p) == false)
893 // Unlight neighbours of node.
894 // This means setting light of all consequent dimmer nodes
896 // This also collects the nodes at the border which will spread
897 // light again into this.
898 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
906 If node is under sunlight, take all sunlighted nodes under
907 it and clear light from them and from where the light has
909 TODO: This could be optimized by mass-unlighting instead
912 if(node_under_sunlight)
916 //m_dout<<DTIME<<"y="<<y<<std::endl;
917 v3s16 n2pos(p.X, y, p.Z);
923 catch(InvalidPositionException &e)
928 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
930 //m_dout<<DTIME<<"doing"<<std::endl;
931 unLightNeighbors(LIGHTBANK_DAY,
932 n2pos, n2.getLight(LIGHTBANK_DAY),
933 light_sources, modified_blocks);
934 n2.setLight(LIGHTBANK_DAY, 0);
942 for(s32 i=0; i<2; i++)
944 enum LightBank bank = banks[i];
947 Spread light from all nodes that might be capable of doing so
948 TODO: Convert to spreadLight
950 spreadLight(bank, light_sources, modified_blocks);
954 Update information about whether day and night light differ
956 for(core::map<v3s16, MapBlock*>::Iterator
957 i = modified_blocks.getIterator();
958 i.atEnd() == false; i++)
960 MapBlock *block = i.getNode()->getValue();
961 block->updateDayNightDiff();
965 Add neighboring liquid nodes and the node itself if it is
966 liquid (=water node was added) to transform queue.
969 v3s16(0,0,0), // self
970 v3s16(0,0,1), // back
972 v3s16(1,0,0), // right
973 v3s16(0,0,-1), // front
974 v3s16(0,-1,0), // bottom
975 v3s16(-1,0,0), // left
977 for(u16 i=0; i<7; i++)
982 v3s16 p2 = p + dirs[i];
984 MapNode n2 = getNode(p2);
985 if(content_liquid(n2.d))
987 m_transforming_liquid.push_back(p2);
990 }catch(InvalidPositionException &e)
998 void Map::removeNodeAndUpdate(v3s16 p,
999 core::map<v3s16, MapBlock*> &modified_blocks)
1001 /*PrintInfo(m_dout);
1002 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1003 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1005 bool node_under_sunlight = true;
1007 v3s16 toppos = p + v3s16(0,1,0);
1009 // Node will be replaced with this
1010 u8 replace_material = CONTENT_AIR;
1013 If there is a node at top and it doesn't have sunlight,
1014 there will be no sunlight going down.
1017 MapNode topnode = getNode(toppos);
1019 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1020 node_under_sunlight = false;
1022 catch(InvalidPositionException &e)
1026 core::map<v3s16, bool> light_sources;
1028 enum LightBank banks[] =
1033 for(s32 i=0; i<2; i++)
1035 enum LightBank bank = banks[i];
1038 Unlight neighbors (in case the node is a light source)
1040 unLightNeighbors(bank, p,
1041 getNode(p).getLight(bank),
1042 light_sources, modified_blocks);
1047 This also clears the lighting.
1051 n.d = replace_material;
1054 for(s32 i=0; i<2; i++)
1056 enum LightBank bank = banks[i];
1059 Recalculate lighting
1061 spreadLight(bank, light_sources, modified_blocks);
1064 // Add the block of the removed node to modified_blocks
1065 v3s16 blockpos = getNodeBlockPos(p);
1066 MapBlock * block = getBlockNoCreate(blockpos);
1067 assert(block != NULL);
1068 modified_blocks.insert(blockpos, block);
1071 If the removed node was under sunlight, propagate the
1072 sunlight down from it and then light all neighbors
1073 of the propagated blocks.
1075 if(node_under_sunlight)
1077 s16 ybottom = propagateSunlight(p, modified_blocks);
1078 /*m_dout<<DTIME<<"Node was under sunlight. "
1079 "Propagating sunlight";
1080 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1082 for(; y >= ybottom; y--)
1084 v3s16 p2(p.X, y, p.Z);
1085 /*m_dout<<DTIME<<"lighting neighbors of node ("
1086 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1088 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1093 // Set the lighting of this node to 0
1094 // TODO: Is this needed? Lighting is cleared up there already.
1096 MapNode n = getNode(p);
1097 n.setLight(LIGHTBANK_DAY, 0);
1100 catch(InvalidPositionException &e)
1106 for(s32 i=0; i<2; i++)
1108 enum LightBank bank = banks[i];
1110 // Get the brightest neighbour node and propagate light from it
1111 v3s16 n2p = getBrightestNeighbour(bank, p);
1113 MapNode n2 = getNode(n2p);
1114 lightNeighbors(bank, n2p, modified_blocks);
1116 catch(InvalidPositionException &e)
1122 Update information about whether day and night light differ
1124 for(core::map<v3s16, MapBlock*>::Iterator
1125 i = modified_blocks.getIterator();
1126 i.atEnd() == false; i++)
1128 MapBlock *block = i.getNode()->getValue();
1129 block->updateDayNightDiff();
1133 Add neighboring liquid nodes to transform queue.
1136 v3s16(0,0,1), // back
1137 v3s16(0,1,0), // top
1138 v3s16(1,0,0), // right
1139 v3s16(0,0,-1), // front
1140 v3s16(0,-1,0), // bottom
1141 v3s16(-1,0,0), // left
1143 for(u16 i=0; i<6; i++)
1148 v3s16 p2 = p + dirs[i];
1150 MapNode n2 = getNode(p2);
1151 if(content_liquid(n2.d))
1153 m_transforming_liquid.push_back(p2);
1156 }catch(InvalidPositionException &e)
1163 void Map::expireMeshes(bool only_daynight_diffed)
1165 TimeTaker timer("expireMeshes()");
1167 core::map<v2s16, MapSector*>::Iterator si;
1168 si = m_sectors.getIterator();
1169 for(; si.atEnd() == false; si++)
1171 MapSector *sector = si.getNode()->getValue();
1173 core::list< MapBlock * > sectorblocks;
1174 sector->getBlocks(sectorblocks);
1176 core::list< MapBlock * >::Iterator i;
1177 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1179 MapBlock *block = *i;
1181 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1187 JMutexAutoLock lock(block->mesh_mutex);
1188 if(block->mesh != NULL)
1190 /*block->mesh->drop();
1191 block->mesh = NULL;*/
1192 block->setMeshExpired(true);
1199 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1201 assert(mapType() == MAPTYPE_CLIENT);
1204 v3s16 p = blockpos + v3s16(0,0,0);
1205 MapBlock *b = getBlockNoCreate(p);
1206 b->updateMesh(daynight_ratio);
1208 catch(InvalidPositionException &e){}
1211 v3s16 p = blockpos + v3s16(-1,0,0);
1212 MapBlock *b = getBlockNoCreate(p);
1213 b->updateMesh(daynight_ratio);
1215 catch(InvalidPositionException &e){}
1217 v3s16 p = blockpos + v3s16(0,-1,0);
1218 MapBlock *b = getBlockNoCreate(p);
1219 b->updateMesh(daynight_ratio);
1221 catch(InvalidPositionException &e){}
1223 v3s16 p = blockpos + v3s16(0,0,-1);
1224 MapBlock *b = getBlockNoCreate(p);
1225 b->updateMesh(daynight_ratio);
1227 catch(InvalidPositionException &e){}
1230 v3s16 p = blockpos + v3s16(1,0,0);
1231 MapBlock *b = getBlockNoCreate(p);
1232 b->updateMesh(daynight_ratio);
1234 catch(InvalidPositionException &e){}
1236 v3s16 p = blockpos + v3s16(0,1,0);
1237 MapBlock *b = getBlockNoCreate(p);
1238 b->updateMesh(daynight_ratio);
1240 catch(InvalidPositionException &e){}
1242 v3s16 p = blockpos + v3s16(0,0,1);
1243 MapBlock *b = getBlockNoCreate(p);
1244 b->updateMesh(daynight_ratio);
1246 catch(InvalidPositionException &e){}*/
1251 bool Map::dayNightDiffed(v3s16 blockpos)
1254 v3s16 p = blockpos + v3s16(0,0,0);
1255 MapBlock *b = getBlockNoCreate(p);
1256 if(b->dayNightDiffed())
1259 catch(InvalidPositionException &e){}
1262 v3s16 p = blockpos + v3s16(-1,0,0);
1263 MapBlock *b = getBlockNoCreate(p);
1264 if(b->dayNightDiffed())
1267 catch(InvalidPositionException &e){}
1269 v3s16 p = blockpos + v3s16(0,-1,0);
1270 MapBlock *b = getBlockNoCreate(p);
1271 if(b->dayNightDiffed())
1274 catch(InvalidPositionException &e){}
1276 v3s16 p = blockpos + v3s16(0,0,-1);
1277 MapBlock *b = getBlockNoCreate(p);
1278 if(b->dayNightDiffed())
1281 catch(InvalidPositionException &e){}
1284 v3s16 p = blockpos + v3s16(1,0,0);
1285 MapBlock *b = getBlockNoCreate(p);
1286 if(b->dayNightDiffed())
1289 catch(InvalidPositionException &e){}
1291 v3s16 p = blockpos + v3s16(0,1,0);
1292 MapBlock *b = getBlockNoCreate(p);
1293 if(b->dayNightDiffed())
1296 catch(InvalidPositionException &e){}
1298 v3s16 p = blockpos + v3s16(0,0,1);
1299 MapBlock *b = getBlockNoCreate(p);
1300 if(b->dayNightDiffed())
1303 catch(InvalidPositionException &e){}
1309 Updates usage timers
1311 void Map::timerUpdate(float dtime)
1313 JMutexAutoLock lock(m_sector_mutex);
1315 core::map<v2s16, MapSector*>::Iterator si;
1317 si = m_sectors.getIterator();
1318 for(; si.atEnd() == false; si++)
1320 MapSector *sector = si.getNode()->getValue();
1321 sector->usage_timer += dtime;
1325 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1328 Wait for caches to be removed before continuing.
1330 This disables the existence of caches while locked
1332 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1334 core::list<v2s16>::Iterator j;
1335 for(j=list.begin(); j!=list.end(); j++)
1337 MapSector *sector = m_sectors[*j];
1340 sector->deleteBlocks();
1345 If sector is in sector cache, remove it from there
1347 if(m_sector_cache == sector)
1349 m_sector_cache = NULL;
1352 Remove from map and delete
1354 m_sectors.remove(*j);
1360 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1361 core::list<v3s16> *deleted_blocks)
1363 JMutexAutoLock lock(m_sector_mutex);
1365 core::list<v2s16> sector_deletion_queue;
1366 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1367 for(; i.atEnd() == false; i++)
1369 MapSector *sector = i.getNode()->getValue();
1371 Delete sector from memory if it hasn't been used in a long time
1373 if(sector->usage_timer > timeout)
1375 sector_deletion_queue.push_back(i.getNode()->getKey());
1377 if(deleted_blocks != NULL)
1379 // Collect positions of blocks of sector
1380 MapSector *sector = i.getNode()->getValue();
1381 core::list<MapBlock*> blocks;
1382 sector->getBlocks(blocks);
1383 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1384 i != blocks.end(); i++)
1386 deleted_blocks->push_back((*i)->getPos());
1391 deleteSectors(sector_deletion_queue, only_blocks);
1392 return sector_deletion_queue.getSize();
1395 void Map::PrintInfo(std::ostream &out)
1400 #define WATER_DROP_BOOST 4
1402 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1404 DSTACK(__FUNCTION_NAME);
1405 //TimeTaker timer("transformLiquids()");
1408 u32 initial_size = m_transforming_liquid.size();
1410 while(m_transforming_liquid.size() != 0)
1413 Get a queued transforming liquid node
1415 v3s16 p0 = m_transforming_liquid.pop_front();
1417 MapNode n0 = getNode(p0);
1419 // Don't deal with non-liquids
1420 if(content_liquid(n0.d) == false)
1423 bool is_source = !content_flowing_liquid(n0.d);
1425 u8 liquid_level = 8;
1426 if(is_source == false)
1427 liquid_level = n0.param2 & 0x0f;
1429 // Turn possible source into non-source
1430 u8 nonsource_c = make_liquid_flowing(n0.d);
1433 If not source, check that some node flows into this one
1434 and what is the level of liquid in this one
1436 if(is_source == false)
1438 s8 new_liquid_level_max = -1;
1440 v3s16 dirs_from[5] = {
1441 v3s16(0,1,0), // top
1442 v3s16(0,0,1), // back
1443 v3s16(1,0,0), // right
1444 v3s16(0,0,-1), // front
1445 v3s16(-1,0,0), // left
1447 for(u16 i=0; i<5; i++)
1452 bool from_top = (i==0);
1454 v3s16 p2 = p0 + dirs_from[i];
1455 MapNode n2 = getNode(p2);
1457 if(content_liquid(n2.d))
1459 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1460 // Check that the liquids are the same type
1461 if(n2_nonsource_c != nonsource_c)
1463 dstream<<"WARNING: Not handling: different liquids"
1464 " collide"<<std::endl;
1467 bool n2_is_source = !content_flowing_liquid(n2.d);
1468 s8 n2_liquid_level = 8;
1469 if(n2_is_source == false)
1470 n2_liquid_level = n2.param2 & 0x07;
1472 s8 new_liquid_level = -1;
1475 //new_liquid_level = 7;
1476 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1477 new_liquid_level = 7;
1479 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1481 else if(n2_liquid_level > 0)
1483 new_liquid_level = n2_liquid_level - 1;
1486 if(new_liquid_level > new_liquid_level_max)
1487 new_liquid_level_max = new_liquid_level;
1490 }catch(InvalidPositionException &e)
1496 If liquid level should be something else, update it and
1497 add all the neighboring water nodes to the transform queue.
1499 if(new_liquid_level_max != liquid_level)
1501 if(new_liquid_level_max == -1)
1503 // Remove water alltoghether
1510 n0.param2 = new_liquid_level_max;
1514 // Block has been modified
1516 v3s16 blockpos = getNodeBlockPos(p0);
1517 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1519 modified_blocks.insert(blockpos, block);
1523 Add neighboring non-source liquid nodes to transform queue.
1526 v3s16(0,0,1), // back
1527 v3s16(0,1,0), // top
1528 v3s16(1,0,0), // right
1529 v3s16(0,0,-1), // front
1530 v3s16(0,-1,0), // bottom
1531 v3s16(-1,0,0), // left
1533 for(u16 i=0; i<6; i++)
1538 v3s16 p2 = p0 + dirs[i];
1540 MapNode n2 = getNode(p2);
1541 if(content_flowing_liquid(n2.d))
1543 m_transforming_liquid.push_back(p2);
1546 }catch(InvalidPositionException &e)
1553 // Get a new one from queue if the node has turned into non-water
1554 if(content_liquid(n0.d) == false)
1558 Flow water from this node
1560 v3s16 dirs_to[5] = {
1561 v3s16(0,-1,0), // bottom
1562 v3s16(0,0,1), // back
1563 v3s16(1,0,0), // right
1564 v3s16(0,0,-1), // front
1565 v3s16(-1,0,0), // left
1567 for(u16 i=0; i<5; i++)
1572 bool to_bottom = (i == 0);
1574 // If liquid is at lowest possible height, it's not going
1575 // anywhere except down
1576 if(liquid_level == 0 && to_bottom == false)
1579 u8 liquid_next_level = 0;
1580 // If going to bottom
1583 //liquid_next_level = 7;
1584 if(liquid_level >= 7 - WATER_DROP_BOOST)
1585 liquid_next_level = 7;
1587 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1590 liquid_next_level = liquid_level - 1;
1592 bool n2_changed = false;
1593 bool flowed = false;
1595 v3s16 p2 = p0 + dirs_to[i];
1597 MapNode n2 = getNode(p2);
1598 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1600 if(content_liquid(n2.d))
1602 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1603 // Check that the liquids are the same type
1604 if(n2_nonsource_c != nonsource_c)
1606 dstream<<"WARNING: Not handling: different liquids"
1607 " collide"<<std::endl;
1610 bool n2_is_source = !content_flowing_liquid(n2.d);
1611 u8 n2_liquid_level = 8;
1612 if(n2_is_source == false)
1613 n2_liquid_level = n2.param2 & 0x07;
1622 // Just flow into the source, nothing changes.
1623 // n2_changed is not set because destination didn't change
1628 if(liquid_next_level > liquid_level)
1630 n2.param2 = liquid_next_level;
1638 else if(n2.d == CONTENT_AIR)
1641 n2.param2 = liquid_next_level;
1648 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1652 m_transforming_liquid.push_back(p2);
1654 v3s16 blockpos = getNodeBlockPos(p2);
1655 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1657 modified_blocks.insert(blockpos, block);
1660 // If n2_changed to bottom, don't flow anywhere else
1661 if(to_bottom && flowed && !is_source)
1664 }catch(InvalidPositionException &e)
1670 //if(loopcount >= 100000)
1671 if(loopcount >= initial_size * 1)
1674 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1681 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1686 Experimental and debug stuff
1690 dstream<<"Generating map point attribute lists"<<std::endl;
1692 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1693 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1694 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1695 PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1696 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1699 NOTE: BEWARE: Too big amount of these will make map generation
1700 slow. Especially those that are read by every block emerge.
1708 for(u32 i=0; i<5000; i++)
1710 /*u32 lim = MAP_GENERATION_LIMIT;
1714 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1717 -lim + myrand()%(lim*2),
1719 -lim + myrand()%(lim*2)
1721 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1722 plants_amount = pow(plants_amount, 5);
1723 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1725 float plants_amount = 0;
1728 plants_amount = 1.5;
1730 else if(myrand()%4 == 0)
1732 plants_amount = 0.5;
1734 else if(myrand()%2 == 0)
1736 plants_amount = 0.03;
1740 plants_amount = 0.0;
1744 list_plants_amount->addPoint(p, Attribute(plants_amount));
1747 for(u32 i=0; i<1000; i++)
1749 /*u32 lim = MAP_GENERATION_LIMIT;
1753 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000;
1756 -lim + myrand()%(lim*2),
1758 -lim + myrand()%(lim*2)
1761 float caves_amount = 0;
1766 else if(myrand()%3 == 0)
1772 caves_amount = 0.05;
1775 list_caves_amount->addPoint(p, Attribute(caves_amount));
1778 for(u32 i=0; i<5000; i++)
1780 /*u32 lim = MAP_GENERATION_LIMIT;
1784 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1787 -lim + (myrand()%(lim*2)),
1789 -lim + (myrand()%(lim*2))
1792 /*s32 bh_i = (myrand()%200) - 50;
1793 float baseheight = (float)bh_i;
1797 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1798 randmax = pow(randmax, e);
1800 //float randmax = (float)(myrand()%60);
1801 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1803 float baseheight = 0;
1805 float randfactor = 0;
1807 /*if(myrand()%5 == 0)
1813 else if(myrand()%6 == 0)
1819 else if(myrand()%4 == 0)
1825 else if(myrand()%3 == 0)
1851 list_baseheight->addPoint(p, Attribute(baseheight));
1852 list_randmax->addPoint(p, Attribute(randmax));
1853 list_randfactor->addPoint(p, Attribute(randfactor));
1856 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
1857 list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
1858 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
1861 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1862 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1863 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1867 Try to load map; if not found, create a new one.
1870 m_savedir = savedir;
1871 m_map_saving_enabled = false;
1875 // If directory exists, check contents and load if possible
1876 if(fs::PathExists(m_savedir))
1878 // If directory is empty, it is safe to save into it.
1879 if(fs::GetDirListing(m_savedir).size() == 0)
1881 dstream<<DTIME<<"Server: Empty save directory is valid."
1883 m_map_saving_enabled = true;
1887 // Load master heightmap
1888 loadMasterHeightmap();
1890 // Load sector (0,0) and throw and exception on fail
1891 if(loadSectorFull(v2s16(0,0)) == false)
1892 throw LoadError("Failed to load sector (0,0)");
1894 dstream<<DTIME<<"Server: Successfully loaded master "
1895 "heightmap and sector (0,0) from "<<savedir<<
1896 ", assuming valid save directory."
1899 m_map_saving_enabled = true;
1900 // Map loaded, not creating new one
1904 // If directory doesn't exist, it is safe to save to it
1906 m_map_saving_enabled = true;
1909 catch(std::exception &e)
1911 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1912 <<", exception: "<<e.what()<<std::endl;
1913 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1914 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1917 dstream<<DTIME<<"Initializing new map."<<std::endl;
1919 // Create master heightmap
1920 /*ValueGenerator *maxgen =
1921 ValueGenerator::deSerialize(hmp.randmax);
1922 ValueGenerator *factorgen =
1923 ValueGenerator::deSerialize(hmp.randfactor);
1924 ValueGenerator *basegen =
1925 ValueGenerator::deSerialize(hmp.base);
1926 m_heightmap = new UnlimitedHeightmap
1927 (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
1929 /*m_heightmap = new UnlimitedHeightmap
1930 (hmp.blocksize, &m_padb);*/
1932 m_heightmap = new UnlimitedHeightmap
1935 // Set map parameters
1938 // Create zero sector
1939 emergeSector(v2s16(0,0));
1941 // Initially write whole map
1945 ServerMap::~ServerMap()
1949 if(m_map_saving_enabled)
1952 // Save only changed parts
1954 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1958 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1961 catch(std::exception &e)
1963 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1964 <<", exception: "<<e.what()<<std::endl;
1967 if(m_heightmap != NULL)
1971 MapSector * ServerMap::emergeSector(v2s16 p2d)
1973 DSTACK("%s: p2d=(%d,%d)",
1976 // Check that it doesn't exist already
1978 return getSectorNoGenerate(p2d);
1980 catch(InvalidPositionException &e)
1985 Try to load the sector from disk.
1987 if(loadSectorFull(p2d) == true)
1989 return getSectorNoGenerate(p2d);
1993 If there is no master heightmap, throw.
1995 if(m_heightmap == NULL)
1997 throw InvalidPositionException("emergeSector(): no heightmap");
2001 Do not generate over-limit
2003 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2004 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2005 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2006 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2007 throw InvalidPositionException("emergeSector(): pos. over limit");
2010 Generate sector and heightmaps
2013 // Number of heightmaps in sector in each direction
2014 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
2016 // Heightmap side width
2017 s16 hm_d = MAP_BLOCKSIZE / hm_split;
2019 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
2021 // Sector position on map in nodes
2022 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2024 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
2025 " heightmaps and objects"<<std::endl;*/
2028 Calculate some information about local properties
2031 v2s16 mhm_p = p2d * hm_split;
2033 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
2034 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
2035 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
2036 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
2039 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
2040 float avgslope = 0.0;
2041 avgslope += fabs(avgheight - corners[0]);
2042 avgslope += fabs(avgheight - corners[1]);
2043 avgslope += fabs(avgheight - corners[2]);
2044 avgslope += fabs(avgheight - corners[3]);
2046 avgslope /= MAP_BLOCKSIZE;
2047 //dstream<<"avgslope="<<avgslope<<std::endl;
2049 float pitness = 0.0;
2051 a = m_heightmap->getSlope(p2d+v2s16(0,0));
2054 a = m_heightmap->getSlope(p2d+v2s16(0,1));
2057 a = m_heightmap->getSlope(p2d+v2s16(1,1));
2060 a = m_heightmap->getSlope(p2d+v2s16(1,0));
2064 pitness /= MAP_BLOCKSIZE;
2065 //dstream<<"pitness="<<pitness<<std::endl;
2068 Get local attributes
2071 float local_plants_amount = 0.0;
2073 //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
2074 //TimeTaker attrtimer("emergeSector() attribute fetch");
2076 // Get plant amount from attributes
2077 PointAttributeList *palist = m_padb.getList("plants_amount");
2079 /*local_plants_amount =
2080 palist->getNearAttr(nodepos2d).getFloat();*/
2081 local_plants_amount =
2082 palist->getInterpolatedFloat(nodepos2d);
2086 Generate sector heightmap
2089 // Loop through sub-heightmaps
2090 for(s16 y=0; y<hm_split; y++)
2091 for(s16 x=0; x<hm_split; x++)
2093 v2s16 p_in_sector = v2s16(x,y);
2094 v2s16 mhm_p = p2d * hm_split + p_in_sector;
2096 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
2097 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
2098 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
2099 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
2102 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
2103 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
2106 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
2108 sector->setHeightmap(p_in_sector, hm);
2110 //TODO: Make these values configurable
2111 //hm->generateContinued(0.0, 0.0, corners);
2112 //hm->generateContinued(0.25, 0.2, corners);
2113 //hm->generateContinued(0.5, 0.2, corners);
2114 //hm->generateContinued(1.0, 0.2, corners);
2115 //hm->generateContinued(2.0, 0.2, corners);
2116 //hm->generateContinued(2.0 * avgslope, 0.5, corners);
2117 hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners);
2126 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
2127 sector->setObjects(objects);
2129 float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
2132 Plant some trees if there is not much slope
2135 // Avgslope is the derivative of a hill
2136 //float t = avgslope * avgslope;
2138 float a = area/16 * m_params.plants_amount * local_plants_amount;
2140 //float something = 0.17*0.17;
2141 float something = 0.3;
2143 tree_max = a / (t/something);
2147 u32 count = (myrand()%(tree_max+1));
2148 //u32 count = tree_max;
2149 for(u32 i=0; i<count; i++)
2151 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
2152 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
2153 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2156 objects->insert(v3s16(x, y, z),
2157 SECTOR_OBJECT_TREE_1);
2161 Plant some bushes if sector is pit-like
2164 // Pitness usually goes at around -0.5...0.5
2166 u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount;
2168 bush_max = (pitness*a*4);
2171 u32 count = (myrand()%(bush_max+1));
2172 for(u32 i=0; i<count; i++)
2174 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
2175 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
2176 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2179 objects->insert(v3s16(x, y, z),
2180 SECTOR_OBJECT_BUSH_1);
2184 Add ravine (randomly)
2186 if(m_params.ravines_amount > 0.001)
2188 if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
2191 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2192 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2195 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2196 objects->insert(v3s16(x, y, z),
2197 SECTOR_OBJECT_RAVINE);
2204 JMutexAutoLock lock(m_sector_mutex);
2205 m_sectors.insert(p2d, sector);
2210 MapBlock * ServerMap::emergeBlock(
2212 bool only_from_disk,
2213 core::map<v3s16, MapBlock*> &changed_blocks,
2214 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2217 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
2219 p.X, p.Y, p.Z, only_from_disk);
2221 /*dstream<<"ServerMap::emergeBlock(): "
2222 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2223 <<", only_from_disk="<<only_from_disk<<std::endl;*/
2224 v2s16 p2d(p.X, p.Z);
2227 This will create or load a sector if not found in memory.
2228 If block exists on disk, it will be loaded.
2230 NOTE: On old save formats, this will be slow, as it generates
2231 lighting on blocks for them.
2233 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
2234 assert(sector->getId() == MAPSECTOR_SERVER);
2236 // Try to get a block from the sector
2237 MapBlock *block = NULL;
2238 bool not_on_disk = false;
2240 block = sector->getBlockNoCreate(block_y);
2241 if(block->isDummy() == true)
2246 catch(InvalidPositionException &e)
2252 If block was not found on disk and not going to generate a
2253 new one, make sure there is a dummy block in place.
2255 if(not_on_disk && only_from_disk)
2259 // Create dummy block
2260 block = new MapBlock(this, p, true);
2262 // Add block to sector
2263 sector->insertBlock(block);
2269 //dstream<<"Not found on disk, generating."<<std::endl;
2271 //TimeTaker("emergeBlock() generate");
2274 Do not generate over-limit
2276 if(blockpos_over_limit(p))
2277 throw InvalidPositionException("emergeBlock(): pos. over limit");
2282 Go on generating the block.
2284 TODO: If a dungeon gets generated so that it's side gets
2285 revealed to the outside air, the lighting should be
2290 If block doesn't exist, create one.
2291 If it exists, it is a dummy. In that case unDummify() it.
2293 NOTE: This already sets the map as the parent of the block
2297 block = sector->createBlankBlockNoInsert(block_y);
2301 // Remove the block so that nobody can get a half-generated one.
2302 sector->removeBlock(block);
2303 // Allocate the block to contain the generated data
2307 /*u8 water_material = CONTENT_WATER;
2308 if(g_settings.getBool("endless_water"))
2309 water_material = CONTENT_WATERSOURCE;*/
2310 u8 water_material = CONTENT_WATERSOURCE;
2312 s32 lowest_ground_y = 32767;
2313 s32 highest_ground_y = -32768;
2316 //sector->printHeightmaps();
2318 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2319 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2321 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
2323 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
2324 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
2325 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
2327 dstream<<"WARNING: Surface height not found in sector "
2328 "for block that is being emerged"<<std::endl;
2332 s16 surface_y = surface_y_f;
2333 //avg_ground_y += surface_y;
2334 if(surface_y < lowest_ground_y)
2335 lowest_ground_y = surface_y;
2336 if(surface_y > highest_ground_y)
2337 highest_ground_y = surface_y;
2339 s32 surface_depth = 0;
2341 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
2343 //float min_slope = 0.45;
2344 //float max_slope = 0.85;
2345 float min_slope = 0.60;
2346 float max_slope = 1.20;
2347 float min_slope_depth = 5.0;
2348 float max_slope_depth = 0;
2350 if(slope < min_slope)
2351 surface_depth = min_slope_depth;
2352 else if(slope > max_slope)
2353 surface_depth = max_slope_depth;
2355 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2357 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2359 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2364 NOTE: If there are some man-made structures above the
2365 newly created block, they won't be taken into account.
2367 if(real_y > surface_y)
2368 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2374 // If node is over heightmap y, it's air or water
2375 if(real_y > surface_y)
2377 // If under water level, it's water
2378 if(real_y < WATER_LEVEL)
2380 n.d = water_material;
2381 n.setLight(LIGHTBANK_DAY,
2382 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2384 Add to transforming liquid queue (in case it'd
2387 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
2388 m_transforming_liquid.push_back(real_pos);
2394 // Else it's ground or dungeons (air)
2397 // If it's surface_depth under ground, it's stone
2398 if(real_y <= surface_y - surface_depth)
2400 n.d = CONTENT_STONE;
2404 // It is mud if it is under the first ground
2405 // level or under water
2406 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2412 n.d = CONTENT_GRASS;
2415 //n.d = CONTENT_MUD;
2417 /*// If under water level, it's mud
2418 if(real_y < WATER_LEVEL)
2420 // Only the topmost node is grass
2421 else if(real_y <= surface_y - 1)
2424 n.d = CONTENT_GRASS;*/
2428 block->setNode(v3s16(x0,y0,z0), n);
2433 Calculate some helper variables
2436 // Completely underground if the highest part of block is under lowest
2438 // This has to be very sure; it's probably one too strict now but
2439 // that's just better.
2440 bool completely_underground =
2441 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2443 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2445 bool mostly_underwater_surface = false;
2446 if(highest_ground_y < WATER_LEVEL
2447 && some_part_underground && !completely_underground)
2448 mostly_underwater_surface = true;
2451 Get local attributes
2454 //dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
2456 float caves_amount = 0;
2460 NOTE: BEWARE: Too big amount of attribute points slows verything
2462 1 interpolation from 5000 points takes 2-3ms.
2464 //TimeTaker timer("emergeBlock() local attribute retrieval");
2465 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2466 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
2467 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
2470 //dstream<<"emergeBlock(): Done"<<std::endl;
2476 // Initialize temporary table
2477 const s32 ued = MAP_BLOCKSIZE;
2478 bool underground_emptiness[ued*ued*ued];
2479 for(s32 i=0; i<ued*ued*ued; i++)
2481 underground_emptiness[i] = 0;
2488 Initialize orp and ors. Try to find if some neighboring
2489 MapBlock has a tunnel ended in its side
2493 (float)(myrand()%ued)+0.5,
2494 (float)(myrand()%ued)+0.5,
2495 (float)(myrand()%ued)+0.5
2498 bool found_existing = false;
2504 for(s16 y=0; y<ued; y++)
2505 for(s16 x=0; x<ued; x++)
2507 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2508 if(getNode(ap).d == CONTENT_AIR)
2510 orp = v3f(x+1,y+1,0);
2511 found_existing = true;
2512 goto continue_generating;
2516 catch(InvalidPositionException &e){}
2522 for(s16 y=0; y<ued; y++)
2523 for(s16 x=0; x<ued; x++)
2525 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2526 if(getNode(ap).d == CONTENT_AIR)
2528 orp = v3f(x+1,y+1,ued-1);
2529 found_existing = true;
2530 goto continue_generating;
2534 catch(InvalidPositionException &e){}
2540 for(s16 y=0; y<ued; y++)
2541 for(s16 z=0; z<ued; z++)
2543 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2544 if(getNode(ap).d == CONTENT_AIR)
2546 orp = v3f(0,y+1,z+1);
2547 found_existing = true;
2548 goto continue_generating;
2552 catch(InvalidPositionException &e){}
2558 for(s16 y=0; y<ued; y++)
2559 for(s16 z=0; z<ued; z++)
2561 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2562 if(getNode(ap).d == CONTENT_AIR)
2564 orp = v3f(ued-1,y+1,z+1);
2565 found_existing = true;
2566 goto continue_generating;
2570 catch(InvalidPositionException &e){}
2576 for(s16 x=0; x<ued; x++)
2577 for(s16 z=0; z<ued; z++)
2579 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2580 if(getNode(ap).d == CONTENT_AIR)
2582 orp = v3f(x+1,0,z+1);
2583 found_existing = true;
2584 goto continue_generating;
2588 catch(InvalidPositionException &e){}
2594 for(s16 x=0; x<ued; x++)
2595 for(s16 z=0; z<ued; z++)
2597 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2598 if(getNode(ap).d == CONTENT_AIR)
2600 orp = v3f(x+1,ued-1,z+1);
2601 found_existing = true;
2602 goto continue_generating;
2606 catch(InvalidPositionException &e){}
2608 continue_generating:
2611 Choose whether to actually generate dungeon
2613 bool do_generate_dungeons = true;
2614 // Don't generate if no part is underground
2615 if(!some_part_underground)
2617 do_generate_dungeons = false;
2619 // Don't generate if mostly underwater surface
2620 else if(mostly_underwater_surface)
2622 do_generate_dungeons = false;
2624 // Partly underground = cave
2625 else if(!completely_underground)
2627 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2629 // Found existing dungeon underground
2630 else if(found_existing && completely_underground)
2632 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2634 // Underground and no dungeons found
2637 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
2640 if(do_generate_dungeons)
2643 Generate some tunnel starting from orp and ors
2645 for(u16 i=0; i<3; i++)
2648 (float)(myrand()%ued)+0.5,
2649 (float)(myrand()%ued)+0.5,
2650 (float)(myrand()%ued)+0.5
2654 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
2658 for(float f=0; f<1.0; f+=0.04)
2660 v3f fp = orp + vec * f;
2661 v3s16 cp(fp.X, fp.Y, fp.Z);
2663 s16 d1 = d0 + rs - 1;
2664 for(s16 z0=d0; z0<=d1; z0++)
2666 s16 si = rs - abs(z0);
2667 for(s16 x0=-si; x0<=si-1; x0++)
2669 s16 si2 = rs - abs(x0);
2670 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2676 if(isInArea(p, ued) == false)
2678 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2690 // Set to true if has caves.
2691 // Set when some non-air is changed to air when making caves.
2692 bool has_dungeons = false;
2695 Apply temporary cave data to block
2698 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2699 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2701 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2703 MapNode n = block->getNode(v3s16(x0,y0,z0));
2706 if(underground_emptiness[
2707 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2708 +ued*(y0*ued/MAP_BLOCKSIZE)
2709 +(x0*ued/MAP_BLOCKSIZE)])
2711 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
2714 has_dungeons = true;
2720 block->setNode(v3s16(x0,y0,z0), n);
2725 This is used for guessing whether or not the block should
2726 receive sunlight from the top if the top block doesn't exist
2728 block->setIsUnderground(completely_underground);
2731 Force lighting update if some part of block is partly
2732 underground and has caves.
2734 /*if(some_part_underground && !completely_underground && has_dungeons)
2736 //dstream<<"Half-ground caves"<<std::endl;
2737 lighting_invalidated_blocks[block->getPos()] = block;
2740 // DEBUG: Always update lighting
2741 //lighting_invalidated_blocks[block->getPos()] = block;
2747 if(some_part_underground)
2749 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2754 for(s16 i=0; i<underground_level/4 + 1; i++)
2756 if(myrand()%50 == 0)
2759 (myrand()%(MAP_BLOCKSIZE-2))+1,
2760 (myrand()%(MAP_BLOCKSIZE-2))+1,
2761 (myrand()%(MAP_BLOCKSIZE-2))+1
2767 for(u16 i=0; i<27; i++)
2769 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
2771 block->setNode(cp+g_27dirs[i], n);
2779 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2780 u16 coal_rareness = 60 / coal_amount;
2781 if(coal_rareness == 0)
2783 if(myrand()%coal_rareness == 0)
2785 u16 a = myrand() % 16;
2786 u16 amount = coal_amount * a*a*a / 1000;
2787 for(s16 i=0; i<amount; i++)
2790 (myrand()%(MAP_BLOCKSIZE-2))+1,
2791 (myrand()%(MAP_BLOCKSIZE-2))+1,
2792 (myrand()%(MAP_BLOCKSIZE-2))+1
2796 n.d = CONTENT_STONE;
2797 n.param = MINERAL_COAL;
2799 for(u16 i=0; i<27; i++)
2801 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
2803 block->setNode(cp+g_27dirs[i], n);
2811 //TODO: change to iron_amount or whatever
2812 u16 iron_amount = 30.0 * g_settings.getFloat("coal_amount");
2813 u16 iron_rareness = 60 / iron_amount;
2814 if(iron_rareness == 0)
2816 if(myrand()%iron_rareness == 0)
2818 u16 a = myrand() % 16;
2819 u16 amount = iron_amount * a*a*a / 1000;
2820 for(s16 i=0; i<amount; i++)
2823 (myrand()%(MAP_BLOCKSIZE-2))+1,
2824 (myrand()%(MAP_BLOCKSIZE-2))+1,
2825 (myrand()%(MAP_BLOCKSIZE-2))+1
2829 n.d = CONTENT_STONE;
2830 n.param = MINERAL_IRON;
2832 for(u16 i=0; i<27; i++)
2834 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
2836 block->setNode(cp+g_27dirs[i], n);
2843 Create a few rats in empty blocks underground
2845 if(completely_underground)
2847 //for(u16 i=0; i<2; i++)
2850 (myrand()%(MAP_BLOCKSIZE-2))+1,
2851 (myrand()%(MAP_BLOCKSIZE-2))+1,
2852 (myrand()%(MAP_BLOCKSIZE-2))+1
2855 // Check that the place is empty
2856 //if(!is_ground_content(block->getNode(cp).d))
2859 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2860 block->addObject(obj);
2866 Add block to sector.
2868 sector->insertBlock(block);
2874 // An y-wise container of changed blocks
2875 core::map<s16, MapBlock*> changed_blocks_sector;
2878 Check if any sector's objects can be placed now.
2881 core::map<v3s16, u8> *objects = sector->getObjects();
2882 core::list<v3s16> objects_to_remove;
2883 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2884 i.atEnd() == false; i++)
2886 v3s16 p = i.getNode()->getKey();
2888 u8 d = i.getNode()->getValue();
2890 // Ground level point (user for stuff that is on ground)
2892 bool ground_found = true;
2894 // Search real ground level
2898 MapNode n = sector->getNode(gp);
2900 // If not air, go one up and continue to placing the tree
2901 if(n.d != CONTENT_AIR)
2907 // If air, go one down
2908 gp += v3s16(0,-1,0);
2910 }catch(InvalidPositionException &e)
2912 // Ground not found.
2913 ground_found = false;
2914 // This is most close to ground
2921 if(d == SECTOR_OBJECT_TEST)
2923 if(sector->isValidArea(p + v3s16(0,0,0),
2924 p + v3s16(0,0,0), &changed_blocks_sector))
2927 n.d = CONTENT_TORCH;
2928 sector->setNode(p, n);
2929 objects_to_remove.push_back(p);
2932 else if(d == SECTOR_OBJECT_TREE_1)
2934 if(ground_found == false)
2937 v3s16 p_min = gp + v3s16(-1,0,-1);
2938 v3s16 p_max = gp + v3s16(1,5,1);
2939 if(sector->isValidArea(p_min, p_max,
2940 &changed_blocks_sector))
2944 sector->setNode(gp+v3s16(0,0,0), n);
2945 sector->setNode(gp+v3s16(0,1,0), n);
2946 sector->setNode(gp+v3s16(0,2,0), n);
2947 sector->setNode(gp+v3s16(0,3,0), n);
2949 n.d = CONTENT_LEAVES;
2951 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2953 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2954 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2955 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2956 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2957 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2958 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2959 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2960 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2962 sector->setNode(gp+v3s16(0,4,0), n);
2964 sector->setNode(gp+v3s16(-1,4,0), n);
2965 sector->setNode(gp+v3s16(1,4,0), n);
2966 sector->setNode(gp+v3s16(0,4,-1), n);
2967 sector->setNode(gp+v3s16(0,4,1), n);
2968 sector->setNode(gp+v3s16(1,4,1), n);
2969 sector->setNode(gp+v3s16(-1,4,1), n);
2970 sector->setNode(gp+v3s16(-1,4,-1), n);
2971 sector->setNode(gp+v3s16(1,4,-1), n);
2973 sector->setNode(gp+v3s16(-1,3,0), n);
2974 sector->setNode(gp+v3s16(1,3,0), n);
2975 sector->setNode(gp+v3s16(0,3,-1), n);
2976 sector->setNode(gp+v3s16(0,3,1), n);
2977 sector->setNode(gp+v3s16(1,3,1), n);
2978 sector->setNode(gp+v3s16(-1,3,1), n);
2979 sector->setNode(gp+v3s16(-1,3,-1), n);
2980 sector->setNode(gp+v3s16(1,3,-1), n);
2982 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2983 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2984 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2985 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2986 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2987 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2988 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2989 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2991 // Objects are identified by wanted position
2992 objects_to_remove.push_back(p);
2994 // Lighting has to be recalculated for this one.
2995 sector->getBlocksInArea(p_min, p_max,
2996 lighting_invalidated_blocks);
2999 else if(d == SECTOR_OBJECT_BUSH_1)
3001 if(ground_found == false)
3004 if(sector->isValidArea(gp + v3s16(0,0,0),
3005 gp + v3s16(0,0,0), &changed_blocks_sector))
3008 n.d = CONTENT_LEAVES;
3009 sector->setNode(gp+v3s16(0,0,0), n);
3011 // Objects are identified by wanted position
3012 objects_to_remove.push_back(p);
3015 else if(d == SECTOR_OBJECT_RAVINE)
3018 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
3019 v3s16 p_max = p + v3s16(6,6,6);
3020 if(sector->isValidArea(p_min, p_max,
3021 &changed_blocks_sector))
3024 n.d = CONTENT_STONE;
3027 s16 depth = maxdepth + (myrand()%10);
3029 s16 minz = -6 - (-2);
3031 for(s16 x=-6; x<=6; x++)
3033 z += -1 + (myrand()%3);
3038 for(s16 y=depth+(myrand()%2); y<=6; y++)
3040 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
3043 v3s16 p2 = p + v3s16(x,y,z-2);
3044 //if(is_ground_content(sector->getNode(p2).d))
3045 if(content_features(sector->getNode(p2).d).walkable)
3046 sector->setNode(p2, n);
3049 v3s16 p2 = p + v3s16(x,y,z-1);
3050 if(content_features(sector->getNode(p2).d).walkable)
3051 sector->setNode(p2, n2);
3054 v3s16 p2 = p + v3s16(x,y,z+0);
3055 if(content_features(sector->getNode(p2).d).walkable)
3056 sector->setNode(p2, n2);
3059 v3s16 p2 = p + v3s16(x,y,z+1);
3060 if(content_features(sector->getNode(p2).d).walkable)
3061 sector->setNode(p2, n);
3064 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
3065 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
3069 objects_to_remove.push_back(p);
3071 // Lighting has to be recalculated for this one.
3072 sector->getBlocksInArea(p_min, p_max,
3073 lighting_invalidated_blocks);
3078 dstream<<"ServerMap::emergeBlock(): "
3079 "Invalid heightmap object"
3084 catch(InvalidPositionException &e)
3086 dstream<<"WARNING: "<<__FUNCTION_NAME
3087 <<": while inserting object "<<(int)d
3088 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
3089 <<" InvalidPositionException.what()="
3090 <<e.what()<<std::endl;
3091 // This is not too fatal and seems to happen sometimes.
3096 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
3097 i != objects_to_remove.end(); i++)
3099 objects->remove(*i);
3103 Initially update sunlight
3107 core::map<v3s16, bool> light_sources;
3108 bool black_air_left = false;
3109 bool bottom_invalid =
3110 block->propagateSunlight(light_sources, true,
3111 &black_air_left, true);
3113 // If sunlight didn't reach everywhere and part of block is
3114 // above ground, lighting has to be properly updated
3115 if(black_air_left && some_part_underground)
3117 lighting_invalidated_blocks[block->getPos()] = block;
3122 lighting_invalidated_blocks[block->getPos()] = block;
3127 Translate sector's changed blocks to global changed blocks
3130 for(core::map<s16, MapBlock*>::Iterator
3131 i = changed_blocks_sector.getIterator();
3132 i.atEnd() == false; i++)
3134 MapBlock *block = i.getNode()->getValue();
3136 changed_blocks.insert(block->getPos(), block);
3145 <<"lighting_invalidated_blocks.size()"
3149 <<" "<<lighting_invalidated_blocks.size()
3150 <<", "<<has_dungeons
3151 <<", "<<completely_underground
3152 <<", "<<some_part_underground
3157 Debug mode operation
3159 bool haxmode = g_settings.getBool("haxmode");
3162 // Don't calculate lighting at all
3163 //lighting_invalidated_blocks.clear();
3169 void ServerMap::createDir(std::string path)
3171 if(fs::CreateDir(path) == false)
3173 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3174 <<"\""<<path<<"\""<<std::endl;
3175 throw BaseException("ServerMap failed to create directory");
3179 std::string ServerMap::getSectorSubDir(v2s16 pos)
3182 snprintf(cc, 9, "%.4x%.4x",
3183 (unsigned int)pos.X&0xffff,
3184 (unsigned int)pos.Y&0xffff);
3186 return std::string(cc);
3189 std::string ServerMap::getSectorDir(v2s16 pos)
3191 return m_savedir + "/sectors/" + getSectorSubDir(pos);
3194 v2s16 ServerMap::getSectorPos(std::string dirname)
3196 if(dirname.size() != 8)
3197 throw InvalidFilenameException("Invalid sector directory name");
3199 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
3201 throw InvalidFilenameException("Invalid sector directory name");
3202 v2s16 pos((s16)x, (s16)y);
3206 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3208 v2s16 p2d = getSectorPos(sectordir);
3210 if(blockfile.size() != 4){
3211 throw InvalidFilenameException("Invalid block filename");
3214 int r = sscanf(blockfile.c_str(), "%4x", &y);
3216 throw InvalidFilenameException("Invalid block filename");
3217 return v3s16(p2d.X, y, p2d.Y);
3221 #define ENABLE_SECTOR_SAVING 1
3222 #define ENABLE_SECTOR_LOADING 1
3223 #define ENABLE_BLOCK_SAVING 1
3224 #define ENABLE_BLOCK_LOADING 1
3226 void ServerMap::save(bool only_changed)
3228 DSTACK(__FUNCTION_NAME);
3229 if(m_map_saving_enabled == false)
3231 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
3235 if(only_changed == false)
3236 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
3239 saveMasterHeightmap();
3241 u32 sector_meta_count = 0;
3242 u32 block_count = 0;
3245 JMutexAutoLock lock(m_sector_mutex);
3247 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3248 for(; i.atEnd() == false; i++)
3250 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3251 assert(sector->getId() == MAPSECTOR_SERVER);
3253 if(ENABLE_SECTOR_SAVING)
3255 if(sector->differs_from_disk || only_changed == false)
3257 saveSectorMeta(sector);
3258 sector_meta_count++;
3261 if(ENABLE_BLOCK_SAVING)
3263 core::list<MapBlock*> blocks;
3264 sector->getBlocks(blocks);
3265 core::list<MapBlock*>::Iterator j;
3266 for(j=blocks.begin(); j!=blocks.end(); j++)
3268 MapBlock *block = *j;
3269 if(block->getChangedFlag() || only_changed == false)
3281 Only print if something happened or saved whole map
3283 if(only_changed == false || sector_meta_count != 0
3284 || block_count != 0)
3286 dstream<<DTIME<<"ServerMap: Written: "
3287 <<sector_meta_count<<" sector metadata files, "
3288 <<block_count<<" block files"
3293 void ServerMap::loadAll()
3295 DSTACK(__FUNCTION_NAME);
3296 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
3298 loadMasterHeightmap();
3300 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
3302 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
3304 JMutexAutoLock lock(m_sector_mutex);
3307 s32 printed_counter = -100000;
3308 s32 count = list.size();
3310 std::vector<fs::DirListNode>::iterator i;
3311 for(i=list.begin(); i!=list.end(); i++)
3313 if(counter > printed_counter + 10)
3315 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
3316 printed_counter = counter;
3320 MapSector *sector = NULL;
3322 // We want directories
3326 sector = loadSectorMeta(i->name);
3328 catch(InvalidFilenameException &e)
3330 // This catches unknown crap in directory
3333 if(ENABLE_BLOCK_LOADING)
3335 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3336 (m_savedir+"/sectors/"+i->name);
3337 std::vector<fs::DirListNode>::iterator i2;
3338 for(i2=list2.begin(); i2!=list2.end(); i2++)
3344 loadBlock(i->name, i2->name, sector);
3346 catch(InvalidFilenameException &e)
3348 // This catches unknown crap in directory
3353 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
3356 void ServerMap::saveMasterHeightmap()
3358 DSTACK(__FUNCTION_NAME);
3359 createDir(m_savedir);
3361 std::string fullpath = m_savedir + "/master_heightmap";
3362 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3363 if(o.good() == false)
3364 throw FileNotGoodException("Cannot open master heightmap");
3366 // Format used for writing
3367 u8 version = SER_FMT_VER_HIGHEST;
3370 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
3372 [0] u8 serialization version
3373 [1] X master heightmap
3375 u32 fullsize = 1 + hmdata.getSize();
3376 SharedBuffer<u8> data(fullsize);
3379 memcpy(&data[1], *hmdata, hmdata.getSize());
3381 o.write((const char*)*data, fullsize);
3384 m_heightmap->serialize(o, version);
3387 void ServerMap::loadMasterHeightmap()
3389 DSTACK(__FUNCTION_NAME);
3390 std::string fullpath = m_savedir + "/master_heightmap";
3391 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3392 if(is.good() == false)
3393 throw FileNotGoodException("Cannot open master heightmap");
3395 if(m_heightmap != NULL)
3398 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
3401 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3403 DSTACK(__FUNCTION_NAME);
3404 // Format used for writing
3405 u8 version = SER_FMT_VER_HIGHEST;
3407 v2s16 pos = sector->getPos();
3408 createDir(m_savedir);
3409 createDir(m_savedir+"/sectors");
3410 std::string dir = getSectorDir(pos);
3413 std::string fullpath = dir + "/heightmap";
3414 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3415 if(o.good() == false)
3416 throw FileNotGoodException("Cannot open master heightmap");
3418 sector->serialize(o, version);
3420 sector->differs_from_disk = false;
3423 MapSector* ServerMap::loadSectorMeta(std::string dirname)
3425 DSTACK(__FUNCTION_NAME);
3427 v2s16 p2d = getSectorPos(dirname);
3428 std::string dir = m_savedir + "/sectors/" + dirname;
3430 std::string fullpath = dir + "/heightmap";
3431 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3432 if(is.good() == false)
3433 throw FileNotGoodException("Cannot open sector heightmap");
3435 ServerMapSector *sector = ServerMapSector::deSerialize
3436 (is, this, p2d, &m_hwrapper, m_sectors);
3438 sector->differs_from_disk = false;
3443 bool ServerMap::loadSectorFull(v2s16 p2d)
3445 DSTACK(__FUNCTION_NAME);
3446 std::string sectorsubdir = getSectorSubDir(p2d);
3448 MapSector *sector = NULL;
3450 JMutexAutoLock lock(m_sector_mutex);
3453 sector = loadSectorMeta(sectorsubdir);
3455 catch(InvalidFilenameException &e)
3459 catch(FileNotGoodException &e)
3463 catch(std::exception &e)
3468 if(ENABLE_BLOCK_LOADING)
3470 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3471 (m_savedir+"/sectors/"+sectorsubdir);
3472 std::vector<fs::DirListNode>::iterator i2;
3473 for(i2=list2.begin(); i2!=list2.end(); i2++)
3479 loadBlock(sectorsubdir, i2->name, sector);
3481 catch(InvalidFilenameException &e)
3483 // This catches unknown crap in directory
3491 bool ServerMap::deFlushSector(v2s16 p2d)
3493 DSTACK(__FUNCTION_NAME);
3494 // See if it already exists in memory
3496 MapSector *sector = getSectorNoGenerate(p2d);
3499 catch(InvalidPositionException &e)
3502 Try to load the sector from disk.
3504 if(loadSectorFull(p2d) == true)
3513 void ServerMap::saveBlock(MapBlock *block)
3515 DSTACK(__FUNCTION_NAME);
3517 Dummy blocks are not written
3519 if(block->isDummy())
3521 /*v3s16 p = block->getPos();
3522 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3523 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3527 // Format used for writing
3528 u8 version = SER_FMT_VER_HIGHEST;
3530 v3s16 p3d = block->getPos();
3531 v2s16 p2d(p3d.X, p3d.Z);
3532 createDir(m_savedir);
3533 createDir(m_savedir+"/sectors");
3534 std::string dir = getSectorDir(p2d);
3537 // Block file is map/sectors/xxxxxxxx/xxxx
3539 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
3540 std::string fullpath = dir + "/" + cc;
3541 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3542 if(o.good() == false)
3543 throw FileNotGoodException("Cannot open block data");
3546 [0] u8 serialization version
3549 o.write((char*)&version, 1);
3551 block->serialize(o, version);
3554 Versions up from 9 have block objects.
3558 block->serializeObjects(o, version);
3561 // We just wrote it to the disk
3562 block->resetChangedFlag();
3565 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
3567 DSTACK(__FUNCTION_NAME);
3571 // Block file is map/sectors/xxxxxxxx/xxxx
3572 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
3573 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3574 if(is.good() == false)
3575 throw FileNotGoodException("Cannot open block file");
3577 v3s16 p3d = getBlockPos(sectordir, blockfile);
3578 v2s16 p2d(p3d.X, p3d.Z);
3580 assert(sector->getPos() == p2d);
3582 u8 version = SER_FMT_VER_INVALID;
3583 is.read((char*)&version, 1);
3585 /*u32 block_size = MapBlock::serializedLength(version);
3586 SharedBuffer<u8> data(block_size);
3587 is.read((char*)*data, block_size);*/
3589 // This will always return a sector because we're the server
3590 //MapSector *sector = emergeSector(p2d);
3592 MapBlock *block = NULL;
3593 bool created_new = false;
3595 block = sector->getBlockNoCreate(p3d.Y);
3597 catch(InvalidPositionException &e)
3599 block = sector->createBlankBlockNoInsert(p3d.Y);
3603 // deserialize block data
3604 block->deSerialize(is, version);
3607 Versions up from 9 have block objects.
3611 block->updateObjects(is, version, NULL, 0);
3615 sector->insertBlock(block);
3618 Convert old formats to new and save
3621 // Save old format blocks in new format
3622 if(version < SER_FMT_VER_HIGHEST)
3627 // We just loaded it from the disk, so it's up-to-date.
3628 block->resetChangedFlag();
3631 catch(SerializationError &e)
3633 dstream<<"WARNING: Invalid block data on disk "
3634 "(SerializationError). Ignoring."
3639 // Gets from master heightmap
3640 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
3642 assert(m_heightmap != NULL);
3650 corners[0] = m_heightmap->getGroundHeight
3651 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
3652 corners[1] = m_heightmap->getGroundHeight
3653 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
3654 corners[2] = m_heightmap->getGroundHeight
3655 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
3656 corners[3] = m_heightmap->getGroundHeight
3657 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
3660 void ServerMap::PrintInfo(std::ostream &out)
3671 ClientMap::ClientMap(
3673 MapDrawControl &control,
3674 scene::ISceneNode* parent,
3675 scene::ISceneManager* mgr,
3679 scene::ISceneNode(parent, mgr, id),
3686 /*m_box = core::aabbox3d<f32>(0,0,0,
3687 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
3688 /*m_box = core::aabbox3d<f32>(0,0,0,
3689 map->getSizeNodes().X * BS,
3690 map->getSizeNodes().Y * BS,
3691 map->getSizeNodes().Z * BS);*/
3692 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3693 BS*1000000,BS*1000000,BS*1000000);
3695 //setPosition(v3f(BS,BS,BS));
3698 ClientMap::~ClientMap()
3700 JMutexAutoLock lock(mesh_mutex);
3709 MapSector * ClientMap::emergeSector(v2s16 p2d)
3711 DSTACK(__FUNCTION_NAME);
3712 // Check that it doesn't exist already
3714 return getSectorNoGenerate(p2d);
3716 catch(InvalidPositionException &e)
3720 // Create a sector with no heightmaps
3721 ClientMapSector *sector = new ClientMapSector(this, p2d);
3724 JMutexAutoLock lock(m_sector_mutex);
3725 m_sectors.insert(p2d, sector);
3731 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3733 DSTACK(__FUNCTION_NAME);
3734 ClientMapSector *sector = NULL;
3736 JMutexAutoLock lock(m_sector_mutex);
3738 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3742 sector = (ClientMapSector*)n->getValue();
3743 assert(sector->getId() == MAPSECTOR_CLIENT);
3747 sector = new ClientMapSector(this, p2d);
3749 JMutexAutoLock lock(m_sector_mutex);
3750 m_sectors.insert(p2d, sector);
3754 sector->deSerialize(is);
3757 void ClientMap::OnRegisterSceneNode()
3761 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3762 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3765 ISceneNode::OnRegisterSceneNode();
3768 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3770 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3771 DSTACK(__FUNCTION_NAME);
3773 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3776 Get time for measuring timeout.
3778 Measuring time is very useful for long delays when the
3779 machine is swapping a lot.
3781 int time1 = time(0);
3783 u32 daynight_ratio = m_client->getDayNightRatio();
3785 m_camera_mutex.Lock();
3786 v3f camera_position = m_camera_position;
3787 v3f camera_direction = m_camera_direction;
3788 m_camera_mutex.Unlock();
3791 Get all blocks and draw all visible ones
3794 v3s16 cam_pos_nodes(
3795 camera_position.X / BS,
3796 camera_position.Y / BS,
3797 camera_position.Z / BS);
3799 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3801 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3802 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3804 // Take a fair amount as we will be dropping more out later
3806 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3807 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3808 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3810 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3811 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3812 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3814 u32 vertex_count = 0;
3816 // For limiting number of mesh updates per frame
3817 u32 mesh_update_count = 0;
3819 u32 blocks_would_have_drawn = 0;
3820 u32 blocks_drawn = 0;
3822 //NOTE: The sectors map should be locked but we're not doing it
3823 // because it'd cause too much delays
3825 int timecheck_counter = 0;
3826 core::map<v2s16, MapSector*>::Iterator si;
3827 si = m_sectors.getIterator();
3828 for(; si.atEnd() == false; si++)
3831 timecheck_counter++;
3832 if(timecheck_counter > 50)
3834 int time2 = time(0);
3835 if(time2 > time1 + 4)
3837 dstream<<"ClientMap::renderMap(): "
3838 "Rendering takes ages, returning."
3845 MapSector *sector = si.getNode()->getValue();
3846 v2s16 sp = sector->getPos();
3848 if(m_control.range_all == false)
3850 if(sp.X < p_blocks_min.X
3851 || sp.X > p_blocks_max.X
3852 || sp.Y < p_blocks_min.Z
3853 || sp.Y > p_blocks_max.Z)
3857 core::list< MapBlock * > sectorblocks;
3858 sector->getBlocks(sectorblocks);
3864 core::list< MapBlock * >::Iterator i;
3865 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3867 MapBlock *block = *i;
3870 Compare block position to camera position, skip
3871 if not seen on display
3874 float range = 100000 * BS;
3875 if(m_control.range_all == false)
3876 range = m_control.wanted_range * BS;
3878 if(isBlockInSight(block->getPos(), camera_position,
3879 camera_direction, range) == false)
3885 v3s16 blockpos_nodes = block->getPosRelative();
3887 // Block center position
3889 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3890 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3891 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3894 // Block position relative to camera
3895 v3f blockpos_relative = blockpos - camera_position;
3897 // Distance in camera direction (+=front, -=back)
3898 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3901 f32 d = blockpos_relative.getLength();
3903 if(m_control.range_all == false)
3905 // If block is far away, don't draw it
3906 if(d > m_control.wanted_range * BS)
3910 // Maximum radius of a block
3911 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3913 // If block is (nearly) touching the camera, don't
3914 // bother validating further (that is, render it anyway)
3915 if(d > block_max_radius * 1.5)
3917 // Cosine of the angle between the camera direction
3918 // and the block direction (camera_direction is an unit vector)
3919 f32 cosangle = dforward / d;
3921 // Compensate for the size of the block
3922 // (as the block has to be shown even if it's a bit off FOV)
3923 // This is an estimate.
3924 cosangle += block_max_radius / dforward;
3926 // If block is not in the field of view, skip it
3927 //if(cosangle < cos(FOV_ANGLE/2))
3928 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3933 v3s16 blockpos_nodes = block->getPosRelative();
3935 // Block center position
3937 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3938 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3939 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3942 // Block position relative to camera
3943 v3f blockpos_relative = blockpos - camera_position;
3946 f32 d = blockpos_relative.getLength();
3953 bool mesh_expired = false;
3956 JMutexAutoLock lock(block->mesh_mutex);
3958 mesh_expired = block->getMeshExpired();
3960 // Mesh has not been expired and there is no mesh:
3961 // block has no content
3962 if(block->mesh == NULL && mesh_expired == false)
3966 f32 faraway = BS*50;
3967 //f32 faraway = m_control.wanted_range * BS;
3970 This has to be done with the mesh_mutex unlocked
3972 // Pretty random but this should work somewhat nicely
3973 if(mesh_expired && (
3974 (mesh_update_count < 3
3975 && (d < faraway || mesh_update_count < 2)
3978 (m_control.range_all && mesh_update_count < 20)
3981 /*if(mesh_expired && mesh_update_count < 6
3982 && (d < faraway || mesh_update_count < 3))*/
3984 mesh_update_count++;
3986 // Mesh has been expired: generate new mesh
3987 //block->updateMeshes(daynight_i);
3988 block->updateMesh(daynight_ratio);
3990 mesh_expired = false;
3994 Don't draw an expired mesh that is far away
3996 /*if(mesh_expired && d >= faraway)
3999 // Instead, delete it
4000 JMutexAutoLock lock(block->mesh_mutex);
4003 block->mesh->drop();
4006 // And continue to next block
4011 Draw the faces of the block
4014 JMutexAutoLock lock(block->mesh_mutex);
4016 scene::SMesh *mesh = block->mesh;
4021 blocks_would_have_drawn++;
4022 if(blocks_drawn >= m_control.wanted_max_blocks
4023 && m_control.range_all == false
4024 && d > m_control.wanted_min_range * BS)
4028 u32 c = mesh->getMeshBufferCount();
4030 for(u32 i=0; i<c; i++)
4032 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
4033 const video::SMaterial& material = buf->getMaterial();
4034 video::IMaterialRenderer* rnd =
4035 driver->getMaterialRenderer(material.MaterialType);
4036 bool transparent = (rnd && rnd->isTransparent());
4037 // Render transparent on transparent pass and likewise.
4038 if(transparent == is_transparent_pass)
4040 driver->setMaterial(buf->getMaterial());
4041 driver->drawMeshBuffer(buf);
4042 vertex_count += buf->getVertexCount();
4046 } // foreach sectorblocks
4049 m_control.blocks_drawn = blocks_drawn;
4050 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4052 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4053 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4056 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4057 core::map<v3s16, MapBlock*> *affected_blocks)
4059 bool changed = false;
4061 Add it to all blocks touching it
4064 v3s16(0,0,0), // this
4065 v3s16(0,0,1), // back
4066 v3s16(0,1,0), // top
4067 v3s16(1,0,0), // right
4068 v3s16(0,0,-1), // front
4069 v3s16(0,-1,0), // bottom
4070 v3s16(-1,0,0), // left
4072 for(u16 i=0; i<7; i++)
4074 v3s16 p2 = p + dirs[i];
4075 // Block position of neighbor (or requested) node
4076 v3s16 blockpos = getNodeBlockPos(p2);
4077 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4078 if(blockref == NULL)
4080 // Relative position of requested node
4081 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4082 if(blockref->setTempMod(relpos, mod))
4087 if(changed && affected_blocks!=NULL)
4089 for(u16 i=0; i<7; i++)
4091 v3s16 p2 = p + dirs[i];
4092 // Block position of neighbor (or requested) node
4093 v3s16 blockpos = getNodeBlockPos(p2);
4094 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4095 if(blockref == NULL)
4097 affected_blocks->insert(blockpos, blockref);
4103 bool ClientMap::clearTempMod(v3s16 p,
4104 core::map<v3s16, MapBlock*> *affected_blocks)
4106 bool changed = false;
4108 v3s16(0,0,0), // this
4109 v3s16(0,0,1), // back
4110 v3s16(0,1,0), // top
4111 v3s16(1,0,0), // right
4112 v3s16(0,0,-1), // front
4113 v3s16(0,-1,0), // bottom
4114 v3s16(-1,0,0), // left
4116 for(u16 i=0; i<7; i++)
4118 v3s16 p2 = p + dirs[i];
4119 // Block position of neighbor (or requested) node
4120 v3s16 blockpos = getNodeBlockPos(p2);
4121 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4122 if(blockref == NULL)
4124 // Relative position of requested node
4125 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4126 if(blockref->clearTempMod(relpos))
4131 if(changed && affected_blocks!=NULL)
4133 for(u16 i=0; i<7; i++)
4135 v3s16 p2 = p + dirs[i];
4136 // Block position of neighbor (or requested) node
4137 v3s16 blockpos = getNodeBlockPos(p2);
4138 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4139 if(blockref == NULL)
4141 affected_blocks->insert(blockpos, blockref);
4147 void ClientMap::PrintInfo(std::ostream &out)
4158 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4163 MapVoxelManipulator::~MapVoxelManipulator()
4165 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4170 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4172 TimeTaker timer1("emerge", &emerge_time);
4174 // Units of these are MapBlocks
4175 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4176 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4178 VoxelArea block_area_nodes
4179 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4181 addArea(block_area_nodes);
4183 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4184 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4185 for(s32 x=p_min.X; x<=p_max.X; x++)
4188 core::map<v3s16, bool>::Node *n;
4189 n = m_loaded_blocks.find(p);
4193 bool block_data_inexistent = false;
4196 TimeTaker timer1("emerge load", &emerge_load_time);
4198 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4199 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4202 dstream<<std::endl;*/
4204 MapBlock *block = m_map->getBlockNoCreate(p);
4205 if(block->isDummy())
4206 block_data_inexistent = true;
4208 block->copyTo(*this);
4210 catch(InvalidPositionException &e)
4212 block_data_inexistent = true;
4215 if(block_data_inexistent)
4217 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4218 // Fill with VOXELFLAG_INEXISTENT
4219 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4220 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4222 s32 i = m_area.index(a.MinEdge.X,y,z);
4223 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4227 m_loaded_blocks.insert(p, true);
4230 //dstream<<"emerge done"<<std::endl;
4238 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4240 TimeTaker timer1("emerge", &emerge_time);
4242 v3s16 size = a.getExtent();
4244 VoxelArea padded = a;
4245 padded.pad(m_area.getExtent() / 4);
4248 for(s16 z=0; z<size.Z; z++)
4249 for(s16 y=0; y<size.Y; y++)
4250 for(s16 x=0; x<size.X; x++)
4253 s32 i = m_area.index(a.MinEdge + p);
4254 // Don't touch nodes that have already been loaded
4255 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4259 TimeTaker timer1("emerge load", &emerge_load_time);
4260 MapNode n = m_map->getNode(a.MinEdge + p);
4264 catch(InvalidPositionException &e)
4266 m_flags[i] = VOXELFLAG_INEXISTENT;
4274 SUGG: Add an option to only update eg. water and air nodes.
4275 This will make it interfere less with important stuff if
4278 void MapVoxelManipulator::blitBack
4279 (core::map<v3s16, MapBlock*> & modified_blocks)
4281 if(m_area.getExtent() == v3s16(0,0,0))
4284 //TimeTaker timer1("blitBack");
4286 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4287 <<m_loaded_blocks.size()<<std::endl;*/
4290 Initialize block cache
4292 v3s16 blockpos_last;
4293 MapBlock *block = NULL;
4294 bool block_checked_in_modified = false;
4296 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4297 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4298 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4302 u8 f = m_flags[m_area.index(p)];
4303 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4306 MapNode &n = m_data[m_area.index(p)];
4308 v3s16 blockpos = getNodeBlockPos(p);
4313 if(block == NULL || blockpos != blockpos_last){
4314 block = m_map->getBlockNoCreate(blockpos);
4315 blockpos_last = blockpos;
4316 block_checked_in_modified = false;
4319 // Calculate relative position in block
4320 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4322 // Don't continue if nothing has changed here
4323 if(block->getNode(relpos) == n)
4326 //m_map->setNode(m_area.MinEdge + p, n);
4327 block->setNode(relpos, n);
4330 Make sure block is in modified_blocks
4332 if(block_checked_in_modified == false)
4334 modified_blocks[blockpos] = block;
4335 block_checked_in_modified = true;
4338 catch(InvalidPositionException &e)
4344 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4345 MapVoxelManipulator(map)
4349 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4353 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4355 // Just create the area to avoid segfaults
4356 VoxelManipulator::emerge(a, caller_id);
4359 Just create the area to avoid segfaults
4362 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4363 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4364 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
4366 s32 i = m_area.index(x,y,z);
4367 // Don't touch nodes that have already been loaded
4368 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4370 m_flags[i] = VOXELFLAG_INEXISTENT;
4374 void ManualMapVoxelManipulator::initialEmerge(
4375 v3s16 blockpos_min, v3s16 blockpos_max)
4377 TimeTaker timer1("emerge", &emerge_time);
4379 // Units of these are MapBlocks
4380 v3s16 p_min = blockpos_min;
4381 v3s16 p_max = blockpos_max;
4383 VoxelArea block_area_nodes
4384 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4386 addArea(block_area_nodes);
4388 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4389 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4390 for(s32 x=p_min.X; x<=p_max.X; x++)
4393 core::map<v3s16, bool>::Node *n;
4394 n = m_loaded_blocks.find(p);
4398 bool block_data_inexistent = false;
4401 TimeTaker timer1("emerge load", &emerge_load_time);
4403 MapBlock *block = m_map->getBlockNoCreate(p);
4404 if(block->isDummy())
4405 block_data_inexistent = true;
4407 block->copyTo(*this);
4409 catch(InvalidPositionException &e)
4411 block_data_inexistent = true;
4414 if(block_data_inexistent)
4416 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4417 // Fill with VOXELFLAG_INEXISTENT
4418 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4419 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4421 s32 i = m_area.index(a.MinEdge.X,y,z);
4422 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4426 m_loaded_blocks.insert(p, true);