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.size()<<" blocks."<<std::endl;*/
627 //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)
1842 list_baseheight->addPoint(p, Attribute(baseheight));
1843 list_randmax->addPoint(p, Attribute(randmax));
1844 list_randfactor->addPoint(p, Attribute(randfactor));
1847 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
1848 list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
1849 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
1852 /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
1853 list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
1854 list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
1858 Try to load map; if not found, create a new one.
1861 m_savedir = savedir;
1862 m_map_saving_enabled = false;
1866 // If directory exists, check contents and load if possible
1867 if(fs::PathExists(m_savedir))
1869 // If directory is empty, it is safe to save into it.
1870 if(fs::GetDirListing(m_savedir).size() == 0)
1872 dstream<<DTIME<<"Server: Empty save directory is valid."
1874 m_map_saving_enabled = true;
1878 // Load master heightmap
1879 loadMasterHeightmap();
1881 // Load sector (0,0) and throw and exception on fail
1882 if(loadSectorFull(v2s16(0,0)) == false)
1883 throw LoadError("Failed to load sector (0,0)");
1885 dstream<<DTIME<<"Server: Successfully loaded master "
1886 "heightmap and sector (0,0) from "<<savedir<<
1887 ", assuming valid save directory."
1890 m_map_saving_enabled = true;
1891 // Map loaded, not creating new one
1895 // If directory doesn't exist, it is safe to save to it
1897 m_map_saving_enabled = true;
1900 catch(std::exception &e)
1902 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1903 <<", exception: "<<e.what()<<std::endl;
1904 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1905 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1908 dstream<<DTIME<<"Initializing new map."<<std::endl;
1910 // Create master heightmap
1911 /*ValueGenerator *maxgen =
1912 ValueGenerator::deSerialize(hmp.randmax);
1913 ValueGenerator *factorgen =
1914 ValueGenerator::deSerialize(hmp.randfactor);
1915 ValueGenerator *basegen =
1916 ValueGenerator::deSerialize(hmp.base);
1917 m_heightmap = new UnlimitedHeightmap
1918 (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
1920 /*m_heightmap = new UnlimitedHeightmap
1921 (hmp.blocksize, &m_padb);*/
1923 m_heightmap = new UnlimitedHeightmap
1926 // Set map parameters
1929 // Create zero sector
1930 emergeSector(v2s16(0,0));
1932 // Initially write whole map
1936 ServerMap::~ServerMap()
1940 if(m_map_saving_enabled)
1943 // Save only changed parts
1945 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1949 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1952 catch(std::exception &e)
1954 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1955 <<", exception: "<<e.what()<<std::endl;
1958 if(m_heightmap != NULL)
1962 MapSector * ServerMap::emergeSector(v2s16 p2d)
1964 DSTACK("%s: p2d=(%d,%d)",
1967 // Check that it doesn't exist already
1969 return getSectorNoGenerate(p2d);
1971 catch(InvalidPositionException &e)
1976 Try to load the sector from disk.
1978 if(loadSectorFull(p2d) == true)
1980 return getSectorNoGenerate(p2d);
1984 If there is no master heightmap, throw.
1986 if(m_heightmap == NULL)
1988 throw InvalidPositionException("emergeSector(): no heightmap");
1992 Do not generate over-limit
1994 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1995 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1996 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1997 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1998 throw InvalidPositionException("emergeSector(): pos. over limit");
2001 Generate sector and heightmaps
2004 // Number of heightmaps in sector in each direction
2005 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
2007 // Heightmap side width
2008 s16 hm_d = MAP_BLOCKSIZE / hm_split;
2010 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
2012 // Sector position on map in nodes
2013 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2015 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
2016 " heightmaps and objects"<<std::endl;*/
2019 Calculate some information about local properties
2022 v2s16 mhm_p = p2d * hm_split;
2024 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
2025 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
2026 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
2027 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
2030 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
2031 float avgslope = 0.0;
2032 avgslope += fabs(avgheight - corners[0]);
2033 avgslope += fabs(avgheight - corners[1]);
2034 avgslope += fabs(avgheight - corners[2]);
2035 avgslope += fabs(avgheight - corners[3]);
2037 avgslope /= MAP_BLOCKSIZE;
2038 //dstream<<"avgslope="<<avgslope<<std::endl;
2040 float pitness = 0.0;
2042 a = m_heightmap->getSlope(p2d+v2s16(0,0));
2045 a = m_heightmap->getSlope(p2d+v2s16(0,1));
2048 a = m_heightmap->getSlope(p2d+v2s16(1,1));
2051 a = m_heightmap->getSlope(p2d+v2s16(1,0));
2055 pitness /= MAP_BLOCKSIZE;
2056 //dstream<<"pitness="<<pitness<<std::endl;
2059 Get local attributes
2062 float local_plants_amount = 0.0;
2064 //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
2065 //TimeTaker attrtimer("emergeSector() attribute fetch");
2067 // Get plant amount from attributes
2068 PointAttributeList *palist = m_padb.getList("plants_amount");
2070 /*local_plants_amount =
2071 palist->getNearAttr(nodepos2d).getFloat();*/
2072 local_plants_amount =
2073 palist->getInterpolatedFloat(nodepos2d);
2077 Generate sector heightmap
2080 // Loop through sub-heightmaps
2081 for(s16 y=0; y<hm_split; y++)
2082 for(s16 x=0; x<hm_split; x++)
2084 v2s16 p_in_sector = v2s16(x,y);
2085 v2s16 mhm_p = p2d * hm_split + p_in_sector;
2087 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
2088 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
2089 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
2090 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
2093 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
2094 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
2097 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
2099 sector->setHeightmap(p_in_sector, hm);
2101 //TODO: Make these values configurable
2102 //hm->generateContinued(0.0, 0.0, corners);
2103 //hm->generateContinued(0.25, 0.2, corners);
2104 //hm->generateContinued(0.5, 0.2, corners);
2105 //hm->generateContinued(1.0, 0.2, corners);
2106 //hm->generateContinued(2.0, 0.2, corners);
2107 //hm->generateContinued(2.0 * avgslope, 0.5, corners);
2108 hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners);
2117 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
2118 sector->setObjects(objects);
2120 float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
2123 Plant some trees if there is not much slope
2126 // Avgslope is the derivative of a hill
2127 //float t = avgslope * avgslope;
2129 float a = area/16 * m_params.plants_amount * local_plants_amount;
2131 //float something = 0.17*0.17;
2132 float something = 0.3;
2134 tree_max = a / (t/something);
2138 u32 count = (myrand()%(tree_max+1));
2139 //u32 count = tree_max;
2140 for(u32 i=0; i<count; i++)
2142 s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
2143 s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
2144 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2147 objects->insert(v3s16(x, y, z),
2148 SECTOR_OBJECT_TREE_1);
2152 Plant some bushes if sector is pit-like
2155 // Pitness usually goes at around -0.5...0.5
2157 u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount;
2159 bush_max = (pitness*a*4);
2162 u32 count = (myrand()%(bush_max+1));
2163 for(u32 i=0; i<count; i++)
2165 s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
2166 s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
2167 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2170 objects->insert(v3s16(x, y, z),
2171 SECTOR_OBJECT_BUSH_1);
2175 Add ravine (randomly)
2177 if(m_params.ravines_amount > 0.001)
2179 if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
2182 s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2183 s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
2186 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
2187 objects->insert(v3s16(x, y, z),
2188 SECTOR_OBJECT_RAVINE);
2195 JMutexAutoLock lock(m_sector_mutex);
2196 m_sectors.insert(p2d, sector);
2201 MapBlock * ServerMap::emergeBlock(
2203 bool only_from_disk,
2204 core::map<v3s16, MapBlock*> &changed_blocks,
2205 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2208 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
2210 p.X, p.Y, p.Z, only_from_disk);
2212 /*dstream<<"ServerMap::emergeBlock(): "
2213 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2214 <<", only_from_disk="<<only_from_disk<<std::endl;*/
2215 v2s16 p2d(p.X, p.Z);
2218 This will create or load a sector if not found in memory.
2219 If block exists on disk, it will be loaded.
2221 NOTE: On old save formats, this will be slow, as it generates
2222 lighting on blocks for them.
2224 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
2225 assert(sector->getId() == MAPSECTOR_SERVER);
2227 // Try to get a block from the sector
2228 MapBlock *block = NULL;
2229 bool not_on_disk = false;
2231 block = sector->getBlockNoCreate(block_y);
2232 if(block->isDummy() == true)
2237 catch(InvalidPositionException &e)
2243 If block was not found on disk and not going to generate a
2244 new one, make sure there is a dummy block in place.
2246 if(not_on_disk && only_from_disk)
2250 // Create dummy block
2251 block = new MapBlock(this, p, true);
2253 // Add block to sector
2254 sector->insertBlock(block);
2260 //dstream<<"Not found on disk, generating."<<std::endl;
2262 //TimeTaker("emergeBlock() generate");
2265 Do not generate over-limit
2267 if(blockpos_over_limit(p))
2268 throw InvalidPositionException("emergeBlock(): pos. over limit");
2273 Go on generating the block.
2275 TODO: If a dungeon gets generated so that it's side gets
2276 revealed to the outside air, the lighting should be
2281 If block doesn't exist, create one.
2282 If it exists, it is a dummy. In that case unDummify() it.
2284 NOTE: This already sets the map as the parent of the block
2288 block = sector->createBlankBlockNoInsert(block_y);
2292 // Remove the block so that nobody can get a half-generated one.
2293 sector->removeBlock(block);
2294 // Allocate the block to contain the generated data
2298 /*u8 water_material = CONTENT_WATER;
2299 if(g_settings.getBool("endless_water"))
2300 water_material = CONTENT_WATERSOURCE;*/
2301 u8 water_material = CONTENT_WATERSOURCE;
2303 s32 lowest_ground_y = 32767;
2304 s32 highest_ground_y = -32768;
2307 //sector->printHeightmaps();
2309 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2310 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2312 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
2314 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
2315 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
2316 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
2318 dstream<<"WARNING: Surface height not found in sector "
2319 "for block that is being emerged"<<std::endl;
2323 s16 surface_y = surface_y_f;
2324 //avg_ground_y += surface_y;
2325 if(surface_y < lowest_ground_y)
2326 lowest_ground_y = surface_y;
2327 if(surface_y > highest_ground_y)
2328 highest_ground_y = surface_y;
2330 s32 surface_depth = 0;
2332 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
2334 //float min_slope = 0.45;
2335 //float max_slope = 0.85;
2336 float min_slope = 0.60;
2337 float max_slope = 1.20;
2338 float min_slope_depth = 5.0;
2339 float max_slope_depth = 0;
2341 if(slope < min_slope)
2342 surface_depth = min_slope_depth;
2343 else if(slope > max_slope)
2344 surface_depth = max_slope_depth;
2346 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
2348 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2350 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
2355 NOTE: If there are some man-made structures above the
2356 newly created block, they won't be taken into account.
2358 if(real_y > surface_y)
2359 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
2365 // If node is over heightmap y, it's air or water
2366 if(real_y > surface_y)
2368 // If under water level, it's water
2369 if(real_y < WATER_LEVEL)
2371 n.d = water_material;
2372 n.setLight(LIGHTBANK_DAY,
2373 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
2375 Add to transforming liquid queue (in case it'd
2378 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
2379 m_transforming_liquid.push_back(real_pos);
2385 // Else it's ground or dungeons (air)
2388 // If it's surface_depth under ground, it's stone
2389 if(real_y <= surface_y - surface_depth)
2391 n.d = CONTENT_STONE;
2395 // It is mud if it is under the first ground
2396 // level or under water
2397 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
2403 n.d = CONTENT_GRASS;
2406 //n.d = CONTENT_MUD;
2408 /*// If under water level, it's mud
2409 if(real_y < WATER_LEVEL)
2411 // Only the topmost node is grass
2412 else if(real_y <= surface_y - 1)
2415 n.d = CONTENT_GRASS;*/
2419 block->setNode(v3s16(x0,y0,z0), n);
2424 Calculate some helper variables
2427 // Completely underground if the highest part of block is under lowest
2429 // This has to be very sure; it's probably one too strict now but
2430 // that's just better.
2431 bool completely_underground =
2432 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
2434 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
2436 bool mostly_underwater_surface = false;
2437 if(highest_ground_y < WATER_LEVEL
2438 && some_part_underground && !completely_underground)
2439 mostly_underwater_surface = true;
2442 Get local attributes
2445 //dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
2447 float caves_amount = 0;
2451 NOTE: BEWARE: Too big amount of attribute points slows verything
2453 1 interpolation from 5000 points takes 2-3ms.
2455 //TimeTaker timer("emergeBlock() local attribute retrieval");
2456 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2457 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
2458 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
2461 //dstream<<"emergeBlock(): Done"<<std::endl;
2467 // Initialize temporary table
2468 const s32 ued = MAP_BLOCKSIZE;
2469 bool underground_emptiness[ued*ued*ued];
2470 for(s32 i=0; i<ued*ued*ued; i++)
2472 underground_emptiness[i] = 0;
2479 Initialize orp and ors. Try to find if some neighboring
2480 MapBlock has a tunnel ended in its side
2484 (float)(myrand()%ued)+0.5,
2485 (float)(myrand()%ued)+0.5,
2486 (float)(myrand()%ued)+0.5
2489 bool found_existing = false;
2495 for(s16 y=0; y<ued; y++)
2496 for(s16 x=0; x<ued; x++)
2498 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2499 if(getNode(ap).d == CONTENT_AIR)
2501 orp = v3f(x+1,y+1,0);
2502 found_existing = true;
2503 goto continue_generating;
2507 catch(InvalidPositionException &e){}
2513 for(s16 y=0; y<ued; y++)
2514 for(s16 x=0; x<ued; x++)
2516 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2517 if(getNode(ap).d == CONTENT_AIR)
2519 orp = v3f(x+1,y+1,ued-1);
2520 found_existing = true;
2521 goto continue_generating;
2525 catch(InvalidPositionException &e){}
2531 for(s16 y=0; y<ued; y++)
2532 for(s16 z=0; z<ued; z++)
2534 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2535 if(getNode(ap).d == CONTENT_AIR)
2537 orp = v3f(0,y+1,z+1);
2538 found_existing = true;
2539 goto continue_generating;
2543 catch(InvalidPositionException &e){}
2549 for(s16 y=0; y<ued; y++)
2550 for(s16 z=0; z<ued; z++)
2552 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2553 if(getNode(ap).d == CONTENT_AIR)
2555 orp = v3f(ued-1,y+1,z+1);
2556 found_existing = true;
2557 goto continue_generating;
2561 catch(InvalidPositionException &e){}
2567 for(s16 x=0; x<ued; x++)
2568 for(s16 z=0; z<ued; z++)
2570 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2571 if(getNode(ap).d == CONTENT_AIR)
2573 orp = v3f(x+1,0,z+1);
2574 found_existing = true;
2575 goto continue_generating;
2579 catch(InvalidPositionException &e){}
2585 for(s16 x=0; x<ued; x++)
2586 for(s16 z=0; z<ued; z++)
2588 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
2589 if(getNode(ap).d == CONTENT_AIR)
2591 orp = v3f(x+1,ued-1,z+1);
2592 found_existing = true;
2593 goto continue_generating;
2597 catch(InvalidPositionException &e){}
2599 continue_generating:
2602 Choose whether to actually generate dungeon
2604 bool do_generate_dungeons = true;
2605 // Don't generate if no part is underground
2606 if(!some_part_underground)
2608 do_generate_dungeons = false;
2610 // Don't generate if mostly underwater surface
2611 else if(mostly_underwater_surface)
2613 do_generate_dungeons = false;
2615 // Partly underground = cave
2616 else if(!completely_underground)
2618 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2620 // Found existing dungeon underground
2621 else if(found_existing && completely_underground)
2623 do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
2625 // Underground and no dungeons found
2628 do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
2631 if(do_generate_dungeons)
2634 Generate some tunnel starting from orp and ors
2636 for(u16 i=0; i<3; i++)
2639 (float)(myrand()%ued)+0.5,
2640 (float)(myrand()%ued)+0.5,
2641 (float)(myrand()%ued)+0.5
2645 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
2649 for(float f=0; f<1.0; f+=0.04)
2651 v3f fp = orp + vec * f;
2652 v3s16 cp(fp.X, fp.Y, fp.Z);
2654 s16 d1 = d0 + rs - 1;
2655 for(s16 z0=d0; z0<=d1; z0++)
2657 s16 si = rs - abs(z0);
2658 for(s16 x0=-si; x0<=si-1; x0++)
2660 s16 si2 = rs - abs(x0);
2661 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2667 if(isInArea(p, ued) == false)
2669 underground_emptiness[ued*ued*z + ued*y + x] = 1;
2681 // Set to true if has caves.
2682 // Set when some non-air is changed to air when making caves.
2683 bool has_dungeons = false;
2686 Apply temporary cave data to block
2689 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2690 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2692 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2694 MapNode n = block->getNode(v3s16(x0,y0,z0));
2697 if(underground_emptiness[
2698 ued*ued*(z0*ued/MAP_BLOCKSIZE)
2699 +ued*(y0*ued/MAP_BLOCKSIZE)
2700 +(x0*ued/MAP_BLOCKSIZE)])
2702 if(is_ground_content(n.d))
2705 has_dungeons = true;
2711 block->setNode(v3s16(x0,y0,z0), n);
2716 This is used for guessing whether or not the block should
2717 receive sunlight from the top if the top block doesn't exist
2719 block->setIsUnderground(completely_underground);
2722 Force lighting update if some part of block is partly
2723 underground and has caves.
2725 /*if(some_part_underground && !completely_underground && has_dungeons)
2727 //dstream<<"Half-ground caves"<<std::endl;
2728 lighting_invalidated_blocks[block->getPos()] = block;
2731 // DEBUG: Always update lighting
2732 //lighting_invalidated_blocks[block->getPos()] = block;
2738 if(some_part_underground)
2740 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
2745 for(s16 i=0; i<underground_level/4 + 1; i++)
2747 if(myrand()%50 == 0)
2750 (myrand()%(MAP_BLOCKSIZE-2))+1,
2751 (myrand()%(MAP_BLOCKSIZE-2))+1,
2752 (myrand()%(MAP_BLOCKSIZE-2))+1
2758 //if(is_ground_content(block->getNode(cp).d))
2759 if(block->getNode(cp).d == CONTENT_STONE)
2761 block->setNode(cp, n);
2763 for(u16 i=0; i<26; i++)
2765 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2766 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2768 block->setNode(cp+g_26dirs[i], n);
2776 u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
2777 u16 coal_rareness = 60 / coal_amount;
2778 if(coal_rareness == 0)
2780 if(myrand()%coal_rareness == 0)
2782 u16 a = myrand() % 16;
2783 u16 amount = coal_amount * a*a*a / 1000;
2784 for(s16 i=0; i<amount; i++)
2787 (myrand()%(MAP_BLOCKSIZE-2))+1,
2788 (myrand()%(MAP_BLOCKSIZE-2))+1,
2789 (myrand()%(MAP_BLOCKSIZE-2))+1
2793 n.d = CONTENT_COALSTONE;
2795 //dstream<<"Adding coalstone"<<std::endl;
2797 //if(is_ground_content(block->getNode(cp).d))
2798 if(block->getNode(cp).d == CONTENT_STONE)
2800 block->setNode(cp, n);
2802 for(u16 i=0; i<26; i++)
2804 //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
2805 if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
2807 block->setNode(cp+g_26dirs[i], n);
2814 Create a few rats in empty blocks underground
2816 if(completely_underground)
2818 //for(u16 i=0; i<2; i++)
2821 (myrand()%(MAP_BLOCKSIZE-2))+1,
2822 (myrand()%(MAP_BLOCKSIZE-2))+1,
2823 (myrand()%(MAP_BLOCKSIZE-2))+1
2826 // Check that the place is empty
2827 //if(!is_ground_content(block->getNode(cp).d))
2830 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
2831 block->addObject(obj);
2837 Add block to sector.
2839 sector->insertBlock(block);
2845 // An y-wise container of changed blocks
2846 core::map<s16, MapBlock*> changed_blocks_sector;
2849 Check if any sector's objects can be placed now.
2852 core::map<v3s16, u8> *objects = sector->getObjects();
2853 core::list<v3s16> objects_to_remove;
2854 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
2855 i.atEnd() == false; i++)
2857 v3s16 p = i.getNode()->getKey();
2859 u8 d = i.getNode()->getValue();
2861 // Ground level point (user for stuff that is on ground)
2863 bool ground_found = true;
2865 // Search real ground level
2869 MapNode n = sector->getNode(gp);
2871 // If not air, go one up and continue to placing the tree
2872 if(n.d != CONTENT_AIR)
2878 // If air, go one down
2879 gp += v3s16(0,-1,0);
2881 }catch(InvalidPositionException &e)
2883 // Ground not found.
2884 ground_found = false;
2885 // This is most close to ground
2892 if(d == SECTOR_OBJECT_TEST)
2894 if(sector->isValidArea(p + v3s16(0,0,0),
2895 p + v3s16(0,0,0), &changed_blocks_sector))
2898 n.d = CONTENT_TORCH;
2899 sector->setNode(p, n);
2900 objects_to_remove.push_back(p);
2903 else if(d == SECTOR_OBJECT_TREE_1)
2905 if(ground_found == false)
2908 v3s16 p_min = gp + v3s16(-1,0,-1);
2909 v3s16 p_max = gp + v3s16(1,5,1);
2910 if(sector->isValidArea(p_min, p_max,
2911 &changed_blocks_sector))
2915 sector->setNode(gp+v3s16(0,0,0), n);
2916 sector->setNode(gp+v3s16(0,1,0), n);
2917 sector->setNode(gp+v3s16(0,2,0), n);
2918 sector->setNode(gp+v3s16(0,3,0), n);
2920 n.d = CONTENT_LEAVES;
2922 if(myrand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
2924 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
2925 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
2926 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
2927 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
2928 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
2929 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
2930 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
2931 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
2933 sector->setNode(gp+v3s16(0,4,0), n);
2935 sector->setNode(gp+v3s16(-1,4,0), n);
2936 sector->setNode(gp+v3s16(1,4,0), n);
2937 sector->setNode(gp+v3s16(0,4,-1), n);
2938 sector->setNode(gp+v3s16(0,4,1), n);
2939 sector->setNode(gp+v3s16(1,4,1), n);
2940 sector->setNode(gp+v3s16(-1,4,1), n);
2941 sector->setNode(gp+v3s16(-1,4,-1), n);
2942 sector->setNode(gp+v3s16(1,4,-1), n);
2944 sector->setNode(gp+v3s16(-1,3,0), n);
2945 sector->setNode(gp+v3s16(1,3,0), n);
2946 sector->setNode(gp+v3s16(0,3,-1), n);
2947 sector->setNode(gp+v3s16(0,3,1), n);
2948 sector->setNode(gp+v3s16(1,3,1), n);
2949 sector->setNode(gp+v3s16(-1,3,1), n);
2950 sector->setNode(gp+v3s16(-1,3,-1), n);
2951 sector->setNode(gp+v3s16(1,3,-1), n);
2953 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
2954 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
2955 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
2956 if(myrand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
2957 /*if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
2958 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
2959 if(myrand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
2960 if(myrand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
2962 // Objects are identified by wanted position
2963 objects_to_remove.push_back(p);
2965 // Lighting has to be recalculated for this one.
2966 sector->getBlocksInArea(p_min, p_max,
2967 lighting_invalidated_blocks);
2970 else if(d == SECTOR_OBJECT_BUSH_1)
2972 if(ground_found == false)
2975 if(sector->isValidArea(gp + v3s16(0,0,0),
2976 gp + v3s16(0,0,0), &changed_blocks_sector))
2979 n.d = CONTENT_LEAVES;
2980 sector->setNode(gp+v3s16(0,0,0), n);
2982 // Objects are identified by wanted position
2983 objects_to_remove.push_back(p);
2986 else if(d == SECTOR_OBJECT_RAVINE)
2989 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2990 v3s16 p_max = p + v3s16(6,6,6);
2991 if(sector->isValidArea(p_min, p_max,
2992 &changed_blocks_sector))
2995 n.d = CONTENT_STONE;
2998 s16 depth = maxdepth + (myrand()%10);
3000 s16 minz = -6 - (-2);
3002 for(s16 x=-6; x<=6; x++)
3004 z += -1 + (myrand()%3);
3009 for(s16 y=depth+(myrand()%2); y<=6; y++)
3011 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
3014 v3s16 p2 = p + v3s16(x,y,z-2);
3015 if(is_ground_content(sector->getNode(p2).d)
3016 && !is_mineral(sector->getNode(p2).d))
3017 sector->setNode(p2, n);
3020 v3s16 p2 = p + v3s16(x,y,z-1);
3021 if(is_ground_content(sector->getNode(p2).d)
3022 && !is_mineral(sector->getNode(p2).d))
3023 sector->setNode(p2, n2);
3026 v3s16 p2 = p + v3s16(x,y,z+0);
3027 if(is_ground_content(sector->getNode(p2).d)
3028 && !is_mineral(sector->getNode(p2).d))
3029 sector->setNode(p2, n2);
3032 v3s16 p2 = p + v3s16(x,y,z+1);
3033 if(is_ground_content(sector->getNode(p2).d)
3034 && !is_mineral(sector->getNode(p2).d))
3035 sector->setNode(p2, n);
3038 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
3039 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
3043 objects_to_remove.push_back(p);
3045 // Lighting has to be recalculated for this one.
3046 sector->getBlocksInArea(p_min, p_max,
3047 lighting_invalidated_blocks);
3052 dstream<<"ServerMap::emergeBlock(): "
3053 "Invalid heightmap object"
3058 catch(InvalidPositionException &e)
3060 dstream<<"WARNING: "<<__FUNCTION_NAME
3061 <<": while inserting object "<<(int)d
3062 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
3063 <<" InvalidPositionException.what()="
3064 <<e.what()<<std::endl;
3065 // This is not too fatal and seems to happen sometimes.
3070 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
3071 i != objects_to_remove.end(); i++)
3073 objects->remove(*i);
3077 Initially update sunlight
3081 core::map<v3s16, bool> light_sources;
3082 bool black_air_left = false;
3083 bool bottom_invalid =
3084 block->propagateSunlight(light_sources, true,
3085 &black_air_left, true);
3087 // If sunlight didn't reach everywhere and part of block is
3088 // above ground, lighting has to be properly updated
3089 if(black_air_left && some_part_underground)
3091 lighting_invalidated_blocks[block->getPos()] = block;
3096 lighting_invalidated_blocks[block->getPos()] = block;
3101 Translate sector's changed blocks to global changed blocks
3104 for(core::map<s16, MapBlock*>::Iterator
3105 i = changed_blocks_sector.getIterator();
3106 i.atEnd() == false; i++)
3108 MapBlock *block = i.getNode()->getValue();
3110 changed_blocks.insert(block->getPos(), block);
3119 <<"lighting_invalidated_blocks.size()"
3123 <<" "<<lighting_invalidated_blocks.size()
3124 <<", "<<has_dungeons
3125 <<", "<<completely_underground
3126 <<", "<<some_part_underground
3131 Debug mode operation
3133 bool haxmode = g_settings.getBool("haxmode");
3136 // Don't calculate lighting at all
3137 //lighting_invalidated_blocks.clear();
3143 void ServerMap::createDir(std::string path)
3145 if(fs::CreateDir(path) == false)
3147 m_dout<<DTIME<<"ServerMap: Failed to create directory "
3148 <<"\""<<path<<"\""<<std::endl;
3149 throw BaseException("ServerMap failed to create directory");
3153 std::string ServerMap::getSectorSubDir(v2s16 pos)
3156 snprintf(cc, 9, "%.4x%.4x",
3157 (unsigned int)pos.X&0xffff,
3158 (unsigned int)pos.Y&0xffff);
3160 return std::string(cc);
3163 std::string ServerMap::getSectorDir(v2s16 pos)
3165 return m_savedir + "/sectors/" + getSectorSubDir(pos);
3168 v2s16 ServerMap::getSectorPos(std::string dirname)
3170 if(dirname.size() != 8)
3171 throw InvalidFilenameException("Invalid sector directory name");
3173 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
3175 throw InvalidFilenameException("Invalid sector directory name");
3176 v2s16 pos((s16)x, (s16)y);
3180 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
3182 v2s16 p2d = getSectorPos(sectordir);
3184 if(blockfile.size() != 4){
3185 throw InvalidFilenameException("Invalid block filename");
3188 int r = sscanf(blockfile.c_str(), "%4x", &y);
3190 throw InvalidFilenameException("Invalid block filename");
3191 return v3s16(p2d.X, y, p2d.Y);
3195 #define ENABLE_SECTOR_SAVING 1
3196 #define ENABLE_SECTOR_LOADING 1
3197 #define ENABLE_BLOCK_SAVING 1
3198 #define ENABLE_BLOCK_LOADING 1
3200 void ServerMap::save(bool only_changed)
3202 DSTACK(__FUNCTION_NAME);
3203 if(m_map_saving_enabled == false)
3205 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
3209 if(only_changed == false)
3210 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
3213 saveMasterHeightmap();
3215 u32 sector_meta_count = 0;
3216 u32 block_count = 0;
3219 JMutexAutoLock lock(m_sector_mutex);
3221 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
3222 for(; i.atEnd() == false; i++)
3224 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
3225 assert(sector->getId() == MAPSECTOR_SERVER);
3227 if(ENABLE_SECTOR_SAVING)
3229 if(sector->differs_from_disk || only_changed == false)
3231 saveSectorMeta(sector);
3232 sector_meta_count++;
3235 if(ENABLE_BLOCK_SAVING)
3237 core::list<MapBlock*> blocks;
3238 sector->getBlocks(blocks);
3239 core::list<MapBlock*>::Iterator j;
3240 for(j=blocks.begin(); j!=blocks.end(); j++)
3242 MapBlock *block = *j;
3243 if(block->getChangedFlag() || only_changed == false)
3255 Only print if something happened or saved whole map
3257 if(only_changed == false || sector_meta_count != 0
3258 || block_count != 0)
3260 dstream<<DTIME<<"ServerMap: Written: "
3261 <<sector_meta_count<<" sector metadata files, "
3262 <<block_count<<" block files"
3267 void ServerMap::loadAll()
3269 DSTACK(__FUNCTION_NAME);
3270 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
3272 loadMasterHeightmap();
3274 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
3276 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
3278 JMutexAutoLock lock(m_sector_mutex);
3281 s32 printed_counter = -100000;
3282 s32 count = list.size();
3284 std::vector<fs::DirListNode>::iterator i;
3285 for(i=list.begin(); i!=list.end(); i++)
3287 if(counter > printed_counter + 10)
3289 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
3290 printed_counter = counter;
3294 MapSector *sector = NULL;
3296 // We want directories
3300 sector = loadSectorMeta(i->name);
3302 catch(InvalidFilenameException &e)
3304 // This catches unknown crap in directory
3307 if(ENABLE_BLOCK_LOADING)
3309 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3310 (m_savedir+"/sectors/"+i->name);
3311 std::vector<fs::DirListNode>::iterator i2;
3312 for(i2=list2.begin(); i2!=list2.end(); i2++)
3318 loadBlock(i->name, i2->name, sector);
3320 catch(InvalidFilenameException &e)
3322 // This catches unknown crap in directory
3327 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
3330 void ServerMap::saveMasterHeightmap()
3332 DSTACK(__FUNCTION_NAME);
3333 createDir(m_savedir);
3335 std::string fullpath = m_savedir + "/master_heightmap";
3336 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3337 if(o.good() == false)
3338 throw FileNotGoodException("Cannot open master heightmap");
3340 // Format used for writing
3341 u8 version = SER_FMT_VER_HIGHEST;
3344 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
3346 [0] u8 serialization version
3347 [1] X master heightmap
3349 u32 fullsize = 1 + hmdata.getSize();
3350 SharedBuffer<u8> data(fullsize);
3353 memcpy(&data[1], *hmdata, hmdata.getSize());
3355 o.write((const char*)*data, fullsize);
3358 m_heightmap->serialize(o, version);
3361 void ServerMap::loadMasterHeightmap()
3363 DSTACK(__FUNCTION_NAME);
3364 std::string fullpath = m_savedir + "/master_heightmap";
3365 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3366 if(is.good() == false)
3367 throw FileNotGoodException("Cannot open master heightmap");
3369 if(m_heightmap != NULL)
3372 m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
3375 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3377 DSTACK(__FUNCTION_NAME);
3378 // Format used for writing
3379 u8 version = SER_FMT_VER_HIGHEST;
3381 v2s16 pos = sector->getPos();
3382 createDir(m_savedir);
3383 createDir(m_savedir+"/sectors");
3384 std::string dir = getSectorDir(pos);
3387 std::string fullpath = dir + "/heightmap";
3388 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3389 if(o.good() == false)
3390 throw FileNotGoodException("Cannot open master heightmap");
3392 sector->serialize(o, version);
3394 sector->differs_from_disk = false;
3397 MapSector* ServerMap::loadSectorMeta(std::string dirname)
3399 DSTACK(__FUNCTION_NAME);
3401 v2s16 p2d = getSectorPos(dirname);
3402 std::string dir = m_savedir + "/sectors/" + dirname;
3404 std::string fullpath = dir + "/heightmap";
3405 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3406 if(is.good() == false)
3407 throw FileNotGoodException("Cannot open sector heightmap");
3409 ServerMapSector *sector = ServerMapSector::deSerialize
3410 (is, this, p2d, &m_hwrapper, m_sectors);
3412 sector->differs_from_disk = false;
3417 bool ServerMap::loadSectorFull(v2s16 p2d)
3419 DSTACK(__FUNCTION_NAME);
3420 std::string sectorsubdir = getSectorSubDir(p2d);
3422 MapSector *sector = NULL;
3424 JMutexAutoLock lock(m_sector_mutex);
3427 sector = loadSectorMeta(sectorsubdir);
3429 catch(InvalidFilenameException &e)
3433 catch(FileNotGoodException &e)
3437 catch(std::exception &e)
3442 if(ENABLE_BLOCK_LOADING)
3444 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3445 (m_savedir+"/sectors/"+sectorsubdir);
3446 std::vector<fs::DirListNode>::iterator i2;
3447 for(i2=list2.begin(); i2!=list2.end(); i2++)
3453 loadBlock(sectorsubdir, i2->name, sector);
3455 catch(InvalidFilenameException &e)
3457 // This catches unknown crap in directory
3465 bool ServerMap::deFlushSector(v2s16 p2d)
3467 DSTACK(__FUNCTION_NAME);
3468 // See if it already exists in memory
3470 MapSector *sector = getSectorNoGenerate(p2d);
3473 catch(InvalidPositionException &e)
3476 Try to load the sector from disk.
3478 if(loadSectorFull(p2d) == true)
3487 void ServerMap::saveBlock(MapBlock *block)
3489 DSTACK(__FUNCTION_NAME);
3491 Dummy blocks are not written
3493 if(block->isDummy())
3495 /*v3s16 p = block->getPos();
3496 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3497 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3501 // Format used for writing
3502 u8 version = SER_FMT_VER_HIGHEST;
3504 v3s16 p3d = block->getPos();
3505 v2s16 p2d(p3d.X, p3d.Z);
3506 createDir(m_savedir);
3507 createDir(m_savedir+"/sectors");
3508 std::string dir = getSectorDir(p2d);
3511 // Block file is map/sectors/xxxxxxxx/xxxx
3513 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
3514 std::string fullpath = dir + "/" + cc;
3515 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3516 if(o.good() == false)
3517 throw FileNotGoodException("Cannot open block data");
3520 [0] u8 serialization version
3523 o.write((char*)&version, 1);
3525 block->serialize(o, version);
3528 Versions up from 9 have block objects.
3532 block->serializeObjects(o, version);
3535 // We just wrote it to the disk
3536 block->resetChangedFlag();
3539 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
3541 DSTACK(__FUNCTION_NAME);
3545 // Block file is map/sectors/xxxxxxxx/xxxx
3546 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
3547 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3548 if(is.good() == false)
3549 throw FileNotGoodException("Cannot open block file");
3551 v3s16 p3d = getBlockPos(sectordir, blockfile);
3552 v2s16 p2d(p3d.X, p3d.Z);
3554 assert(sector->getPos() == p2d);
3556 u8 version = SER_FMT_VER_INVALID;
3557 is.read((char*)&version, 1);
3559 /*u32 block_size = MapBlock::serializedLength(version);
3560 SharedBuffer<u8> data(block_size);
3561 is.read((char*)*data, block_size);*/
3563 // This will always return a sector because we're the server
3564 //MapSector *sector = emergeSector(p2d);
3566 MapBlock *block = NULL;
3567 bool created_new = false;
3569 block = sector->getBlockNoCreate(p3d.Y);
3571 catch(InvalidPositionException &e)
3573 block = sector->createBlankBlockNoInsert(p3d.Y);
3577 // deserialize block data
3578 block->deSerialize(is, version);
3581 Versions up from 9 have block objects.
3585 block->updateObjects(is, version, NULL, 0);
3589 sector->insertBlock(block);
3592 Convert old formats to new and save
3595 // Save old format blocks in new format
3596 if(version < SER_FMT_VER_HIGHEST)
3601 // We just loaded it from the disk, so it's up-to-date.
3602 block->resetChangedFlag();
3605 catch(SerializationError &e)
3607 dstream<<"WARNING: Invalid block data on disk "
3608 "(SerializationError). Ignoring."
3613 // Gets from master heightmap
3614 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
3616 assert(m_heightmap != NULL);
3624 corners[0] = m_heightmap->getGroundHeight
3625 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
3626 corners[1] = m_heightmap->getGroundHeight
3627 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
3628 corners[2] = m_heightmap->getGroundHeight
3629 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
3630 corners[3] = m_heightmap->getGroundHeight
3631 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
3634 void ServerMap::PrintInfo(std::ostream &out)
3645 ClientMap::ClientMap(
3647 MapDrawControl &control,
3648 scene::ISceneNode* parent,
3649 scene::ISceneManager* mgr,
3653 scene::ISceneNode(parent, mgr, id),
3660 /*m_box = core::aabbox3d<f32>(0,0,0,
3661 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
3662 /*m_box = core::aabbox3d<f32>(0,0,0,
3663 map->getSizeNodes().X * BS,
3664 map->getSizeNodes().Y * BS,
3665 map->getSizeNodes().Z * BS);*/
3666 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3667 BS*1000000,BS*1000000,BS*1000000);
3669 //setPosition(v3f(BS,BS,BS));
3672 ClientMap::~ClientMap()
3674 JMutexAutoLock lock(mesh_mutex);
3683 MapSector * ClientMap::emergeSector(v2s16 p2d)
3685 DSTACK(__FUNCTION_NAME);
3686 // Check that it doesn't exist already
3688 return getSectorNoGenerate(p2d);
3690 catch(InvalidPositionException &e)
3694 // Create a sector with no heightmaps
3695 ClientMapSector *sector = new ClientMapSector(this, p2d);
3698 JMutexAutoLock lock(m_sector_mutex);
3699 m_sectors.insert(p2d, sector);
3705 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3707 DSTACK(__FUNCTION_NAME);
3708 ClientMapSector *sector = NULL;
3710 JMutexAutoLock lock(m_sector_mutex);
3712 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3716 sector = (ClientMapSector*)n->getValue();
3717 assert(sector->getId() == MAPSECTOR_CLIENT);
3721 sector = new ClientMapSector(this, p2d);
3723 JMutexAutoLock lock(m_sector_mutex);
3724 m_sectors.insert(p2d, sector);
3728 sector->deSerialize(is);
3731 void ClientMap::OnRegisterSceneNode()
3735 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3736 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3739 ISceneNode::OnRegisterSceneNode();
3742 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3744 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3745 DSTACK(__FUNCTION_NAME);
3747 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3750 Get time for measuring timeout.
3752 Measuring time is very useful for long delays when the
3753 machine is swapping a lot.
3755 int time1 = time(0);
3757 u32 daynight_ratio = m_client->getDayNightRatio();
3759 m_camera_mutex.Lock();
3760 v3f camera_position = m_camera_position;
3761 v3f camera_direction = m_camera_direction;
3762 m_camera_mutex.Unlock();
3765 Get all blocks and draw all visible ones
3768 v3s16 cam_pos_nodes(
3769 camera_position.X / BS,
3770 camera_position.Y / BS,
3771 camera_position.Z / BS);
3773 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3775 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3776 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3778 // Take a fair amount as we will be dropping more out later
3780 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3781 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3782 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3784 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3785 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3786 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3788 u32 vertex_count = 0;
3790 // For limiting number of mesh updates per frame
3791 u32 mesh_update_count = 0;
3793 u32 blocks_would_have_drawn = 0;
3794 u32 blocks_drawn = 0;
3796 //NOTE: The sectors map should be locked but we're not doing it
3797 // because it'd cause too much delays
3799 int timecheck_counter = 0;
3800 core::map<v2s16, MapSector*>::Iterator si;
3801 si = m_sectors.getIterator();
3802 for(; si.atEnd() == false; si++)
3805 timecheck_counter++;
3806 if(timecheck_counter > 50)
3808 int time2 = time(0);
3809 if(time2 > time1 + 4)
3811 dstream<<"ClientMap::renderMap(): "
3812 "Rendering takes ages, returning."
3819 MapSector *sector = si.getNode()->getValue();
3820 v2s16 sp = sector->getPos();
3822 if(m_control.range_all == false)
3824 if(sp.X < p_blocks_min.X
3825 || sp.X > p_blocks_max.X
3826 || sp.Y < p_blocks_min.Z
3827 || sp.Y > p_blocks_max.Z)
3831 core::list< MapBlock * > sectorblocks;
3832 sector->getBlocks(sectorblocks);
3838 core::list< MapBlock * >::Iterator i;
3839 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3841 MapBlock *block = *i;
3844 Compare block position to camera position, skip
3845 if not seen on display
3848 float range = 100000 * BS;
3849 if(m_control.range_all == false)
3850 range = m_control.wanted_range * BS;
3852 if(isBlockInSight(block->getPos(), camera_position,
3853 camera_direction, range) == false)
3859 v3s16 blockpos_nodes = block->getPosRelative();
3861 // Block center position
3863 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3864 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3865 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3868 // Block position relative to camera
3869 v3f blockpos_relative = blockpos - camera_position;
3871 // Distance in camera direction (+=front, -=back)
3872 f32 dforward = blockpos_relative.dotProduct(camera_direction);
3875 f32 d = blockpos_relative.getLength();
3877 if(m_control.range_all == false)
3879 // If block is far away, don't draw it
3880 if(d > m_control.wanted_range * BS)
3884 // Maximum radius of a block
3885 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
3887 // If block is (nearly) touching the camera, don't
3888 // bother validating further (that is, render it anyway)
3889 if(d > block_max_radius * 1.5)
3891 // Cosine of the angle between the camera direction
3892 // and the block direction (camera_direction is an unit vector)
3893 f32 cosangle = dforward / d;
3895 // Compensate for the size of the block
3896 // (as the block has to be shown even if it's a bit off FOV)
3897 // This is an estimate.
3898 cosangle += block_max_radius / dforward;
3900 // If block is not in the field of view, skip it
3901 //if(cosangle < cos(FOV_ANGLE/2))
3902 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
3907 v3s16 blockpos_nodes = block->getPosRelative();
3909 // Block center position
3911 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
3912 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
3913 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
3916 // Block position relative to camera
3917 v3f blockpos_relative = blockpos - camera_position;
3920 f32 d = blockpos_relative.getLength();
3927 bool mesh_expired = false;
3930 JMutexAutoLock lock(block->mesh_mutex);
3932 mesh_expired = block->getMeshExpired();
3934 // Mesh has not been expired and there is no mesh:
3935 // block has no content
3936 if(block->mesh == NULL && mesh_expired == false)
3940 f32 faraway = BS*50;
3941 //f32 faraway = m_control.wanted_range * BS;
3944 This has to be done with the mesh_mutex unlocked
3946 // Pretty random but this should work somewhat nicely
3947 if(mesh_expired && (
3948 (mesh_update_count < 3
3949 && (d < faraway || mesh_update_count < 2)
3952 (m_control.range_all && mesh_update_count < 20)
3955 /*if(mesh_expired && mesh_update_count < 6
3956 && (d < faraway || mesh_update_count < 3))*/
3958 mesh_update_count++;
3960 // Mesh has been expired: generate new mesh
3961 //block->updateMeshes(daynight_i);
3962 block->updateMesh(daynight_ratio);
3964 mesh_expired = false;
3968 Don't draw an expired mesh that is far away
3970 /*if(mesh_expired && d >= faraway)
3973 // Instead, delete it
3974 JMutexAutoLock lock(block->mesh_mutex);
3977 block->mesh->drop();
3980 // And continue to next block
3985 Draw the faces of the block
3988 JMutexAutoLock lock(block->mesh_mutex);
3990 scene::SMesh *mesh = block->mesh;
3995 blocks_would_have_drawn++;
3996 if(blocks_drawn >= m_control.wanted_max_blocks
3997 && m_control.range_all == false
3998 && d > m_control.wanted_min_range * BS)
4002 u32 c = mesh->getMeshBufferCount();
4004 for(u32 i=0; i<c; i++)
4006 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
4007 const video::SMaterial& material = buf->getMaterial();
4008 video::IMaterialRenderer* rnd =
4009 driver->getMaterialRenderer(material.MaterialType);
4010 bool transparent = (rnd && rnd->isTransparent());
4011 // Render transparent on transparent pass and likewise.
4012 if(transparent == is_transparent_pass)
4014 driver->setMaterial(buf->getMaterial());
4015 driver->drawMeshBuffer(buf);
4016 vertex_count += buf->getVertexCount();
4020 } // foreach sectorblocks
4023 m_control.blocks_drawn = blocks_drawn;
4024 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4026 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4027 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4030 v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod, bool *changed)
4033 Add it to all blocks touching it
4036 v3s16(0,0,0), // this
4037 v3s16(0,0,1), // back
4038 v3s16(0,1,0), // top
4039 v3s16(1,0,0), // right
4040 v3s16(0,0,-1), // front
4041 v3s16(0,-1,0), // bottom
4042 v3s16(-1,0,0), // left
4044 for(u16 i=0; i<7; i++)
4046 v3s16 p2 = p + dirs[i];
4047 // Block position of neighbor (or requested) node
4048 v3s16 blockpos = getNodeBlockPos(p2);
4049 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4050 if(blockref == NULL)
4052 // Relative position of requested node
4053 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4054 if(blockref->setTempMod(relpos, mod))
4060 return getNodeBlockPos(p);
4062 v3s16 ClientMap::clearTempMod(v3s16 p, bool *changed)
4065 v3s16(0,0,0), // this
4066 v3s16(0,0,1), // back
4067 v3s16(0,1,0), // top
4068 v3s16(1,0,0), // right
4069 v3s16(0,0,-1), // front
4070 v3s16(0,-1,0), // bottom
4071 v3s16(-1,0,0), // left
4073 for(u16 i=0; i<7; i++)
4075 v3s16 p2 = p + dirs[i];
4076 // Block position of neighbor (or requested) node
4077 v3s16 blockpos = getNodeBlockPos(p2);
4078 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4079 if(blockref == NULL)
4081 // Relative position of requested node
4082 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4083 if(blockref->clearTempMod(relpos))
4089 return getNodeBlockPos(p);
4092 void ClientMap::PrintInfo(std::ostream &out)
4103 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4108 MapVoxelManipulator::~MapVoxelManipulator()
4110 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4115 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4117 TimeTaker timer1("emerge", &emerge_time);
4119 // Units of these are MapBlocks
4120 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4121 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4123 VoxelArea block_area_nodes
4124 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4126 addArea(block_area_nodes);
4128 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4129 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4130 for(s32 x=p_min.X; x<=p_max.X; x++)
4133 core::map<v3s16, bool>::Node *n;
4134 n = m_loaded_blocks.find(p);
4138 bool block_data_inexistent = false;
4141 TimeTaker timer1("emerge load", &emerge_load_time);
4143 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4144 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4147 dstream<<std::endl;*/
4149 MapBlock *block = m_map->getBlockNoCreate(p);
4150 if(block->isDummy())
4151 block_data_inexistent = true;
4153 block->copyTo(*this);
4155 catch(InvalidPositionException &e)
4157 block_data_inexistent = true;
4160 if(block_data_inexistent)
4162 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4163 // Fill with VOXELFLAG_INEXISTENT
4164 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4165 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4167 s32 i = m_area.index(a.MinEdge.X,y,z);
4168 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4172 m_loaded_blocks.insert(p, true);
4175 //dstream<<"emerge done"<<std::endl;
4183 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4185 TimeTaker timer1("emerge", &emerge_time);
4187 v3s16 size = a.getExtent();
4189 VoxelArea padded = a;
4190 padded.pad(m_area.getExtent() / 4);
4193 for(s16 z=0; z<size.Z; z++)
4194 for(s16 y=0; y<size.Y; y++)
4195 for(s16 x=0; x<size.X; x++)
4198 s32 i = m_area.index(a.MinEdge + p);
4199 // Don't touch nodes that have already been loaded
4200 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4204 TimeTaker timer1("emerge load", &emerge_load_time);
4205 MapNode n = m_map->getNode(a.MinEdge + p);
4209 catch(InvalidPositionException &e)
4211 m_flags[i] = VOXELFLAG_INEXISTENT;
4219 TODO: Add an option to only update eg. water and air nodes.
4220 This will make it interfere less with important stuff if
4223 void MapVoxelManipulator::blitBack
4224 (core::map<v3s16, MapBlock*> & modified_blocks)
4226 if(m_area.getExtent() == v3s16(0,0,0))
4229 //TimeTaker timer1("blitBack");
4231 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4232 <<m_loaded_blocks.size()<<std::endl;*/
4235 Initialize block cache
4237 v3s16 blockpos_last;
4238 MapBlock *block = NULL;
4239 bool block_checked_in_modified = false;
4241 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4242 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4243 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4247 u8 f = m_flags[m_area.index(p)];
4248 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4251 MapNode &n = m_data[m_area.index(p)];
4253 v3s16 blockpos = getNodeBlockPos(p);
4258 if(block == NULL || blockpos != blockpos_last){
4259 block = m_map->getBlockNoCreate(blockpos);
4260 blockpos_last = blockpos;
4261 block_checked_in_modified = false;
4264 // Calculate relative position in block
4265 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4267 // Don't continue if nothing has changed here
4268 if(block->getNode(relpos) == n)
4271 //m_map->setNode(m_area.MinEdge + p, n);
4272 block->setNode(relpos, n);
4275 Make sure block is in modified_blocks
4277 if(block_checked_in_modified == false)
4279 modified_blocks[blockpos] = block;
4280 block_checked_in_modified = true;
4283 catch(InvalidPositionException &e)
4289 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4290 MapVoxelManipulator(map)
4294 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4298 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4300 // Just create the area to avoid segfaults
4301 VoxelManipulator::emerge(a, caller_id);
4304 Just create the area to avoid segfaults
4307 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4308 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4309 for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++)
4311 s32 i = m_area.index(x,y,z);
4312 // Don't touch nodes that have already been loaded
4313 if(!(m_flags[i] & VOXELFLAG_NOT_LOADED))
4315 m_flags[i] = VOXELFLAG_INEXISTENT;
4319 void ManualMapVoxelManipulator::initialEmerge(
4320 v3s16 blockpos_min, v3s16 blockpos_max)
4322 TimeTaker timer1("emerge", &emerge_time);
4324 // Units of these are MapBlocks
4325 v3s16 p_min = blockpos_min;
4326 v3s16 p_max = blockpos_max;
4328 VoxelArea block_area_nodes
4329 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4331 addArea(block_area_nodes);
4333 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4334 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4335 for(s32 x=p_min.X; x<=p_max.X; x++)
4338 core::map<v3s16, bool>::Node *n;
4339 n = m_loaded_blocks.find(p);
4343 bool block_data_inexistent = false;
4346 TimeTaker timer1("emerge load", &emerge_load_time);
4348 MapBlock *block = m_map->getBlockNoCreate(p);
4349 if(block->isDummy())
4350 block_data_inexistent = true;
4352 block->copyTo(*this);
4354 catch(InvalidPositionException &e)
4356 block_data_inexistent = true;
4359 if(block_data_inexistent)
4361 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4362 // Fill with VOXELFLAG_INEXISTENT
4363 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4364 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4366 s32 i = m_area.index(a.MinEdge.X,y,z);
4367 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4371 m_loaded_blocks.insert(p, true);