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),
40 m_sector_mutex.Init();
41 m_camera_mutex.Init();
42 assert(m_sector_mutex.IsInitialized());
43 assert(m_camera_mutex.IsInitialized());
45 // Get this so that the player can stay on it at first
46 //getSector(v2s16(0,0));
54 /*updater.setRun(false);
55 while(updater.IsRunning())
61 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
62 for(; i.atEnd() == false; i++)
64 MapSector *sector = i.getNode()->getValue();
69 MapSector * Map::getSectorNoGenerate(v2s16 p)
71 JMutexAutoLock lock(m_sector_mutex);
73 if(m_sector_cache != NULL && p == m_sector_cache_p){
74 MapSector * sector = m_sector_cache;
75 // Reset inactivity timer
76 sector->usage_timer = 0.0;
80 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
81 // If sector doesn't exist, throw an exception
84 throw InvalidPositionException();
87 MapSector *sector = n->getValue();
89 // Cache the last result
91 m_sector_cache = sector;
93 //MapSector * ref(sector);
95 // Reset inactivity timer
96 sector->usage_timer = 0.0;
100 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
102 v2s16 p2d(p3d.X, p3d.Z);
103 MapSector * sector = getSectorNoGenerate(p2d);
105 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
110 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
114 v2s16 p2d(p3d.X, p3d.Z);
115 MapSector * sector = getSectorNoGenerate(p2d);
116 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
119 catch(InvalidPositionException &e)
125 f32 Map::getGroundHeight(v2s16 p, bool generate)
128 v2s16 sectorpos = getNodeSectorPos(p);
129 MapSector * sref = getSectorNoGenerate(sectorpos);
130 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
131 f32 y = sref->getGroundHeight(relpos);
134 catch(InvalidPositionException &e)
136 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
140 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
142 /*m_dout<<DTIME<<"Map::setGroundHeight(("
144 <<"), "<<y<<")"<<std::endl;*/
145 v2s16 sectorpos = getNodeSectorPos(p);
146 MapSector * sref = getSectorNoGenerate(sectorpos);
147 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
148 //sref->mutex.Lock();
149 sref->setGroundHeight(relpos, y);
150 //sref->mutex.Unlock();
153 bool Map::isNodeUnderground(v3s16 p)
155 v3s16 blockpos = getNodeBlockPos(p);
157 MapBlock * block = getBlockNoCreate(blockpos);
158 return block->getIsUnderground();
160 catch(InvalidPositionException &e)
167 Goes recursively through the neighbours of the node.
169 Alters only transparent nodes.
171 If the lighting of the neighbour is lower than the lighting of
172 the node was (before changing it to 0 at the step before), the
173 lighting of the neighbour is set to 0 and then the same stuff
174 repeats for the neighbour.
176 The ending nodes of the routine are stored in light_sources.
177 This is useful when a light is removed. In such case, this
178 routine can be called for the light node and then again for
179 light_sources to re-light the area without the removed light.
181 values of from_nodes are lighting values.
183 void Map::unspreadLight(enum LightBank bank,
184 core::map<v3s16, u8> & from_nodes,
185 core::map<v3s16, bool> & light_sources,
186 core::map<v3s16, MapBlock*> & modified_blocks)
189 v3s16(0,0,1), // back
191 v3s16(1,0,0), // right
192 v3s16(0,0,-1), // front
193 v3s16(0,-1,0), // bottom
194 v3s16(-1,0,0), // left
197 if(from_nodes.size() == 0)
200 u32 blockchangecount = 0;
202 core::map<v3s16, u8> unlighted_nodes;
203 core::map<v3s16, u8>::Iterator j;
204 j = from_nodes.getIterator();
207 Initialize block cache
210 MapBlock *block = NULL;
211 // Cache this a bit, too
212 bool block_checked_in_modified = false;
214 for(; j.atEnd() == false; j++)
216 v3s16 pos = j.getNode()->getKey();
217 v3s16 blockpos = getNodeBlockPos(pos);
219 // Only fetch a new block if the block position has changed
221 if(block == NULL || blockpos != blockpos_last){
222 block = getBlockNoCreate(blockpos);
223 blockpos_last = blockpos;
225 block_checked_in_modified = false;
229 catch(InvalidPositionException &e)
237 // Calculate relative position in block
238 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
240 // Get node straight from the block
241 MapNode n = block->getNode(relpos);
243 u8 oldlight = j.getNode()->getValue();
245 // Loop through 6 neighbors
246 for(u16 i=0; i<6; i++)
248 // Get the position of the neighbor node
249 v3s16 n2pos = pos + dirs[i];
251 // Get the block where the node is located
252 v3s16 blockpos = getNodeBlockPos(n2pos);
256 // Only fetch a new block if the block position has changed
258 if(block == NULL || blockpos != blockpos_last){
259 block = getBlockNoCreate(blockpos);
260 blockpos_last = blockpos;
262 block_checked_in_modified = false;
266 catch(InvalidPositionException &e)
271 // Calculate relative position in block
272 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
273 // Get node straight from the block
274 MapNode n2 = block->getNode(relpos);
276 bool changed = false;
278 //TODO: Optimize output by optimizing light_sources?
281 If the neighbor is dimmer than what was specified
282 as oldlight (the light of the previous node)
284 if(n2.getLight(bank) < oldlight)
287 And the neighbor is transparent and it has some light
289 if(n2.light_propagates() && n2.getLight(bank) != 0)
292 Set light to 0 and add to queue
295 u8 current_light = n2.getLight(bank);
296 n2.setLight(bank, 0);
297 block->setNode(relpos, n2);
299 unlighted_nodes.insert(n2pos, current_light);
303 Remove from light_sources if it is there
304 NOTE: This doesn't happen nearly at all
306 /*if(light_sources.find(n2pos))
308 std::cout<<"Removed from light_sources"<<std::endl;
309 light_sources.remove(n2pos);
314 if(light_sources.find(n2pos) != NULL)
315 light_sources.remove(n2pos);*/
318 light_sources.insert(n2pos, true);
321 // Add to modified_blocks
322 if(changed == true && block_checked_in_modified == false)
324 // If the block is not found in modified_blocks, add.
325 if(modified_blocks.find(blockpos) == NULL)
327 modified_blocks.insert(blockpos, block);
329 block_checked_in_modified = true;
332 catch(InvalidPositionException &e)
339 /*dstream<<"unspreadLight(): Changed block "
340 <<blockchangecount<<" times"
341 <<" for "<<from_nodes.size()<<" nodes"
344 if(unlighted_nodes.size() > 0)
345 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
349 A single-node wrapper of the above
351 void Map::unLightNeighbors(enum LightBank bank,
352 v3s16 pos, u8 lightwas,
353 core::map<v3s16, bool> & light_sources,
354 core::map<v3s16, MapBlock*> & modified_blocks)
356 core::map<v3s16, u8> from_nodes;
357 from_nodes.insert(pos, lightwas);
359 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
363 Lights neighbors of from_nodes, collects all them and then
366 void Map::spreadLight(enum LightBank bank,
367 core::map<v3s16, bool> & from_nodes,
368 core::map<v3s16, MapBlock*> & modified_blocks)
370 const v3s16 dirs[6] = {
371 v3s16(0,0,1), // back
373 v3s16(1,0,0), // right
374 v3s16(0,0,-1), // front
375 v3s16(0,-1,0), // bottom
376 v3s16(-1,0,0), // left
379 if(from_nodes.size() == 0)
382 u32 blockchangecount = 0;
384 core::map<v3s16, bool> lighted_nodes;
385 core::map<v3s16, bool>::Iterator j;
386 j = from_nodes.getIterator();
389 Initialize block cache
392 MapBlock *block = NULL;
393 // Cache this a bit, too
394 bool block_checked_in_modified = false;
396 for(; j.atEnd() == false; j++)
397 //for(; j != from_nodes.end(); j++)
399 v3s16 pos = j.getNode()->getKey();
401 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
402 v3s16 blockpos = getNodeBlockPos(pos);
404 // Only fetch a new block if the block position has changed
406 if(block == NULL || blockpos != blockpos_last){
407 block = getBlockNoCreate(blockpos);
408 blockpos_last = blockpos;
410 block_checked_in_modified = false;
414 catch(InvalidPositionException &e)
422 // Calculate relative position in block
423 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
425 // Get node straight from the block
426 MapNode n = block->getNode(relpos);
428 u8 oldlight = n.getLight(bank);
429 u8 newlight = diminish_light(oldlight);
431 // Loop through 6 neighbors
432 for(u16 i=0; i<6; i++){
433 // Get the position of the neighbor node
434 v3s16 n2pos = pos + dirs[i];
436 // Get the block where the node is located
437 v3s16 blockpos = getNodeBlockPos(n2pos);
441 // Only fetch a new block if the block position has changed
443 if(block == NULL || blockpos != blockpos_last){
444 block = getBlockNoCreate(blockpos);
445 blockpos_last = blockpos;
447 block_checked_in_modified = false;
451 catch(InvalidPositionException &e)
456 // Calculate relative position in block
457 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
458 // Get node straight from the block
459 MapNode n2 = block->getNode(relpos);
461 bool changed = false;
463 If the neighbor is brighter than the current node,
464 add to list (it will light up this node on its turn)
466 if(n2.getLight(bank) > undiminish_light(oldlight))
468 lighted_nodes.insert(n2pos, true);
469 //lighted_nodes.push_back(n2pos);
473 If the neighbor is dimmer than how much light this node
474 would spread on it, add to list
476 if(n2.getLight(bank) < newlight)
478 if(n2.light_propagates())
480 n2.setLight(bank, newlight);
481 block->setNode(relpos, n2);
482 lighted_nodes.insert(n2pos, true);
483 //lighted_nodes.push_back(n2pos);
488 // Add to modified_blocks
489 if(changed == true && block_checked_in_modified == false)
491 // If the block is not found in modified_blocks, add.
492 if(modified_blocks.find(blockpos) == NULL)
494 modified_blocks.insert(blockpos, block);
496 block_checked_in_modified = true;
499 catch(InvalidPositionException &e)
506 /*dstream<<"spreadLight(): Changed block "
507 <<blockchangecount<<" times"
508 <<" for "<<from_nodes.size()<<" nodes"
511 if(lighted_nodes.size() > 0)
512 spreadLight(bank, lighted_nodes, modified_blocks);
516 A single-node source variation of the above.
518 void Map::lightNeighbors(enum LightBank bank,
520 core::map<v3s16, MapBlock*> & modified_blocks)
522 core::map<v3s16, bool> from_nodes;
523 from_nodes.insert(pos, true);
524 spreadLight(bank, from_nodes, modified_blocks);
527 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
530 v3s16(0,0,1), // back
532 v3s16(1,0,0), // right
533 v3s16(0,0,-1), // front
534 v3s16(0,-1,0), // bottom
535 v3s16(-1,0,0), // left
538 u8 brightest_light = 0;
539 v3s16 brightest_pos(0,0,0);
540 bool found_something = false;
542 // Loop through 6 neighbors
543 for(u16 i=0; i<6; i++){
544 // Get the position of the neighbor node
545 v3s16 n2pos = p + dirs[i];
550 catch(InvalidPositionException &e)
554 if(n2.getLight(bank) > brightest_light || found_something == false){
555 brightest_light = n2.getLight(bank);
556 brightest_pos = n2pos;
557 found_something = true;
561 if(found_something == false)
562 throw InvalidPositionException();
564 return brightest_pos;
568 Propagates sunlight down from a node.
569 Starting point gets sunlight.
571 Returns the lowest y value of where the sunlight went.
573 Mud is turned into grass in where the sunlight stops.
575 s16 Map::propagateSunlight(v3s16 start,
576 core::map<v3s16, MapBlock*> & modified_blocks)
581 v3s16 pos(start.X, y, start.Z);
583 v3s16 blockpos = getNodeBlockPos(pos);
586 block = getBlockNoCreate(blockpos);
588 catch(InvalidPositionException &e)
593 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
594 MapNode n = block->getNode(relpos);
596 if(n.sunlight_propagates())
598 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
599 block->setNode(relpos, n);
601 modified_blocks.insert(blockpos, block);
605 // Turn mud into grass
606 if(n.d == CONTENT_MUD)
609 block->setNode(relpos, n);
610 modified_blocks.insert(blockpos, block);
613 // Sunlight goes no further
620 void Map::updateLighting(enum LightBank bank,
621 core::map<v3s16, MapBlock*> & a_blocks,
622 core::map<v3s16, MapBlock*> & modified_blocks)
624 /*m_dout<<DTIME<<"Map::updateLighting(): "
625 <<a_blocks.getSize()<<" blocks... ";*/
629 u32 count_was = modified_blocks.size();
631 core::map<v3s16, bool> light_sources;
633 core::map<v3s16, u8> unlight_from;
635 core::map<v3s16, MapBlock*>::Iterator i;
636 i = a_blocks.getIterator();
637 for(; i.atEnd() == false; i++)
639 MapBlock *block = i.getNode()->getValue();
643 // Don't bother with dummy blocks.
647 v3s16 pos = block->getPos();
648 modified_blocks.insert(pos, block);
651 Clear all light from block
653 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
654 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
655 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
660 MapNode n = block->getNode(v3s16(x,y,z));
661 u8 oldlight = n.getLight(bank);
663 block->setNode(v3s16(x,y,z), n);
665 // Collect borders for unlighting
666 if(x==0 || x == MAP_BLOCKSIZE-1
667 || y==0 || y == MAP_BLOCKSIZE-1
668 || z==0 || z == MAP_BLOCKSIZE-1)
670 v3s16 p_map = p + v3s16(
673 MAP_BLOCKSIZE*pos.Z);
674 unlight_from.insert(p_map, oldlight);
677 catch(InvalidPositionException &e)
680 This would happen when dealing with a
684 dstream<<"updateLighting(): InvalidPositionException"
689 if(bank == LIGHTBANK_DAY)
691 bool bottom_valid = block->propagateSunlight(light_sources);
693 // If bottom is valid, we're done.
697 else if(bank == LIGHTBANK_NIGHT)
706 /*dstream<<"Bottom for sunlight-propagated block ("
707 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
710 // Else get the block below and loop to it
714 block = getBlockNoCreate(pos);
716 catch(InvalidPositionException &e)
725 //TimeTaker timer("unspreadLight");
726 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
731 u32 diff = modified_blocks.size() - count_was;
732 count_was = modified_blocks.size();
733 dstream<<"unspreadLight modified "<<diff<<std::endl;
736 // TODO: Spread light from propagated sunlight?
737 // Yes, add it to light_sources... somehow.
738 // It has to be added at somewhere above, in the loop.
740 // NOTE: This actually works fine without doing so
741 // - Find out why it works
744 //TimeTaker timer("spreadLight");
745 spreadLight(bank, light_sources, modified_blocks);
750 u32 diff = modified_blocks.size() - count_was;
751 count_was = modified_blocks.size();
752 dstream<<"spreadLight modified "<<diff<<std::endl;
755 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
758 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
759 core::map<v3s16, MapBlock*> & modified_blocks)
761 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
762 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
765 Update information about whether day and night light differ
767 for(core::map<v3s16, MapBlock*>::Iterator
768 i = modified_blocks.getIterator();
769 i.atEnd() == false; i++)
771 MapBlock *block = i.getNode()->getValue();
772 block->updateDayNightDiff();
777 This is called after changing a node from transparent to opaque.
778 The lighting value of the node should be left as-is after changing
779 other values. This sets the lighting value to 0.
781 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
782 core::map<v3s16, MapBlock*> &modified_blocks)*/
783 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
784 core::map<v3s16, MapBlock*> &modified_blocks)
787 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
788 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
791 From this node to nodes underneath:
792 If lighting is sunlight (1.0), unlight neighbours and
797 v3s16 toppos = p + v3s16(0,1,0);
798 v3s16 bottompos = p + v3s16(0,-1,0);
800 bool node_under_sunlight = true;
801 core::map<v3s16, bool> light_sources;
804 If there is a node at top and it doesn't have sunlight,
805 there has not been any sunlight going down.
807 Otherwise there probably is.
810 MapNode topnode = getNode(toppos);
812 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
813 node_under_sunlight = false;
815 catch(InvalidPositionException &e)
819 if(n.d != CONTENT_TORCH)
822 If there is grass below, change it to mud
825 MapNode bottomnode = getNode(bottompos);
827 if(bottomnode.d == CONTENT_GRASS
828 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
830 bottomnode.d = CONTENT_MUD;
831 setNode(bottompos, bottomnode);
834 catch(InvalidPositionException &e)
839 enum LightBank banks[] =
844 for(s32 i=0; i<2; i++)
846 enum LightBank bank = banks[i];
848 u8 lightwas = getNode(p).getLight(bank);
850 // Add the block of the added node to modified_blocks
851 v3s16 blockpos = getNodeBlockPos(p);
852 MapBlock * block = getBlockNoCreate(blockpos);
853 assert(block != NULL);
854 modified_blocks.insert(blockpos, block);
856 if(isValidPosition(p) == false)
859 // Unlight neighbours of node.
860 // This means setting light of all consequent dimmer nodes
862 // This also collects the nodes at the border which will spread
863 // light again into this.
864 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
872 If node is under sunlight, take all sunlighted nodes under
873 it and clear light from them and from where the light has
875 TODO: This could be optimized by mass-unlighting instead
878 if(node_under_sunlight)
882 //m_dout<<DTIME<<"y="<<y<<std::endl;
883 v3s16 n2pos(p.X, y, p.Z);
889 catch(InvalidPositionException &e)
894 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
896 //m_dout<<DTIME<<"doing"<<std::endl;
897 unLightNeighbors(LIGHTBANK_DAY,
898 n2pos, n2.getLight(LIGHTBANK_DAY),
899 light_sources, modified_blocks);
900 n2.setLight(LIGHTBANK_DAY, 0);
908 for(s32 i=0; i<2; i++)
910 enum LightBank bank = banks[i];
913 Spread light from all nodes that might be capable of doing so
914 TODO: Convert to spreadLight
916 spreadLight(bank, light_sources, modified_blocks);
920 Update information about whether day and night light differ
922 for(core::map<v3s16, MapBlock*>::Iterator
923 i = modified_blocks.getIterator();
924 i.atEnd() == false; i++)
926 MapBlock *block = i.getNode()->getValue();
927 block->updateDayNightDiff();
931 Add neighboring liquid nodes and the node itself if it is
932 liquid (=water node was added) to transform queue.
935 v3s16(0,0,0), // self
936 v3s16(0,0,1), // back
938 v3s16(1,0,0), // right
939 v3s16(0,0,-1), // front
940 v3s16(0,-1,0), // bottom
941 v3s16(-1,0,0), // left
943 for(u16 i=0; i<7; i++)
948 v3s16 p2 = p + dirs[i];
950 MapNode n2 = getNode(p2);
951 if(content_liquid(n2.d))
953 m_transforming_liquid.push_back(p2);
956 }catch(InvalidPositionException &e)
964 void Map::removeNodeAndUpdate(v3s16 p,
965 core::map<v3s16, MapBlock*> &modified_blocks)
968 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
969 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
971 bool node_under_sunlight = true;
973 v3s16 toppos = p + v3s16(0,1,0);
975 // Node will be replaced with this
976 u8 replace_material = CONTENT_AIR;
979 If there is a node at top and it doesn't have sunlight,
980 there will be no sunlight going down.
983 MapNode topnode = getNode(toppos);
985 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
986 node_under_sunlight = false;
988 catch(InvalidPositionException &e)
992 core::map<v3s16, bool> light_sources;
994 enum LightBank banks[] =
999 for(s32 i=0; i<2; i++)
1001 enum LightBank bank = banks[i];
1004 Unlight neighbors (in case the node is a light source)
1006 unLightNeighbors(bank, p,
1007 getNode(p).getLight(bank),
1008 light_sources, modified_blocks);
1013 This also clears the lighting.
1017 n.d = replace_material;
1020 for(s32 i=0; i<2; i++)
1022 enum LightBank bank = banks[i];
1025 Recalculate lighting
1027 spreadLight(bank, light_sources, modified_blocks);
1030 // Add the block of the removed node to modified_blocks
1031 v3s16 blockpos = getNodeBlockPos(p);
1032 MapBlock * block = getBlockNoCreate(blockpos);
1033 assert(block != NULL);
1034 modified_blocks.insert(blockpos, block);
1037 If the removed node was under sunlight, propagate the
1038 sunlight down from it and then light all neighbors
1039 of the propagated blocks.
1041 if(node_under_sunlight)
1043 s16 ybottom = propagateSunlight(p, modified_blocks);
1044 /*m_dout<<DTIME<<"Node was under sunlight. "
1045 "Propagating sunlight";
1046 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1048 for(; y >= ybottom; y--)
1050 v3s16 p2(p.X, y, p.Z);
1051 /*m_dout<<DTIME<<"lighting neighbors of node ("
1052 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1054 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1059 // Set the lighting of this node to 0
1060 // TODO: Is this needed? Lighting is cleared up there already.
1062 MapNode n = getNode(p);
1063 n.setLight(LIGHTBANK_DAY, 0);
1066 catch(InvalidPositionException &e)
1072 for(s32 i=0; i<2; i++)
1074 enum LightBank bank = banks[i];
1076 // Get the brightest neighbour node and propagate light from it
1077 v3s16 n2p = getBrightestNeighbour(bank, p);
1079 MapNode n2 = getNode(n2p);
1080 lightNeighbors(bank, n2p, modified_blocks);
1082 catch(InvalidPositionException &e)
1088 Update information about whether day and night light differ
1090 for(core::map<v3s16, MapBlock*>::Iterator
1091 i = modified_blocks.getIterator();
1092 i.atEnd() == false; i++)
1094 MapBlock *block = i.getNode()->getValue();
1095 block->updateDayNightDiff();
1099 Add neighboring liquid nodes to transform queue.
1102 v3s16(0,0,1), // back
1103 v3s16(0,1,0), // top
1104 v3s16(1,0,0), // right
1105 v3s16(0,0,-1), // front
1106 v3s16(0,-1,0), // bottom
1107 v3s16(-1,0,0), // left
1109 for(u16 i=0; i<6; i++)
1114 v3s16 p2 = p + dirs[i];
1116 MapNode n2 = getNode(p2);
1117 if(content_liquid(n2.d))
1119 m_transforming_liquid.push_back(p2);
1122 }catch(InvalidPositionException &e)
1129 void Map::expireMeshes(bool only_daynight_diffed)
1131 TimeTaker timer("expireMeshes()");
1133 core::map<v2s16, MapSector*>::Iterator si;
1134 si = m_sectors.getIterator();
1135 for(; si.atEnd() == false; si++)
1137 MapSector *sector = si.getNode()->getValue();
1139 core::list< MapBlock * > sectorblocks;
1140 sector->getBlocks(sectorblocks);
1142 core::list< MapBlock * >::Iterator i;
1143 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1145 MapBlock *block = *i;
1147 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
1153 JMutexAutoLock lock(block->mesh_mutex);
1154 if(block->mesh != NULL)
1156 /*block->mesh->drop();
1157 block->mesh = NULL;*/
1158 block->setMeshExpired(true);
1165 void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
1167 assert(mapType() == MAPTYPE_CLIENT);
1170 v3s16 p = blockpos + v3s16(0,0,0);
1171 MapBlock *b = getBlockNoCreate(p);
1172 b->updateMesh(daynight_ratio);
1174 catch(InvalidPositionException &e){}
1177 v3s16 p = blockpos + v3s16(-1,0,0);
1178 MapBlock *b = getBlockNoCreate(p);
1179 b->updateMesh(daynight_ratio);
1181 catch(InvalidPositionException &e){}
1183 v3s16 p = blockpos + v3s16(0,-1,0);
1184 MapBlock *b = getBlockNoCreate(p);
1185 b->updateMesh(daynight_ratio);
1187 catch(InvalidPositionException &e){}
1189 v3s16 p = blockpos + v3s16(0,0,-1);
1190 MapBlock *b = getBlockNoCreate(p);
1191 b->updateMesh(daynight_ratio);
1193 catch(InvalidPositionException &e){}
1196 v3s16 p = blockpos + v3s16(1,0,0);
1197 MapBlock *b = getBlockNoCreate(p);
1198 b->updateMesh(daynight_ratio);
1200 catch(InvalidPositionException &e){}
1202 v3s16 p = blockpos + v3s16(0,1,0);
1203 MapBlock *b = getBlockNoCreate(p);
1204 b->updateMesh(daynight_ratio);
1206 catch(InvalidPositionException &e){}
1208 v3s16 p = blockpos + v3s16(0,0,1);
1209 MapBlock *b = getBlockNoCreate(p);
1210 b->updateMesh(daynight_ratio);
1212 catch(InvalidPositionException &e){}*/
1217 bool Map::dayNightDiffed(v3s16 blockpos)
1220 v3s16 p = blockpos + v3s16(0,0,0);
1221 MapBlock *b = getBlockNoCreate(p);
1222 if(b->dayNightDiffed())
1225 catch(InvalidPositionException &e){}
1228 v3s16 p = blockpos + v3s16(-1,0,0);
1229 MapBlock *b = getBlockNoCreate(p);
1230 if(b->dayNightDiffed())
1233 catch(InvalidPositionException &e){}
1235 v3s16 p = blockpos + v3s16(0,-1,0);
1236 MapBlock *b = getBlockNoCreate(p);
1237 if(b->dayNightDiffed())
1240 catch(InvalidPositionException &e){}
1242 v3s16 p = blockpos + v3s16(0,0,-1);
1243 MapBlock *b = getBlockNoCreate(p);
1244 if(b->dayNightDiffed())
1247 catch(InvalidPositionException &e){}
1250 v3s16 p = blockpos + v3s16(1,0,0);
1251 MapBlock *b = getBlockNoCreate(p);
1252 if(b->dayNightDiffed())
1255 catch(InvalidPositionException &e){}
1257 v3s16 p = blockpos + v3s16(0,1,0);
1258 MapBlock *b = getBlockNoCreate(p);
1259 if(b->dayNightDiffed())
1262 catch(InvalidPositionException &e){}
1264 v3s16 p = blockpos + v3s16(0,0,1);
1265 MapBlock *b = getBlockNoCreate(p);
1266 if(b->dayNightDiffed())
1269 catch(InvalidPositionException &e){}
1275 Updates usage timers
1277 void Map::timerUpdate(float dtime)
1279 JMutexAutoLock lock(m_sector_mutex);
1281 core::map<v2s16, MapSector*>::Iterator si;
1283 si = m_sectors.getIterator();
1284 for(; si.atEnd() == false; si++)
1286 MapSector *sector = si.getNode()->getValue();
1287 sector->usage_timer += dtime;
1291 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1294 Wait for caches to be removed before continuing.
1296 This disables the existence of caches while locked
1298 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1300 core::list<v2s16>::Iterator j;
1301 for(j=list.begin(); j!=list.end(); j++)
1303 MapSector *sector = m_sectors[*j];
1306 sector->deleteBlocks();
1311 If sector is in sector cache, remove it from there
1313 if(m_sector_cache == sector)
1315 m_sector_cache = NULL;
1318 Remove from map and delete
1320 m_sectors.remove(*j);
1326 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1327 core::list<v3s16> *deleted_blocks)
1329 JMutexAutoLock lock(m_sector_mutex);
1331 core::list<v2s16> sector_deletion_queue;
1332 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1333 for(; i.atEnd() == false; i++)
1335 MapSector *sector = i.getNode()->getValue();
1337 Delete sector from memory if it hasn't been used in a long time
1339 if(sector->usage_timer > timeout)
1341 sector_deletion_queue.push_back(i.getNode()->getKey());
1343 if(deleted_blocks != NULL)
1345 // Collect positions of blocks of sector
1346 MapSector *sector = i.getNode()->getValue();
1347 core::list<MapBlock*> blocks;
1348 sector->getBlocks(blocks);
1349 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1350 i != blocks.end(); i++)
1352 deleted_blocks->push_back((*i)->getPos());
1357 deleteSectors(sector_deletion_queue, only_blocks);
1358 return sector_deletion_queue.getSize();
1361 void Map::PrintInfo(std::ostream &out)
1366 #define WATER_DROP_BOOST 4
1368 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1370 DSTACK(__FUNCTION_NAME);
1371 TimeTaker timer("transformLiquids()");
1374 u32 initial_size = m_transforming_liquid.size();
1375 while(m_transforming_liquid.size() != 0)
1377 v3s16 p0 = m_transforming_liquid.pop_front();
1379 MapNode n0 = getNode(p0);
1381 // Don't deal with non-liquids
1382 if(content_liquid(n0.d) == false)
1385 bool is_source = !content_flowing_liquid(n0.d);
1387 u8 liquid_level = 8;
1388 if(is_source == false)
1389 liquid_level = n0.param2 & 0x0f;
1391 // Turn possible source into non-source
1392 u8 nonsource_c = make_liquid_flowing(n0.d);
1395 If not source, check that some node flows into this one
1396 and what is the level of liquid in this one
1398 if(is_source == false)
1400 s8 new_liquid_level_max = -1;
1402 v3s16 dirs_from[5] = {
1403 v3s16(0,1,0), // top
1404 v3s16(0,0,1), // back
1405 v3s16(1,0,0), // right
1406 v3s16(0,0,-1), // front
1407 v3s16(-1,0,0), // left
1409 for(u16 i=0; i<5; i++)
1414 bool from_top = (i==0);
1416 v3s16 p2 = p0 + dirs_from[i];
1417 MapNode n2 = getNode(p2);
1419 if(content_liquid(n2.d))
1421 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1422 // Check that the liquids are the same type
1423 if(n2_nonsource_c != nonsource_c)
1425 dstream<<"WARNING: Not handling: different liquids"
1426 " collide"<<std::endl;
1429 bool n2_is_source = !content_flowing_liquid(n2.d);
1430 s8 n2_liquid_level = 8;
1431 if(n2_is_source == false)
1432 n2_liquid_level = n2.param2 & 0x07;
1434 s8 new_liquid_level = -1;
1437 //new_liquid_level = 7;
1438 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1439 new_liquid_level = 7;
1441 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1443 else if(n2_liquid_level > 0)
1445 new_liquid_level = n2_liquid_level - 1;
1448 if(new_liquid_level > new_liquid_level_max)
1449 new_liquid_level_max = new_liquid_level;
1452 }catch(InvalidPositionException &e)
1458 If liquid level should be something else, update it and
1459 add all the neighboring water nodes to the transform queue.
1461 if(new_liquid_level_max != liquid_level)
1463 if(new_liquid_level_max == -1)
1465 // Remove water alltoghether
1472 n0.param2 = new_liquid_level_max;
1476 // Block has been modified
1478 v3s16 blockpos = getNodeBlockPos(p0);
1479 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1481 modified_blocks.insert(blockpos, block);
1485 Add neighboring non-source liquid nodes to transform queue.
1488 v3s16(0,0,1), // back
1489 v3s16(0,1,0), // top
1490 v3s16(1,0,0), // right
1491 v3s16(0,0,-1), // front
1492 v3s16(0,-1,0), // bottom
1493 v3s16(-1,0,0), // left
1495 for(u16 i=0; i<6; i++)
1500 v3s16 p2 = p0 + dirs[i];
1502 MapNode n2 = getNode(p2);
1503 if(content_flowing_liquid(n2.d))
1505 m_transforming_liquid.push_back(p2);
1508 }catch(InvalidPositionException &e)
1515 // Get a new one from queue if the node has turned into non-water
1516 if(content_liquid(n0.d) == false)
1520 Flow water from this node
1522 v3s16 dirs_to[5] = {
1523 v3s16(0,-1,0), // bottom
1524 v3s16(0,0,1), // back
1525 v3s16(1,0,0), // right
1526 v3s16(0,0,-1), // front
1527 v3s16(-1,0,0), // left
1529 for(u16 i=0; i<5; i++)
1534 bool to_bottom = (i == 0);
1536 // If liquid is at lowest possible height, it's not going
1537 // anywhere except down
1538 if(liquid_level == 0 && to_bottom == false)
1541 u8 liquid_next_level = 0;
1542 // If going to bottom
1545 //liquid_next_level = 7;
1546 if(liquid_level >= 7 - WATER_DROP_BOOST)
1547 liquid_next_level = 7;
1549 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1552 liquid_next_level = liquid_level - 1;
1554 bool n2_changed = false;
1555 bool flowed = false;
1557 v3s16 p2 = p0 + dirs_to[i];
1559 MapNode n2 = getNode(p2);
1561 if(content_liquid(n2.d))
1563 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1564 // Check that the liquids are the same type
1565 if(n2_nonsource_c != nonsource_c)
1567 dstream<<"WARNING: Not handling: different liquids"
1568 " collide"<<std::endl;
1571 bool n2_is_source = !content_flowing_liquid(n2.d);
1572 u8 n2_liquid_level = 8;
1573 if(n2_is_source == false)
1574 n2_liquid_level = n2.param2 & 0x07;
1583 // Just flow into the source, nothing changes.
1584 // n2_changed is not set because destination didn't change
1589 if(liquid_next_level > liquid_level)
1591 n2.param2 = liquid_next_level;
1599 else if(n2.d == CONTENT_AIR)
1602 n2.param2 = liquid_next_level;
1611 m_transforming_liquid.push_back(p2);
1613 v3s16 blockpos = getNodeBlockPos(p2);
1614 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1616 modified_blocks.insert(blockpos, block);
1619 // If n2_changed to bottom, don't flow anywhere else
1620 if(to_bottom && flowed)
1623 }catch(InvalidPositionException &e)
1629 //if(loopcount >= 100000)
1630 if(loopcount >= initial_size * 1)
1633 dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1640 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1645 Experimental and debug stuff
1649 dstream<<"Generating map point attribute lists"<<std::endl;
1651 PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
1652 PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
1653 PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
1654 PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
1655 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
1658 NOTE: BEWARE: Too big amount of these will make map generation
1659 slow. Especially those that are read by every block emerge.
1667 for(u32 i=0; i<5000; i++)
1669 /*u32 lim = MAP_GENERATION_LIMIT;
1673 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1676 -lim + myrand()%(lim*2),
1678 -lim + myrand()%(lim*2)
1680 /*float plants_amount = (float)(myrand()%1050) / 1000.0;
1681 plants_amount = pow(plants_amount, 5);
1682 list_plants_amount->addPoint(p, Attribute(plants_amount));*/
1684 float plants_amount = 0;
1687 plants_amount = 1.5;
1689 else if(myrand()%4 == 0)
1691 plants_amount = 0.5;
1693 else if(myrand()%2 == 0)
1695 plants_amount = 0.03;
1699 plants_amount = 0.0;
1703 list_plants_amount->addPoint(p, Attribute(plants_amount));
1706 for(u32 i=0; i<1000; i++)
1708 /*u32 lim = MAP_GENERATION_LIMIT;
1712 u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000;
1715 -lim + myrand()%(lim*2),
1717 -lim + myrand()%(lim*2)
1720 float caves_amount = 0;
1725 else if(myrand()%3 == 0)
1731 caves_amount = 0.05;
1734 list_caves_amount->addPoint(p, Attribute(caves_amount));
1737 for(u32 i=0; i<5000; i++)
1739 /*u32 lim = MAP_GENERATION_LIMIT;
1743 u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
1746 -lim + (myrand()%(lim*2)),
1748 -lim + (myrand()%(lim*2))
1751 /*s32 bh_i = (myrand()%200) - 50;
1752 float baseheight = (float)bh_i;
1756 float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
1757 randmax = pow(randmax, e);
1759 //float randmax = (float)(myrand()%60);
1760 float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
1762 float baseheight = 0;
1764 float randfactor = 0;
1772 else if(myrand()%6 == 0)
1778 else if(myrand()%4 == 0)
1784 else if(myrand()%3 == 0)
1797 list_baseheight->addPoint(p, Attribute(baseheight));
1798 list_randmax->addPoint(p, Attribute(randmax));
1799 list_randfactor->addPoint(p, Attribute(randfactor));
1802 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
1803 list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
1804 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
1807 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1808 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1809 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1813 Try to load map; if not found, create a new one.
1816 m_savedir = savedir;
1817 m_map_saving_enabled = false;
1821 // If directory exists, check contents and load if possible
1822 if(fs::PathExists(m_savedir))
1824 // If directory is empty, it is safe to save into it.
1825 if(fs::GetDirListing(m_savedir).size() == 0)
1827 dstream<<DTIME<<"Server: Empty save directory is valid."
1829 m_map_saving_enabled = true;
1833 // Load master heightmap
1834 loadMasterHeightmap();
1836 // Load sector (0,0) and throw and exception on fail
1837 if(loadSectorFull(v2s16(0,0)) == false)
1838 throw LoadError("Failed to load sector (0,0)");
1840 dstream<<DTIME<<"Server: Successfully loaded master "
1841 "heightmap and sector (0,0) from "<<savedir<<
1842 ", assuming valid save directory."
1845 m_map_saving_enabled = true;
1846 // Map loaded, not creating new one
1850 // If directory doesn't exist, it is safe to save to it
1852 m_map_saving_enabled = true;
1855 catch(std::exception &e)
1857 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1858 <<", exception: "<<e.what()<<std::endl;
1859 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1860 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1863 dstream<<DTIME<<"Initializing new map."<<std::endl;
1865 // Create master heightmap
1866 /*ValueGenerator *maxgen =
1867 ValueGenerator::deSerialize(hmp.randmax);
1868 ValueGenerator *factorgen =
1869 ValueGenerator::deSerialize(hmp.randfactor);
1870 ValueGenerator *basegen =
1871 ValueGenerator::deSerialize(hmp.base);
1872 m_heightmap = new UnlimitedHeightmap
1873 (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
1875 /*m_heightmap = new UnlimitedHeightmap
1876 (hmp.blocksize, &m_padb);*/
1878 m_heightmap = new UnlimitedHeightmap
1881 // Set map parameters
1884 // Create zero sector
1885 emergeSector(v2s16(0,0));
1887 // Initially write whole map
1891 ServerMap::~ServerMap()
1895 if(m_map_saving_enabled)
1898 // Save only changed parts
1900 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1904 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1907 catch(std::exception &e)
1909 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1910 <<", exception: "<<e.what()<<std::endl;
1913 if(m_heightmap != NULL)
1917 MapSector * ServerMap::emergeSector(v2s16 p2d)
1919 DSTACK("%s: p2d=(%d,%d)",
1922 // Check that it doesn't exist already
1924 return getSectorNoGenerate(p2d);
1926 catch(InvalidPositionException &e)
1931 Try to load the sector from disk.
1933 if(loadSectorFull(p2d) == true)
1935 return getSectorNoGenerate(p2d);
1939 If there is no master heightmap, throw.
1941 if(m_heightmap == NULL)
1943 throw InvalidPositionException("emergeSector(): no heightmap");
1947 Do not generate over-limit
1949 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1950 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1951 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1952 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1953 throw InvalidPositionException("emergeSector(): pos. over limit");
1956 Generate sector and heightmaps
1959 // Number of heightmaps in sector in each direction
1960 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1962 // Heightmap side width
1963 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1965 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1967 // Sector position on map in nodes
1968 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
1970 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1971 " heightmaps and objects"<<std::endl;*/
1974 Calculate some information about local properties
1977 v2s16 mhm_p = p2d * hm_split;
1979 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1980 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1981 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1982 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1985 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1986 float avgslope = 0.0;
1987 avgslope += fabs(avgheight - corners[0]);
1988 avgslope += fabs(avgheight - corners[1]);
1989 avgslope += fabs(avgheight - corners[2]);
1990 avgslope += fabs(avgheight - corners[3]);
1992 avgslope /= MAP_BLOCKSIZE;
1993 //dstream<<"avgslope="<<avgslope<<std::endl;
1995 float pitness = 0.0;
1997 a = m_heightmap->getSlope(p2d+v2s16(0,0));
2000 a = m_heightmap->getSlope(p2d+v2s16(0,1));
2003 a = m_heightmap->getSlope(p2d+v2s16(1,1));
2006 a = m_heightmap->getSlope(p2d+v2s16(1,0));
2010 pitness /= MAP_BLOCKSIZE;
2011 //dstream<<"pitness="<<pitness<<std::endl;
2014 Get local attributes
2017 float local_plants_amount = 0.0;
2019 //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
2020 //TimeTaker attrtimer("emergeSector() attribute fetch");
2022 // Get plant amount from attributes
2023 PointAttributeList *palist = m_padb.getList("plants_amount");
2025 /*local_plants_amount =
2026 palist->getNearAttr(nodepos2d).getFloat();*/
2027 local_plants_amount =
2028 palist->getInterpolatedFloat(nodepos2d);
2032 Generate sector heightmap
2035 // Loop through sub-heightmaps
2036 for(s16 y=0; y<hm_split; y++)
2037 for(s16 x=0; x<hm_split; x++)
2039 v2s16 p_in_sector = v2s16(x,y);
2040 v2s16 mhm_p = p2d * hm_split + p_in_sector;
2042 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
2043 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
2044 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
2045 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
2048 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
2049 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
2052 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
2054 sector->setHeightmap(p_in_sector, hm);
2056 //TODO: Make these values configurable
2057 //hm->generateContinued(0.0, 0.0, corners);
2058 //hm->generateContinued(0.25, 0.2, corners);
2059 //hm->generateContinued(0.5, 0.2, corners);
2060 //hm->generateContinued(1.0, 0.2, corners);
2061 //hm->generateContinued(2.0, 0.2, corners);
2062 //hm->generateContinued(2.0 * avgslope, 0.5, corners);
2063 hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners);
2072 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
2073 sector->setObjects(objects);
2075 float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
2078 Plant some trees if there is not much slope
2081 // Avgslope is the derivative of a hill
2082 //float t = avgslope * avgslope;
2084 float a = area/16 * m_params.plants_amount * local_plants_amount;
2086 //float something = 0.17*0.17;
2087 float something = 0.3;
2089 tree_max = a / (t/something);
2093 u32 count = (myrand()%(tree_max+1));
2094 //u32 count = tree_max;
2095 for(u32 i=0; i<count; i++)
2097 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
2098 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
2099 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2102 objects->insert(v3s16(x, y, z),
2103 SECTOR_OBJECT_TREE_1);
2107 Plant some bushes if sector is pit-like
2110 // Pitness usually goes at around -0.5...0.5
2112 u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount;
2114 bush_max = (pitness*a*4);
2117 u32 count = (myrand()%(bush_max+1));
2118 for(u32 i=0; i<count; i++)
2120 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
2121 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
2122 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2125 objects->insert(v3s16(x, y, z),
2126 SECTOR_OBJECT_BUSH_1);
2130 Add ravine (randomly)
2132 if(m_params.ravines_amount > 0.001)
2134 if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
2137 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2138 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2141 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2142 objects->insert(v3s16(x, y, z),
2143 SECTOR_OBJECT_RAVINE);
2150 JMutexAutoLock lock(m_sector_mutex);
2151 m_sectors.insert(p2d, sector);
2156 MapBlock * ServerMap::emergeBlock(
2158 bool only_from_disk,
2159 core::map<v3s16, MapBlock*> &changed_blocks,
2160 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2163 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
2165 p.X, p.Y, p.Z, only_from_disk);
2167 /*dstream<<"ServerMap::emergeBlock(): "
2168 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2169 <<", only_from_disk="<<only_from_disk<<std::endl;*/
2170 v2s16 p2d(p.X, p.Z);
2173 This will create or load a sector if not found in memory.
2174 If block exists on disk, it will be loaded.
2176 NOTE: On old save formats, this will be slow, as it generates
2177 lighting on blocks for them.
2179 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
2180 assert(sector->getId() == MAPSECTOR_SERVER);
2182 // Try to get a block from the sector
2183 MapBlock *block = NULL;
2184 bool not_on_disk = false;
2186 block = sector->getBlockNoCreate(block_y);
2187 if(block->isDummy() == true)
2192 catch(InvalidPositionException &e)
2198 If block was not found on disk and not going to generate a
2199 new one, make sure there is a dummy block in place.
2201 if(not_on_disk && only_from_disk)
2205 // Create dummy block
2206 block = new MapBlock(this, p, true);
2208 // Add block to sector
2209 sector->insertBlock(block);
2215 //dstream<<"Not found on disk, generating."<<std::endl;
2216 //TimeTaker("emergeBlock()", g_irrlicht);
2219 Do not generate over-limit
2221 if(blockpos_over_limit(p))
2222 throw InvalidPositionException("emergeBlock(): pos. over limit");
2227 Go on generating the block.
2229 TODO: If a dungeon gets generated so that it's side gets
2230 revealed to the outside air, the lighting should be
2235 If block doesn't exist, create one.
2236 If it exists, it is a dummy. In that case unDummify() it.
2238 NOTE: This already sets the map as the parent of the block
2242 block = sector->createBlankBlockNoInsert(block_y);
2246 // Remove the block so that nobody can get a half-generated one.
2247 sector->removeBlock(block);
2248 // Allocate the block to contain the generated data
2252 /*u8 water_material = CONTENT_WATER;
2253 if(g_settings.getBool("endless_water"))
2254 water_material = CONTENT_WATERSOURCE;*/
2255 u8 water_material = CONTENT_WATERSOURCE;
2257 s32 lowest_ground_y = 32767;
2258 s32 highest_ground_y = -32768;
2261 //sector->printHeightmaps();
2263 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2264 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2266 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
2268 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
2269 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
2270 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
2272 dstream<<"WARNING: Surface height not found in sector "
2273 "for block that is being emerged"<<std::endl;
2277 s16 surface_y = surface_y_f;
2278 //avg_ground_y += surface_y;
2279 if(surface_y < lowest_ground_y)
2280 lowest_ground_y = surface_y;
2281 if(surface_y > highest_ground_y)
2282 highest_ground_y = surface_y;
2284 s32 surface_depth = 0;
2286 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
2288 //float min_slope = 0.45;
2289 //float max_slope = 0.85;
2290 float min_slope = 0.60;
2291 float max_slope = 1.20;
2292 float min_slope_depth = 5.0;
2293 float max_slope_depth = 0;
2295 if(slope < min_slope)
2296 surface_depth = min_slope_depth;
2297 else if(slope > max_slope)
2298 surface_depth = max_slope_depth;
2300 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2302 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2304 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2309 NOTE: If there are some man-made structures above the
2310 newly created block, they won't be taken into account.
2312 if(real_y > surface_y)
2313 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2319 // If node is over heightmap y, it's air or water
2320 if(real_y > surface_y)
2322 // If under water level, it's water
2323 if(real_y < WATER_LEVEL)
2325 n.d = water_material;
2326 n.setLight(LIGHTBANK_DAY,
2327 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2329 Add to transforming liquid queue (in case it'd
2332 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
2333 m_transforming_liquid.push_back(real_pos);
2339 // Else it's ground or dungeons (air)
2342 // If it's surface_depth under ground, it's stone
2343 if(real_y <= surface_y - surface_depth)
2345 n.d = CONTENT_STONE;
2349 // It is mud if it is under the first ground
2350 // level or under water
2351 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2357 n.d = CONTENT_GRASS;
2360 //n.d = CONTENT_MUD;
2362 /*// If under water level, it's mud
2363 if(real_y < WATER_LEVEL)
2365 // Only the topmost node is grass
2366 else if(real_y <= surface_y - 1)
2369 n.d = CONTENT_GRASS;*/
2373 block->setNode(v3s16(x0,y0,z0), n);
2378 Calculate some helper variables
2381 // Completely underground if the highest part of block is under lowest
2383 // This has to be very sure; it's probably one too strict now but
2384 // that's just better.
2385 bool completely_underground =
2386 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2388 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2390 bool mostly_underwater_surface = false;
2391 if(highest_ground_y < WATER_LEVEL
2392 && some_part_underground && !completely_underground)
2393 mostly_underwater_surface = true;
2396 Get local attributes
2399 //dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
2401 float caves_amount = 0;
2405 NOTE: BEWARE: Too big amount of attribute points slows verything
2407 1 interpolation from 5000 points takes 2-3ms.
2409 //TimeTaker timer("emergeBlock() local attribute retrieval");
2410 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2411 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
2412 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
2415 //dstream<<"emergeBlock(): Done"<<std::endl;
2421 // Initialize temporary table
2422 const s32 ued = MAP_BLOCKSIZE;
2423 bool underground_emptiness[ued*ued*ued];
2424 for(s32 i=0; i<ued*ued*ued; i++)
2426 underground_emptiness[i] = 0;
2433 Initialize orp and ors. Try to find if some neighboring
2434 MapBlock has a tunnel ended in its side
2438 (float)(myrand()%ued)+0.5,
2439 (float)(myrand()%ued)+0.5,
2440 (float)(myrand()%ued)+0.5
2443 bool found_existing = false;
2449 for(s16 y=0; y<ued; y++)
2450 for(s16 x=0; x<ued; x++)
2452 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2453 if(getNode(ap).d == CONTENT_AIR)
2455 orp = v3f(x+1,y+1,0);
2456 found_existing = true;
2457 goto continue_generating;
2461 catch(InvalidPositionException &e){}
2467 for(s16 y=0; y<ued; y++)
2468 for(s16 x=0; x<ued; x++)
2470 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2471 if(getNode(ap).d == CONTENT_AIR)
2473 orp = v3f(x+1,y+1,ued-1);
2474 found_existing = true;
2475 goto continue_generating;
2479 catch(InvalidPositionException &e){}
2485 for(s16 y=0; y<ued; y++)
2486 for(s16 z=0; z<ued; z++)
2488 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2489 if(getNode(ap).d == CONTENT_AIR)
2491 orp = v3f(0,y+1,z+1);
2492 found_existing = true;
2493 goto continue_generating;
2497 catch(InvalidPositionException &e){}
2503 for(s16 y=0; y<ued; y++)
2504 for(s16 z=0; z<ued; z++)
2506 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2507 if(getNode(ap).d == CONTENT_AIR)
2509 orp = v3f(ued-1,y+1,z+1);
2510 found_existing = true;
2511 goto continue_generating;
2515 catch(InvalidPositionException &e){}
2521 for(s16 x=0; x<ued; x++)
2522 for(s16 z=0; z<ued; z++)
2524 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2525 if(getNode(ap).d == CONTENT_AIR)
2527 orp = v3f(x+1,0,z+1);
2528 found_existing = true;
2529 goto continue_generating;
2533 catch(InvalidPositionException &e){}
2539 for(s16 x=0; x<ued; x++)
2540 for(s16 z=0; z<ued; z++)
2542 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2543 if(getNode(ap).d == CONTENT_AIR)
2545 orp = v3f(x+1,ued-1,z+1);
2546 found_existing = true;
2547 goto continue_generating;
2551 catch(InvalidPositionException &e){}
2553 continue_generating:
2556 Choose whether to actually generate dungeon
2558 bool do_generate_dungeons = true;
2559 // Don't generate if no part is underground
2560 if(!some_part_underground)
2562 do_generate_dungeons = false;
2564 // Don't generate if mostly underwater surface
2565 else if(mostly_underwater_surface)
2567 do_generate_dungeons = false;
2569 // Partly underground = cave
2570 else if(!completely_underground)
2572 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2574 // Found existing dungeon underground
2575 else if(found_existing && completely_underground)
2577 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2579 // Underground and no dungeons found
2582 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
2585 if(do_generate_dungeons)
2588 Generate some tunnel starting from orp and ors
2590 for(u16 i=0; i<3; i++)
2593 (float)(myrand()%ued)+0.5,
2594 (float)(myrand()%ued)+0.5,
2595 (float)(myrand()%ued)+0.5
2599 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
2603 for(float f=0; f<1.0; f+=0.04)
2605 v3f fp = orp + vec * f;
2606 v3s16 cp(fp.X, fp.Y, fp.Z);
2608 s16 d1 = d0 + rs - 1;
2609 for(s16 z0=d0; z0<=d1; z0++)
2611 s16 si = rs - abs(z0);
2612 for(s16 x0=-si; x0<=si-1; x0++)
2614 s16 si2 = rs - abs(x0);
2615 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2621 if(isInArea(p, ued) == false)
2623 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2635 // Set to true if has caves.
2636 // Set when some non-air is changed to air when making caves.
2637 bool has_dungeons = false;
2640 Apply temporary cave data to block
2643 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2644 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2646 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2648 MapNode n = block->getNode(v3s16(x0,y0,z0));
2651 if(underground_emptiness[
2652 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2653 +ued*(y0*ued/MAP_BLOCKSIZE)
2654 +(x0*ued/MAP_BLOCKSIZE)])
2656 if(is_ground_content(n.d))
2659 has_dungeons = true;
2665 block->setNode(v3s16(x0,y0,z0), n);
2670 This is used for guessing whether or not the block should
2671 receive sunlight from the top if the top block doesn't exist
2673 block->setIsUnderground(completely_underground);
2676 Force lighting update if some part of block is partly
2677 underground and has caves.
2679 /*if(some_part_underground && !completely_underground && has_dungeons)
2681 //dstream<<"Half-ground caves"<<std::endl;
2682 lighting_invalidated_blocks[block->getPos()] = block;
2685 // DEBUG: Always update lighting
2686 //lighting_invalidated_blocks[block->getPos()] = block;
2692 if(some_part_underground)
2694 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2699 for(s16 i=0; i<underground_level/4 + 1; i++)
2701 if(myrand()%50 == 0)
2704 (myrand()%(MAP_BLOCKSIZE-2))+1,
2705 (myrand()%(MAP_BLOCKSIZE-2))+1,
2706 (myrand()%(MAP_BLOCKSIZE-2))+1
2712 //if(is_ground_content(block->getNode(cp).d))
2713 if(block->getNode(cp).d == CONTENT_STONE)
2715 block->setNode(cp, n);
2717 for(u16 i=0; i<26; i++)
2719 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2720 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2722 block->setNode(cp+g_26dirs[i], n);
2730 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2731 u16 coal_rareness = 60 / coal_amount;
2732 if(coal_rareness == 0)
2734 if(myrand()%coal_rareness == 0)
2736 u16 a = myrand() % 16;
2737 u16 amount = coal_amount * a*a*a / 1000;
2738 for(s16 i=0; i<amount; i++)
2741 (myrand()%(MAP_BLOCKSIZE-2))+1,
2742 (myrand()%(MAP_BLOCKSIZE-2))+1,
2743 (myrand()%(MAP_BLOCKSIZE-2))+1
2747 n.d = CONTENT_COALSTONE;
2749 //dstream<<"Adding coalstone"<<std::endl;
2751 //if(is_ground_content(block->getNode(cp).d))
2752 if(block->getNode(cp).d == CONTENT_STONE)
2754 block->setNode(cp, n);
2756 for(u16 i=0; i<26; i++)
2758 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2759 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2761 block->setNode(cp+g_26dirs[i], n);
2768 Create a few rats in empty blocks underground
2770 if(completely_underground)
2772 //for(u16 i=0; i<2; i++)
2775 (myrand()%(MAP_BLOCKSIZE-2))+1,
2776 (myrand()%(MAP_BLOCKSIZE-2))+1,
2777 (myrand()%(MAP_BLOCKSIZE-2))+1
2780 // Check that the place is empty
2781 //if(!is_ground_content(block->getNode(cp).d))
2784 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2785 block->addObject(obj);
2791 Add block to sector.
2793 sector->insertBlock(block);
2799 // An y-wise container of changed blocks
2800 core::map<s16, MapBlock*> changed_blocks_sector;
2803 Check if any sector's objects can be placed now.
2806 core::map<v3s16, u8> *objects = sector->getObjects();
2807 core::list<v3s16> objects_to_remove;
2808 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2809 i.atEnd() == false; i++)
2811 v3s16 p = i.getNode()->getKey();
2813 u8 d = i.getNode()->getValue();
2815 // Ground level point (user for stuff that is on ground)
2817 bool ground_found = true;
2819 // Search real ground level
2823 MapNode n = sector->getNode(gp);
2825 // If not air, go one up and continue to placing the tree
2826 if(n.d != CONTENT_AIR)
2832 // If air, go one down
2833 gp += v3s16(0,-1,0);
2835 }catch(InvalidPositionException &e)
2837 // Ground not found.
2838 ground_found = false;
2839 // This is most close to ground
2846 if(d == SECTOR_OBJECT_TEST)
2848 if(sector->isValidArea(p + v3s16(0,0,0),
2849 p + v3s16(0,0,0), &changed_blocks_sector))
2852 n.d = CONTENT_TORCH;
2853 sector->setNode(p, n);
2854 objects_to_remove.push_back(p);
2857 else if(d == SECTOR_OBJECT_TREE_1)
2859 if(ground_found == false)
2862 v3s16 p_min = gp + v3s16(-1,0,-1);
2863 v3s16 p_max = gp + v3s16(1,5,1);
2864 if(sector->isValidArea(p_min, p_max,
2865 &changed_blocks_sector))
2869 sector->setNode(gp+v3s16(0,0,0), n);
2870 sector->setNode(gp+v3s16(0,1,0), n);
2871 sector->setNode(gp+v3s16(0,2,0), n);
2872 sector->setNode(gp+v3s16(0,3,0), n);
2874 n.d = CONTENT_LEAVES;
2876 if(rand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2878 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2879 if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2880 if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2881 if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2882 /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2883 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2884 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2885 if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2887 sector->setNode(gp+v3s16(0,4,0), n);
2889 sector->setNode(gp+v3s16(-1,4,0), n);
2890 sector->setNode(gp+v3s16(1,4,0), n);
2891 sector->setNode(gp+v3s16(0,4,-1), n);
2892 sector->setNode(gp+v3s16(0,4,1), n);
2893 sector->setNode(gp+v3s16(1,4,1), n);
2894 sector->setNode(gp+v3s16(-1,4,1), n);
2895 sector->setNode(gp+v3s16(-1,4,-1), n);
2896 sector->setNode(gp+v3s16(1,4,-1), n);
2898 sector->setNode(gp+v3s16(-1,3,0), n);
2899 sector->setNode(gp+v3s16(1,3,0), n);
2900 sector->setNode(gp+v3s16(0,3,-1), n);
2901 sector->setNode(gp+v3s16(0,3,1), n);
2902 sector->setNode(gp+v3s16(1,3,1), n);
2903 sector->setNode(gp+v3s16(-1,3,1), n);
2904 sector->setNode(gp+v3s16(-1,3,-1), n);
2905 sector->setNode(gp+v3s16(1,3,-1), n);
2907 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2908 if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2909 if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2910 if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2911 /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2912 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2913 if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2914 if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2916 // Objects are identified by wanted position
2917 objects_to_remove.push_back(p);
2919 // Lighting has to be recalculated for this one.
2920 sector->getBlocksInArea(p_min, p_max,
2921 lighting_invalidated_blocks);
2924 else if(d == SECTOR_OBJECT_BUSH_1)
2926 if(ground_found == false)
2929 if(sector->isValidArea(gp + v3s16(0,0,0),
2930 gp + v3s16(0,0,0), &changed_blocks_sector))
2933 n.d = CONTENT_LEAVES;
2934 sector->setNode(gp+v3s16(0,0,0), n);
2936 // Objects are identified by wanted position
2937 objects_to_remove.push_back(p);
2940 else if(d == SECTOR_OBJECT_RAVINE)
2943 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2944 v3s16 p_max = p + v3s16(6,6,6);
2945 if(sector->isValidArea(p_min, p_max,
2946 &changed_blocks_sector))
2949 n.d = CONTENT_STONE;
2952 s16 depth = maxdepth + (myrand()%10);
2954 s16 minz = -6 - (-2);
2956 for(s16 x=-6; x<=6; x++)
2958 z += -1 + (myrand()%3);
2963 for(s16 y=depth+(myrand()%2); y<=6; y++)
2965 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2968 v3s16 p2 = p + v3s16(x,y,z-2);
2969 if(is_ground_content(sector->getNode(p2).d)
2970 && !is_mineral(sector->getNode(p2).d))
2971 sector->setNode(p2, n);
2974 v3s16 p2 = p + v3s16(x,y,z-1);
2975 if(is_ground_content(sector->getNode(p2).d)
2976 && !is_mineral(sector->getNode(p2).d))
2977 sector->setNode(p2, n2);
2980 v3s16 p2 = p + v3s16(x,y,z+0);
2981 if(is_ground_content(sector->getNode(p2).d)
2982 && !is_mineral(sector->getNode(p2).d))
2983 sector->setNode(p2, n2);
2986 v3s16 p2 = p + v3s16(x,y,z+1);
2987 if(is_ground_content(sector->getNode(p2).d)
2988 && !is_mineral(sector->getNode(p2).d))
2989 sector->setNode(p2, n);
2992 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2993 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2997 objects_to_remove.push_back(p);
2999 // Lighting has to be recalculated for this one.
3000 sector->getBlocksInArea(p_min, p_max,
3001 lighting_invalidated_blocks);
3006 dstream<<"ServerMap::emergeBlock(): "
3007 "Invalid heightmap object"
3012 catch(InvalidPositionException &e)
3014 dstream<<"WARNING: "<<__FUNCTION_NAME
3015 <<": while inserting object "<<(int)d
3016 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
3017 <<" InvalidPositionException.what()="
3018 <<e.what()<<std::endl;
3019 // This is not too fatal and seems to happen sometimes.
3024 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
3025 i != objects_to_remove.end(); i++)
3027 objects->remove(*i);
3031 Initially update sunlight
3035 core::map<v3s16, bool> light_sources;
3036 bool black_air_left = false;
3037 bool bottom_invalid =
3038 block->propagateSunlight(light_sources, true,
3039 &black_air_left, true);
3041 // If sunlight didn't reach everywhere and part of block is
3042 // above ground, lighting has to be properly updated
3043 if(black_air_left && some_part_underground)
3045 lighting_invalidated_blocks[block->getPos()] = block;
3050 lighting_invalidated_blocks[block->getPos()] = block;
3055 Translate sector's changed blocks to global changed blocks
3058 for(core::map<s16, MapBlock*>::Iterator
3059 i = changed_blocks_sector.getIterator();
3060 i.atEnd() == false; i++)
3062 MapBlock *block = i.getNode()->getValue();
3064 changed_blocks.insert(block->getPos(), block);
3073 <<"lighting_invalidated_blocks.size()"
3077 <<" "<<lighting_invalidated_blocks.size()
3078 <<", "<<has_dungeons
3079 <<", "<<completely_underground
3080 <<", "<<some_part_underground
3085 Debug mode operation
3087 bool haxmode = g_settings.getBool("haxmode");
3090 // Don't calculate lighting at all
3091 //lighting_invalidated_blocks.clear();
3097 void ServerMap::createDir(std::string path)
3099 if(fs::CreateDir(path) == false)
3101 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3102 <<"\""<<path<<"\""<<std::endl;
3103 throw BaseException("ServerMap failed to create directory");
3107 std::string ServerMap::getSectorSubDir(v2s16 pos)
3110 snprintf(cc, 9, "%.4x%.4x",
3111 (unsigned int)pos.X&0xffff,
3112 (unsigned int)pos.Y&0xffff);
3114 return std::string(cc);
3117 std::string ServerMap::getSectorDir(v2s16 pos)
3119 return m_savedir + "/sectors/" + getSectorSubDir(pos);
3122 v2s16 ServerMap::getSectorPos(std::string dirname)
3124 if(dirname.size() != 8)
3125 throw InvalidFilenameException("Invalid sector directory name");
3127 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
3129 throw InvalidFilenameException("Invalid sector directory name");
3130 v2s16 pos((s16)x, (s16)y);
3134 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3136 v2s16 p2d = getSectorPos(sectordir);
3138 if(blockfile.size() != 4){
3139 throw InvalidFilenameException("Invalid block filename");
3142 int r = sscanf(blockfile.c_str(), "%4x", &y);
3144 throw InvalidFilenameException("Invalid block filename");
3145 return v3s16(p2d.X, y, p2d.Y);
3149 #define ENABLE_SECTOR_SAVING 1
3150 #define ENABLE_SECTOR_LOADING 1
3151 #define ENABLE_BLOCK_SAVING 1
3152 #define ENABLE_BLOCK_LOADING 1
3154 void ServerMap::save(bool only_changed)
3156 DSTACK(__FUNCTION_NAME);
3157 if(m_map_saving_enabled == false)
3159 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
3163 if(only_changed == false)
3164 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
3167 saveMasterHeightmap();
3169 u32 sector_meta_count = 0;
3170 u32 block_count = 0;
3173 JMutexAutoLock lock(m_sector_mutex);
3175 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3176 for(; i.atEnd() == false; i++)
3178 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3179 assert(sector->getId() == MAPSECTOR_SERVER);
3181 if(ENABLE_SECTOR_SAVING)
3183 if(sector->differs_from_disk || only_changed == false)
3185 saveSectorMeta(sector);
3186 sector_meta_count++;
3189 if(ENABLE_BLOCK_SAVING)
3191 core::list<MapBlock*> blocks;
3192 sector->getBlocks(blocks);
3193 core::list<MapBlock*>::Iterator j;
3194 for(j=blocks.begin(); j!=blocks.end(); j++)
3196 MapBlock *block = *j;
3197 if(block->getChangedFlag() || only_changed == false)
3209 Only print if something happened or saved whole map
3211 if(only_changed == false || sector_meta_count != 0
3212 || block_count != 0)
3214 dstream<<DTIME<<"ServerMap: Written: "
3215 <<sector_meta_count<<" sector metadata files, "
3216 <<block_count<<" block files"
3221 void ServerMap::loadAll()
3223 DSTACK(__FUNCTION_NAME);
3224 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
3226 loadMasterHeightmap();
3228 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
3230 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
3232 JMutexAutoLock lock(m_sector_mutex);
3235 s32 printed_counter = -100000;
3236 s32 count = list.size();
3238 std::vector<fs::DirListNode>::iterator i;
3239 for(i=list.begin(); i!=list.end(); i++)
3241 if(counter > printed_counter + 10)
3243 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
3244 printed_counter = counter;
3248 MapSector *sector = NULL;
3250 // We want directories
3254 sector = loadSectorMeta(i->name);
3256 catch(InvalidFilenameException &e)
3258 // This catches unknown crap in directory
3261 if(ENABLE_BLOCK_LOADING)
3263 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3264 (m_savedir+"/sectors/"+i->name);
3265 std::vector<fs::DirListNode>::iterator i2;
3266 for(i2=list2.begin(); i2!=list2.end(); i2++)
3272 loadBlock(i->name, i2->name, sector);
3274 catch(InvalidFilenameException &e)
3276 // This catches unknown crap in directory
3281 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
3284 void ServerMap::saveMasterHeightmap()
3286 DSTACK(__FUNCTION_NAME);
3287 createDir(m_savedir);
3289 std::string fullpath = m_savedir + "/master_heightmap";
3290 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3291 if(o.good() == false)
3292 throw FileNotGoodException("Cannot open master heightmap");
3294 // Format used for writing
3295 u8 version = SER_FMT_VER_HIGHEST;
3298 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
3300 [0] u8 serialization version
3301 [1] X master heightmap
3303 u32 fullsize = 1 + hmdata.getSize();
3304 SharedBuffer<u8> data(fullsize);
3307 memcpy(&data[1], *hmdata, hmdata.getSize());
3309 o.write((const char*)*data, fullsize);
3312 m_heightmap->serialize(o, version);
3315 void ServerMap::loadMasterHeightmap()
3317 DSTACK(__FUNCTION_NAME);
3318 std::string fullpath = m_savedir + "/master_heightmap";
3319 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3320 if(is.good() == false)
3321 throw FileNotGoodException("Cannot open master heightmap");
3323 if(m_heightmap != NULL)
3326 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
3329 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3331 DSTACK(__FUNCTION_NAME);
3332 // Format used for writing
3333 u8 version = SER_FMT_VER_HIGHEST;
3335 v2s16 pos = sector->getPos();
3336 createDir(m_savedir);
3337 createDir(m_savedir+"/sectors");
3338 std::string dir = getSectorDir(pos);
3341 std::string fullpath = dir + "/heightmap";
3342 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3343 if(o.good() == false)
3344 throw FileNotGoodException("Cannot open master heightmap");
3346 sector->serialize(o, version);
3348 sector->differs_from_disk = false;
3351 MapSector* ServerMap::loadSectorMeta(std::string dirname)
3353 DSTACK(__FUNCTION_NAME);
3355 v2s16 p2d = getSectorPos(dirname);
3356 std::string dir = m_savedir + "/sectors/" + dirname;
3358 std::string fullpath = dir + "/heightmap";
3359 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3360 if(is.good() == false)
3361 throw FileNotGoodException("Cannot open sector heightmap");
3363 ServerMapSector *sector = ServerMapSector::deSerialize
3364 (is, this, p2d, &m_hwrapper, m_sectors);
3366 sector->differs_from_disk = false;
3371 bool ServerMap::loadSectorFull(v2s16 p2d)
3373 DSTACK(__FUNCTION_NAME);
3374 std::string sectorsubdir = getSectorSubDir(p2d);
3376 MapSector *sector = NULL;
3378 JMutexAutoLock lock(m_sector_mutex);
3381 sector = loadSectorMeta(sectorsubdir);
3383 catch(InvalidFilenameException &e)
3387 catch(FileNotGoodException &e)
3391 catch(std::exception &e)
3396 if(ENABLE_BLOCK_LOADING)
3398 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3399 (m_savedir+"/sectors/"+sectorsubdir);
3400 std::vector<fs::DirListNode>::iterator i2;
3401 for(i2=list2.begin(); i2!=list2.end(); i2++)
3407 loadBlock(sectorsubdir, i2->name, sector);
3409 catch(InvalidFilenameException &e)
3411 // This catches unknown crap in directory
3419 bool ServerMap::deFlushSector(v2s16 p2d)
3421 DSTACK(__FUNCTION_NAME);
3422 // See if it already exists in memory
3424 MapSector *sector = getSectorNoGenerate(p2d);
3427 catch(InvalidPositionException &e)
3430 Try to load the sector from disk.
3432 if(loadSectorFull(p2d) == true)
3441 void ServerMap::saveBlock(MapBlock *block)
3443 DSTACK(__FUNCTION_NAME);
3445 Dummy blocks are not written
3447 if(block->isDummy())
3449 /*v3s16 p = block->getPos();
3450 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3451 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3455 // Format used for writing
3456 u8 version = SER_FMT_VER_HIGHEST;
3458 v3s16 p3d = block->getPos();
3459 v2s16 p2d(p3d.X, p3d.Z);
3460 createDir(m_savedir);
3461 createDir(m_savedir+"/sectors");
3462 std::string dir = getSectorDir(p2d);
3465 // Block file is map/sectors/xxxxxxxx/xxxx
3467 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
3468 std::string fullpath = dir + "/" + cc;
3469 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3470 if(o.good() == false)
3471 throw FileNotGoodException("Cannot open block data");
3474 [0] u8 serialization version
3477 o.write((char*)&version, 1);
3479 block->serialize(o, version);
3482 Versions up from 9 have block objects.
3486 block->serializeObjects(o, version);
3489 // We just wrote it to the disk
3490 block->resetChangedFlag();
3493 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
3495 DSTACK(__FUNCTION_NAME);
3499 // Block file is map/sectors/xxxxxxxx/xxxx
3500 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
3501 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3502 if(is.good() == false)
3503 throw FileNotGoodException("Cannot open block file");
3505 v3s16 p3d = getBlockPos(sectordir, blockfile);
3506 v2s16 p2d(p3d.X, p3d.Z);
3508 assert(sector->getPos() == p2d);
3510 u8 version = SER_FMT_VER_INVALID;
3511 is.read((char*)&version, 1);
3513 /*u32 block_size = MapBlock::serializedLength(version);
3514 SharedBuffer<u8> data(block_size);
3515 is.read((char*)*data, block_size);*/
3517 // This will always return a sector because we're the server
3518 //MapSector *sector = emergeSector(p2d);
3520 MapBlock *block = NULL;
3521 bool created_new = false;
3523 block = sector->getBlockNoCreate(p3d.Y);
3525 catch(InvalidPositionException &e)
3527 block = sector->createBlankBlockNoInsert(p3d.Y);
3531 // deserialize block data
3532 block->deSerialize(is, version);
3535 Versions up from 9 have block objects.
3539 block->updateObjects(is, version, NULL, 0);
3543 sector->insertBlock(block);
3546 Convert old formats to new and save
3549 // Save old format blocks in new format
3550 if(version < SER_FMT_VER_HIGHEST)
3555 // We just loaded it from the disk, so it's up-to-date.
3556 block->resetChangedFlag();
3559 catch(SerializationError &e)
3561 dstream<<"WARNING: Invalid block data on disk "
3562 "(SerializationError). Ignoring."
3567 // Gets from master heightmap
3568 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
3570 assert(m_heightmap != NULL);
3578 corners[0] = m_heightmap->getGroundHeight
3579 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
3580 corners[1] = m_heightmap->getGroundHeight
3581 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
3582 corners[2] = m_heightmap->getGroundHeight
3583 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
3584 corners[3] = m_heightmap->getGroundHeight
3585 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
3588 void ServerMap::PrintInfo(std::ostream &out)
3599 ClientMap::ClientMap(
3601 MapDrawControl &control,
3602 scene::ISceneNode* parent,
3603 scene::ISceneManager* mgr,
3607 scene::ISceneNode(parent, mgr, id),
3614 /*m_box = core::aabbox3d<f32>(0,0,0,
3615 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
3616 /*m_box = core::aabbox3d<f32>(0,0,0,
3617 map->getSizeNodes().X * BS,
3618 map->getSizeNodes().Y * BS,
3619 map->getSizeNodes().Z * BS);*/
3620 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3621 BS*1000000,BS*1000000,BS*1000000);
3623 //setPosition(v3f(BS,BS,BS));
3626 ClientMap::~ClientMap()
3628 JMutexAutoLock lock(mesh_mutex);
3637 MapSector * ClientMap::emergeSector(v2s16 p2d)
3639 DSTACK(__FUNCTION_NAME);
3640 // Check that it doesn't exist already
3642 return getSectorNoGenerate(p2d);
3644 catch(InvalidPositionException &e)
3648 // Create a sector with no heightmaps
3649 ClientMapSector *sector = new ClientMapSector(this, p2d);
3652 JMutexAutoLock lock(m_sector_mutex);
3653 m_sectors.insert(p2d, sector);
3659 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3661 DSTACK(__FUNCTION_NAME);
3662 ClientMapSector *sector = NULL;
3664 JMutexAutoLock lock(m_sector_mutex);
3666 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3670 sector = (ClientMapSector*)n->getValue();
3671 assert(sector->getId() == MAPSECTOR_CLIENT);
3675 sector = new ClientMapSector(this, p2d);
3677 JMutexAutoLock lock(m_sector_mutex);
3678 m_sectors.insert(p2d, sector);
3682 sector->deSerialize(is);
3685 void ClientMap::OnRegisterSceneNode()
3689 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3690 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3693 ISceneNode::OnRegisterSceneNode();
3696 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3698 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3699 DSTACK(__FUNCTION_NAME);
3701 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3704 Get time for measuring timeout.
3706 Measuring time is very useful for long delays when the
3707 machine is swapping a lot.
3709 int time1 = time(0);
3711 u32 daynight_ratio = m_client->getDayNightRatio();
3713 m_camera_mutex.Lock();
3714 v3f camera_position = m_camera_position;
3715 v3f camera_direction = m_camera_direction;
3716 m_camera_mutex.Unlock();
3719 Get all blocks and draw all visible ones
3722 v3s16 cam_pos_nodes(
3723 camera_position.X / BS,
3724 camera_position.Y / BS,
3725 camera_position.Z / BS);
3727 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3729 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3730 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3732 // Take a fair amount as we will be dropping more out later
3734 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3735 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3736 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3738 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3739 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3740 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3742 u32 vertex_count = 0;
3744 // For limiting number of mesh updates per frame
3745 u32 mesh_update_count = 0;
3747 u32 blocks_would_have_drawn = 0;
3748 u32 blocks_drawn = 0;
3750 //NOTE: The sectors map should be locked but we're not doing it
3751 // because it'd cause too much delays
3753 int timecheck_counter = 0;
3754 core::map<v2s16, MapSector*>::Iterator si;
3755 si = m_sectors.getIterator();
3756 for(; si.atEnd() == false; si++)
3759 timecheck_counter++;
3760 if(timecheck_counter > 50)
3762 int time2 = time(0);
3763 if(time2 > time1 + 4)
3765 dstream<<"ClientMap::renderMap(): "
3766 "Rendering takes ages, returning."
3773 MapSector *sector = si.getNode()->getValue();
3774 v2s16 sp = sector->getPos();
3776 if(m_control.range_all == false)
3778 if(sp.X < p_blocks_min.X
3779 || sp.X > p_blocks_max.X
3780 || sp.Y < p_blocks_min.Z
3781 || sp.Y > p_blocks_max.Z)
3785 core::list< MapBlock * > sectorblocks;
3786 sector->getBlocks(sectorblocks);
3792 core::list< MapBlock * >::Iterator i;
3793 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3795 MapBlock *block = *i;
3798 Compare block position to camera position, skip
3799 if not seen on display
3802 float range = 100000 * BS;
3803 if(m_control.range_all == false)
3804 range = m_control.wanted_range * BS;
3806 if(isBlockInSight(block->getPos(), camera_position,
3807 camera_direction, range) == false)
3813 v3s16 blockpos_nodes = block->getPosRelative();
3815 // Block center position
3817 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3818 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3819 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3822 // Block position relative to camera
3823 v3f blockpos_relative = blockpos - camera_position;
3825 // Distance in camera direction (+=front, -=back)
3826 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3829 f32 d = blockpos_relative.getLength();
3831 if(m_control.range_all == false)
3833 // If block is far away, don't draw it
3834 if(d > m_control.wanted_range * BS)
3838 // Maximum radius of a block
3839 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3841 // If block is (nearly) touching the camera, don't
3842 // bother validating further (that is, render it anyway)
3843 if(d > block_max_radius * 1.5)
3845 // Cosine of the angle between the camera direction
3846 // and the block direction (camera_direction is an unit vector)
3847 f32 cosangle = dforward / d;
3849 // Compensate for the size of the block
3850 // (as the block has to be shown even if it's a bit off FOV)
3851 // This is an estimate.
3852 cosangle += block_max_radius / dforward;
3854 // If block is not in the field of view, skip it
3855 //if(cosangle < cos(FOV_ANGLE/2))
3856 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3861 v3s16 blockpos_nodes = block->getPosRelative();
3863 // Block center position
3865 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3866 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3867 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3870 // Block position relative to camera
3871 v3f blockpos_relative = blockpos - camera_position;
3874 f32 d = blockpos_relative.getLength();
3881 bool mesh_expired = false;
3884 JMutexAutoLock lock(block->mesh_mutex);
3886 mesh_expired = block->getMeshExpired();
3888 // Mesh has not been expired and there is no mesh:
3889 // block has no content
3890 if(block->mesh == NULL && mesh_expired == false)
3894 f32 faraway = BS*50;
3895 //f32 faraway = m_control.wanted_range * BS;
3898 This has to be done with the mesh_mutex unlocked
3900 // Pretty random but this should work somewhat nicely
3901 if(mesh_expired && (
3902 (mesh_update_count < 3
3903 && (d < faraway || mesh_update_count < 2)
3906 (m_control.range_all && mesh_update_count < 20)
3909 /*if(mesh_expired && mesh_update_count < 6
3910 && (d < faraway || mesh_update_count < 3))*/
3912 mesh_update_count++;
3914 // Mesh has been expired: generate new mesh
3915 //block->updateMeshes(daynight_i);
3916 block->updateMesh(daynight_ratio);
3918 mesh_expired = false;
3922 Don't draw an expired mesh that is far away
3924 /*if(mesh_expired && d >= faraway)
3927 // Instead, delete it
3928 JMutexAutoLock lock(block->mesh_mutex);
3931 block->mesh->drop();
3934 // And continue to next block
3939 Draw the faces of the block
3942 JMutexAutoLock lock(block->mesh_mutex);
3944 scene::SMesh *mesh = block->mesh;
3949 blocks_would_have_drawn++;
3950 if(blocks_drawn >= m_control.wanted_max_blocks
3951 && m_control.range_all == false
3952 && d > m_control.wanted_min_range * BS)
3956 u32 c = mesh->getMeshBufferCount();
3958 for(u32 i=0; i<c; i++)
3960 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3961 const video::SMaterial& material = buf->getMaterial();
3962 video::IMaterialRenderer* rnd =
3963 driver->getMaterialRenderer(material.MaterialType);
3964 bool transparent = (rnd && rnd->isTransparent());
3965 // Render transparent on transparent pass and likewise.
3966 if(transparent == is_transparent_pass)
3968 driver->setMaterial(buf->getMaterial());
3969 driver->drawMeshBuffer(buf);
3970 vertex_count += buf->getVertexCount();
3974 } // foreach sectorblocks
3977 m_control.blocks_drawn = blocks_drawn;
3978 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3980 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3981 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3984 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod, bool *changed)
3987 Add it to all blocks touching it
3990 v3s16(0,0,0), // this
3991 v3s16(0,0,1), // back
3992 v3s16(0,1,0), // top
3993 v3s16(1,0,0), // right
3994 v3s16(0,0,-1), // front
3995 v3s16(0,-1,0), // bottom
3996 v3s16(-1,0,0), // left
3998 for(u16 i=0; i<7; i++)
4000 v3s16 p2 = p + dirs[i];
4001 // Block position of neighbor (or requested) node
4002 v3s16 blockpos = getNodeBlockPos(p2);
4003 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4004 if(blockref == NULL)
4006 // Relative position of requested node
4007 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4008 if(blockref->setTempMod(relpos, mod))
4014 return getNodeBlockPos(p);
4016 v3s16 ClientMap::clearTempMod(v3s16 p, bool *changed)
4019 v3s16(0,0,0), // this
4020 v3s16(0,0,1), // back
4021 v3s16(0,1,0), // top
4022 v3s16(1,0,0), // right
4023 v3s16(0,0,-1), // front
4024 v3s16(0,-1,0), // bottom
4025 v3s16(-1,0,0), // left
4027 for(u16 i=0; i<7; i++)
4029 v3s16 p2 = p + dirs[i];
4030 // Block position of neighbor (or requested) node
4031 v3s16 blockpos = getNodeBlockPos(p2);
4032 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4033 if(blockref == NULL)
4035 // Relative position of requested node
4036 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4037 if(blockref->clearTempMod(relpos))
4043 return getNodeBlockPos(p);
4046 void ClientMap::PrintInfo(std::ostream &out)
4057 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4062 MapVoxelManipulator::~MapVoxelManipulator()
4064 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4069 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4071 TimeTaker timer1("emerge", &emerge_time);
4073 // Units of these are MapBlocks
4074 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4075 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4077 VoxelArea block_area_nodes
4078 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4080 addArea(block_area_nodes);
4082 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4083 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4084 for(s32 x=p_min.X; x<=p_max.X; x++)
4087 core::map<v3s16, bool>::Node *n;
4088 n = m_loaded_blocks.find(p);
4092 bool block_data_inexistent = false;
4095 TimeTaker timer1("emerge load", &emerge_load_time);
4097 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4098 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4101 dstream<<std::endl;*/
4103 MapBlock *block = m_map->getBlockNoCreate(p);
4104 if(block->isDummy())
4105 block_data_inexistent = true;
4107 block->copyTo(*this);
4109 catch(InvalidPositionException &e)
4111 block_data_inexistent = true;
4114 if(block_data_inexistent)
4116 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4117 // Fill with VOXELFLAG_INEXISTENT
4118 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4119 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4121 s32 i = m_area.index(a.MinEdge.X,y,z);
4122 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4126 m_loaded_blocks.insert(p, true);
4129 //dstream<<"emerge done"<<std::endl;
4134 void MapVoxelManipulator::emerge(VoxelArea a)
4136 TimeTaker timer1("emerge", &emerge_time);
4138 v3s16 size = a.getExtent();
4140 VoxelArea padded = a;
4141 padded.pad(m_area.getExtent() / 4);
4144 for(s16 z=0; z<size.Z; z++)
4145 for(s16 y=0; y<size.Y; y++)
4146 for(s16 x=0; x<size.X; x++)
4149 s32 i = m_area.index(a.MinEdge + p);
4150 // Don't touch nodes that have already been loaded
4151 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4155 TimeTaker timer1("emerge load", &emerge_load_time);
4156 MapNode n = m_map->getNode(a.MinEdge + p);
4160 catch(InvalidPositionException &e)
4162 m_flags[i] = VOXELFLAG_INEXISTENT;
4170 TODO: Add an option to only update eg. water and air nodes.
4171 This will make it interfere less with important stuff if
4174 void MapVoxelManipulator::blitBack
4175 (core::map<v3s16, MapBlock*> & modified_blocks)
4177 if(m_area.getExtent() == v3s16(0,0,0))
4180 //TimeTaker timer1("blitBack");
4183 Initialize block cache
4185 v3s16 blockpos_last;
4186 MapBlock *block = NULL;
4187 bool block_checked_in_modified = false;
4189 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4190 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4191 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4195 u8 f = m_flags[m_area.index(p)];
4196 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4199 MapNode &n = m_data[m_area.index(p)];
4201 v3s16 blockpos = getNodeBlockPos(p);
4206 if(block == NULL || blockpos != blockpos_last){
4207 block = m_map->getBlockNoCreate(blockpos);
4208 blockpos_last = blockpos;
4209 block_checked_in_modified = false;
4212 // Calculate relative position in block
4213 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4215 // Don't continue if nothing has changed here
4216 if(block->getNode(relpos) == n)
4219 //m_map->setNode(m_area.MinEdge + p, n);
4220 block->setNode(relpos, n);
4223 Make sure block is in modified_blocks
4225 if(block_checked_in_modified == false)
4227 modified_blocks[blockpos] = block;
4228 block_checked_in_modified = true;
4231 catch(InvalidPositionException &e)