3 Copyright (C) 2010-2011 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.
21 #include "mapsector.h"
32 #include "nodemetadata.h"
34 #include <IMaterialRenderer.h>
42 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
45 SQLite format specification:
46 - Initially only replaces sectors/ and sectors2/
48 If map.sqlite does not exist in the save dir
49 or the block was not found in the database
50 the map will try to load from sectors folder.
51 In either case, map.sqlite will be created
52 and all future saves will save there.
54 Structure of map.sqlite:
65 Map::Map(std::ostream &dout, IGameDef *gamedef):
70 /*m_sector_mutex.Init();
71 assert(m_sector_mutex.IsInitialized());*/
79 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
80 for(; i.atEnd() == false; i++)
82 MapSector *sector = i.getNode()->getValue();
87 void Map::addEventReceiver(MapEventReceiver *event_receiver)
89 m_event_receivers.insert(event_receiver, false);
92 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
94 if(m_event_receivers.find(event_receiver) == NULL)
96 m_event_receivers.remove(event_receiver);
99 void Map::dispatchEvent(MapEditEvent *event)
101 for(core::map<MapEventReceiver*, bool>::Iterator
102 i = m_event_receivers.getIterator();
103 i.atEnd()==false; i++)
105 MapEventReceiver* event_receiver = i.getNode()->getKey();
106 event_receiver->onMapEditEvent(event);
110 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
112 if(m_sector_cache != NULL && p == m_sector_cache_p){
113 MapSector * sector = m_sector_cache;
117 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
122 MapSector *sector = n->getValue();
124 // Cache the last result
125 m_sector_cache_p = p;
126 m_sector_cache = sector;
131 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
133 return getSectorNoGenerateNoExNoLock(p);
136 MapSector * Map::getSectorNoGenerate(v2s16 p)
138 MapSector *sector = getSectorNoGenerateNoEx(p);
140 throw InvalidPositionException();
145 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorNoGenerateNoEx(p2d);
151 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
155 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
157 MapBlock *block = getBlockNoCreateNoEx(p3d);
159 throw InvalidPositionException();
163 bool Map::isNodeUnderground(v3s16 p)
165 v3s16 blockpos = getNodeBlockPos(p);
167 MapBlock * block = getBlockNoCreate(blockpos);
168 return block->getIsUnderground();
170 catch(InvalidPositionException &e)
176 bool Map::isValidPosition(v3s16 p)
178 v3s16 blockpos = getNodeBlockPos(p);
179 MapBlock *block = getBlockNoCreate(blockpos);
180 return (block != NULL);
183 // Returns a CONTENT_IGNORE node if not found
184 MapNode Map::getNodeNoEx(v3s16 p)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreateNoEx(blockpos);
189 return MapNode(CONTENT_IGNORE);
190 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
191 return block->getNodeNoCheck(relpos);
194 // throws InvalidPositionException if not found
195 MapNode Map::getNode(v3s16 p)
197 v3s16 blockpos = getNodeBlockPos(p);
198 MapBlock *block = getBlockNoCreateNoEx(blockpos);
200 throw InvalidPositionException();
201 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
202 return block->getNodeNoCheck(relpos);
205 // throws InvalidPositionException if not found
206 void Map::setNode(v3s16 p, MapNode & n)
208 v3s16 blockpos = getNodeBlockPos(p);
209 MapBlock *block = getBlockNoCreate(blockpos);
210 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
211 block->setNodeNoCheck(relpos, n);
216 Goes recursively through the neighbours of the node.
218 Alters only transparent nodes.
220 If the lighting of the neighbour is lower than the lighting of
221 the node was (before changing it to 0 at the step before), the
222 lighting of the neighbour is set to 0 and then the same stuff
223 repeats for the neighbour.
225 The ending nodes of the routine are stored in light_sources.
226 This is useful when a light is removed. In such case, this
227 routine can be called for the light node and then again for
228 light_sources to re-light the area without the removed light.
230 values of from_nodes are lighting values.
232 void Map::unspreadLight(enum LightBank bank,
233 core::map<v3s16, u8> & from_nodes,
234 core::map<v3s16, bool> & light_sources,
235 core::map<v3s16, MapBlock*> & modified_blocks)
237 INodeDefManager *nodemgr = m_gamedef->ndef();
240 v3s16(0,0,1), // back
242 v3s16(1,0,0), // right
243 v3s16(0,0,-1), // front
244 v3s16(0,-1,0), // bottom
245 v3s16(-1,0,0), // left
248 if(from_nodes.size() == 0)
251 u32 blockchangecount = 0;
253 core::map<v3s16, u8> unlighted_nodes;
254 core::map<v3s16, u8>::Iterator j;
255 j = from_nodes.getIterator();
258 Initialize block cache
261 MapBlock *block = NULL;
262 // Cache this a bit, too
263 bool block_checked_in_modified = false;
265 for(; j.atEnd() == false; j++)
267 v3s16 pos = j.getNode()->getKey();
268 v3s16 blockpos = getNodeBlockPos(pos);
270 // Only fetch a new block if the block position has changed
272 if(block == NULL || blockpos != blockpos_last){
273 block = getBlockNoCreate(blockpos);
274 blockpos_last = blockpos;
276 block_checked_in_modified = false;
280 catch(InvalidPositionException &e)
288 // Calculate relative position in block
289 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
291 // Get node straight from the block
292 MapNode n = block->getNode(relpos);
294 u8 oldlight = j.getNode()->getValue();
296 // Loop through 6 neighbors
297 for(u16 i=0; i<6; i++)
299 // Get the position of the neighbor node
300 v3s16 n2pos = pos + dirs[i];
302 // Get the block where the node is located
303 v3s16 blockpos = getNodeBlockPos(n2pos);
307 // Only fetch a new block if the block position has changed
309 if(block == NULL || blockpos != blockpos_last){
310 block = getBlockNoCreate(blockpos);
311 blockpos_last = blockpos;
313 block_checked_in_modified = false;
317 catch(InvalidPositionException &e)
322 // Calculate relative position in block
323 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
324 // Get node straight from the block
325 MapNode n2 = block->getNode(relpos);
327 bool changed = false;
329 //TODO: Optimize output by optimizing light_sources?
332 If the neighbor is dimmer than what was specified
333 as oldlight (the light of the previous node)
335 if(n2.getLight(bank, nodemgr) < oldlight)
338 And the neighbor is transparent and it has some light
340 if(nodemgr->get(n2).light_propagates
341 && n2.getLight(bank, nodemgr) != 0)
344 Set light to 0 and add to queue
347 u8 current_light = n2.getLight(bank, nodemgr);
348 n2.setLight(bank, 0, nodemgr);
349 block->setNode(relpos, n2);
351 unlighted_nodes.insert(n2pos, current_light);
355 Remove from light_sources if it is there
356 NOTE: This doesn't happen nearly at all
358 /*if(light_sources.find(n2pos))
360 infostream<<"Removed from light_sources"<<std::endl;
361 light_sources.remove(n2pos);
366 if(light_sources.find(n2pos) != NULL)
367 light_sources.remove(n2pos);*/
370 light_sources.insert(n2pos, true);
373 // Add to modified_blocks
374 if(changed == true && block_checked_in_modified == false)
376 // If the block is not found in modified_blocks, add.
377 if(modified_blocks.find(blockpos) == NULL)
379 modified_blocks.insert(blockpos, block);
381 block_checked_in_modified = true;
384 catch(InvalidPositionException &e)
391 /*infostream<<"unspreadLight(): Changed block "
392 <<blockchangecount<<" times"
393 <<" for "<<from_nodes.size()<<" nodes"
396 if(unlighted_nodes.size() > 0)
397 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
401 A single-node wrapper of the above
403 void Map::unLightNeighbors(enum LightBank bank,
404 v3s16 pos, u8 lightwas,
405 core::map<v3s16, bool> & light_sources,
406 core::map<v3s16, MapBlock*> & modified_blocks)
408 core::map<v3s16, u8> from_nodes;
409 from_nodes.insert(pos, lightwas);
411 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
415 Lights neighbors of from_nodes, collects all them and then
418 void Map::spreadLight(enum LightBank bank,
419 core::map<v3s16, bool> & from_nodes,
420 core::map<v3s16, MapBlock*> & modified_blocks)
422 INodeDefManager *nodemgr = m_gamedef->ndef();
424 const v3s16 dirs[6] = {
425 v3s16(0,0,1), // back
427 v3s16(1,0,0), // right
428 v3s16(0,0,-1), // front
429 v3s16(0,-1,0), // bottom
430 v3s16(-1,0,0), // left
433 if(from_nodes.size() == 0)
436 u32 blockchangecount = 0;
438 core::map<v3s16, bool> lighted_nodes;
439 core::map<v3s16, bool>::Iterator j;
440 j = from_nodes.getIterator();
443 Initialize block cache
446 MapBlock *block = NULL;
447 // Cache this a bit, too
448 bool block_checked_in_modified = false;
450 for(; j.atEnd() == false; j++)
451 //for(; j != from_nodes.end(); j++)
453 v3s16 pos = j.getNode()->getKey();
455 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
456 v3s16 blockpos = getNodeBlockPos(pos);
458 // Only fetch a new block if the block position has changed
460 if(block == NULL || blockpos != blockpos_last){
461 block = getBlockNoCreate(blockpos);
462 blockpos_last = blockpos;
464 block_checked_in_modified = false;
468 catch(InvalidPositionException &e)
476 // Calculate relative position in block
477 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
479 // Get node straight from the block
480 MapNode n = block->getNode(relpos);
482 u8 oldlight = n.getLight(bank, nodemgr);
483 u8 newlight = diminish_light(oldlight);
485 // Loop through 6 neighbors
486 for(u16 i=0; i<6; i++){
487 // Get the position of the neighbor node
488 v3s16 n2pos = pos + dirs[i];
490 // Get the block where the node is located
491 v3s16 blockpos = getNodeBlockPos(n2pos);
495 // Only fetch a new block if the block position has changed
497 if(block == NULL || blockpos != blockpos_last){
498 block = getBlockNoCreate(blockpos);
499 blockpos_last = blockpos;
501 block_checked_in_modified = false;
505 catch(InvalidPositionException &e)
510 // Calculate relative position in block
511 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
512 // Get node straight from the block
513 MapNode n2 = block->getNode(relpos);
515 bool changed = false;
517 If the neighbor is brighter than the current node,
518 add to list (it will light up this node on its turn)
520 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
522 lighted_nodes.insert(n2pos, true);
523 //lighted_nodes.push_back(n2pos);
527 If the neighbor is dimmer than how much light this node
528 would spread on it, add to list
530 if(n2.getLight(bank, nodemgr) < newlight)
532 if(nodemgr->get(n2).light_propagates)
534 n2.setLight(bank, newlight, nodemgr);
535 block->setNode(relpos, n2);
536 lighted_nodes.insert(n2pos, true);
537 //lighted_nodes.push_back(n2pos);
542 // Add to modified_blocks
543 if(changed == true && block_checked_in_modified == false)
545 // If the block is not found in modified_blocks, add.
546 if(modified_blocks.find(blockpos) == NULL)
548 modified_blocks.insert(blockpos, block);
550 block_checked_in_modified = true;
553 catch(InvalidPositionException &e)
560 /*infostream<<"spreadLight(): Changed block "
561 <<blockchangecount<<" times"
562 <<" for "<<from_nodes.size()<<" nodes"
565 if(lighted_nodes.size() > 0)
566 spreadLight(bank, lighted_nodes, modified_blocks);
570 A single-node source variation of the above.
572 void Map::lightNeighbors(enum LightBank bank,
574 core::map<v3s16, MapBlock*> & modified_blocks)
576 core::map<v3s16, bool> from_nodes;
577 from_nodes.insert(pos, true);
578 spreadLight(bank, from_nodes, modified_blocks);
581 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
583 INodeDefManager *nodemgr = m_gamedef->ndef();
586 v3s16(0,0,1), // back
588 v3s16(1,0,0), // right
589 v3s16(0,0,-1), // front
590 v3s16(0,-1,0), // bottom
591 v3s16(-1,0,0), // left
594 u8 brightest_light = 0;
595 v3s16 brightest_pos(0,0,0);
596 bool found_something = false;
598 // Loop through 6 neighbors
599 for(u16 i=0; i<6; i++){
600 // Get the position of the neighbor node
601 v3s16 n2pos = p + dirs[i];
606 catch(InvalidPositionException &e)
610 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
611 brightest_light = n2.getLight(bank, nodemgr);
612 brightest_pos = n2pos;
613 found_something = true;
617 if(found_something == false)
618 throw InvalidPositionException();
620 return brightest_pos;
624 Propagates sunlight down from a node.
625 Starting point gets sunlight.
627 Returns the lowest y value of where the sunlight went.
629 Mud is turned into grass in where the sunlight stops.
631 s16 Map::propagateSunlight(v3s16 start,
632 core::map<v3s16, MapBlock*> & modified_blocks)
634 INodeDefManager *nodemgr = m_gamedef->ndef();
639 v3s16 pos(start.X, y, start.Z);
641 v3s16 blockpos = getNodeBlockPos(pos);
644 block = getBlockNoCreate(blockpos);
646 catch(InvalidPositionException &e)
651 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
652 MapNode n = block->getNode(relpos);
654 if(nodemgr->get(n).sunlight_propagates)
656 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
657 block->setNode(relpos, n);
659 modified_blocks.insert(blockpos, block);
663 // Sunlight goes no further
670 void Map::updateLighting(enum LightBank bank,
671 core::map<v3s16, MapBlock*> & a_blocks,
672 core::map<v3s16, MapBlock*> & modified_blocks)
674 INodeDefManager *nodemgr = m_gamedef->ndef();
676 /*m_dout<<DTIME<<"Map::updateLighting(): "
677 <<a_blocks.size()<<" blocks."<<std::endl;*/
679 //TimeTaker timer("updateLighting");
683 //u32 count_was = modified_blocks.size();
685 core::map<v3s16, MapBlock*> blocks_to_update;
687 core::map<v3s16, bool> light_sources;
689 core::map<v3s16, u8> unlight_from;
691 core::map<v3s16, MapBlock*>::Iterator i;
692 i = a_blocks.getIterator();
693 for(; i.atEnd() == false; i++)
695 MapBlock *block = i.getNode()->getValue();
699 // Don't bother with dummy blocks.
703 v3s16 pos = block->getPos();
704 modified_blocks.insert(pos, block);
706 blocks_to_update.insert(pos, block);
709 Clear all light from block
711 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
712 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
713 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
718 MapNode n = block->getNode(v3s16(x,y,z));
719 u8 oldlight = n.getLight(bank, nodemgr);
720 n.setLight(bank, 0, nodemgr);
721 block->setNode(v3s16(x,y,z), n);
723 // Collect borders for unlighting
724 if(x==0 || x == MAP_BLOCKSIZE-1
725 || y==0 || y == MAP_BLOCKSIZE-1
726 || z==0 || z == MAP_BLOCKSIZE-1)
728 v3s16 p_map = p + v3s16(
731 MAP_BLOCKSIZE*pos.Z);
732 unlight_from.insert(p_map, oldlight);
735 catch(InvalidPositionException &e)
738 This would happen when dealing with a
742 infostream<<"updateLighting(): InvalidPositionException"
747 if(bank == LIGHTBANK_DAY)
749 bool bottom_valid = block->propagateSunlight(light_sources);
751 // If bottom is valid, we're done.
755 else if(bank == LIGHTBANK_NIGHT)
757 // For night lighting, sunlight is not propagated
762 // Invalid lighting bank
766 /*infostream<<"Bottom for sunlight-propagated block ("
767 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
770 // Bottom sunlight is not valid; get the block and loop to it
774 block = getBlockNoCreate(pos);
776 catch(InvalidPositionException &e)
785 Enable this to disable proper lighting for speeding up map
786 generation for testing or whatever
789 //if(g_settings->get(""))
791 core::map<v3s16, MapBlock*>::Iterator i;
792 i = blocks_to_update.getIterator();
793 for(; i.atEnd() == false; i++)
795 MapBlock *block = i.getNode()->getValue();
796 v3s16 p = block->getPos();
797 block->setLightingExpired(false);
805 TimeTaker timer("unspreadLight");
806 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
811 u32 diff = modified_blocks.size() - count_was;
812 count_was = modified_blocks.size();
813 infostream<<"unspreadLight modified "<<diff<<std::endl;
817 TimeTaker timer("spreadLight");
818 spreadLight(bank, light_sources, modified_blocks);
823 u32 diff = modified_blocks.size() - count_was;
824 count_was = modified_blocks.size();
825 infostream<<"spreadLight modified "<<diff<<std::endl;
830 //MapVoxelManipulator vmanip(this);
832 // Make a manual voxel manipulator and load all the blocks
833 // that touch the requested blocks
834 ManualMapVoxelManipulator vmanip(this);
835 core::map<v3s16, MapBlock*>::Iterator i;
836 i = blocks_to_update.getIterator();
837 for(; i.atEnd() == false; i++)
839 MapBlock *block = i.getNode()->getValue();
840 v3s16 p = block->getPos();
842 // Add all surrounding blocks
843 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
846 Add all surrounding blocks that have up-to-date lighting
847 NOTE: This doesn't quite do the job (not everything
848 appropriate is lighted)
850 /*for(s16 z=-1; z<=1; z++)
851 for(s16 y=-1; y<=1; y++)
852 for(s16 x=-1; x<=1; x++)
855 MapBlock *block = getBlockNoCreateNoEx(p);
860 if(block->getLightingExpired())
862 vmanip.initialEmerge(p, p);
865 // Lighting of block will be updated completely
866 block->setLightingExpired(false);
870 //TimeTaker timer("unSpreadLight");
871 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
874 //TimeTaker timer("spreadLight");
875 vmanip.spreadLight(bank, light_sources, nodemgr);
878 //TimeTaker timer("blitBack");
879 vmanip.blitBack(modified_blocks);
881 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
885 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
888 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
889 core::map<v3s16, MapBlock*> & modified_blocks)
891 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
892 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
895 Update information about whether day and night light differ
897 for(core::map<v3s16, MapBlock*>::Iterator
898 i = modified_blocks.getIterator();
899 i.atEnd() == false; i++)
901 MapBlock *block = i.getNode()->getValue();
902 block->updateDayNightDiff();
908 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
909 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
911 INodeDefManager *nodemgr = m_gamedef->ndef();
914 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
915 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
918 From this node to nodes underneath:
919 If lighting is sunlight (1.0), unlight neighbours and
924 v3s16 toppos = p + v3s16(0,1,0);
925 v3s16 bottompos = p + v3s16(0,-1,0);
927 bool node_under_sunlight = true;
928 core::map<v3s16, bool> light_sources;
931 If there is a node at top and it doesn't have sunlight,
932 there has not been any sunlight going down.
934 Otherwise there probably is.
937 MapNode topnode = getNode(toppos);
939 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
940 node_under_sunlight = false;
942 catch(InvalidPositionException &e)
947 Remove all light that has come out of this node
950 enum LightBank banks[] =
955 for(s32 i=0; i<2; i++)
957 enum LightBank bank = banks[i];
959 u8 lightwas = getNode(p).getLight(bank, nodemgr);
961 // Add the block of the added node to modified_blocks
962 v3s16 blockpos = getNodeBlockPos(p);
963 MapBlock * block = getBlockNoCreate(blockpos);
964 assert(block != NULL);
965 modified_blocks.insert(blockpos, block);
967 assert(isValidPosition(p));
969 // Unlight neighbours of node.
970 // This means setting light of all consequent dimmer nodes
972 // This also collects the nodes at the border which will spread
973 // light again into this.
974 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
976 n.setLight(bank, 0, nodemgr);
980 If node lets sunlight through and is under sunlight, it has
983 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
985 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
989 Set the node on the map
998 NodeMetadata *meta_proto = nodemgr->get(n).initial_metadata;
1001 NodeMetadata *meta = meta_proto->clone(m_gamedef);
1002 meta->setOwner(player_name);
1003 setNodeMetadata(p, meta);
1007 If node is under sunlight and doesn't let sunlight through,
1008 take all sunlighted nodes under it and clear light from them
1009 and from where the light has been spread.
1010 TODO: This could be optimized by mass-unlighting instead
1013 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1017 //m_dout<<DTIME<<"y="<<y<<std::endl;
1018 v3s16 n2pos(p.X, y, p.Z);
1022 n2 = getNode(n2pos);
1024 catch(InvalidPositionException &e)
1029 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1031 unLightNeighbors(LIGHTBANK_DAY,
1032 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1033 light_sources, modified_blocks);
1034 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1042 for(s32 i=0; i<2; i++)
1044 enum LightBank bank = banks[i];
1047 Spread light from all nodes that might be capable of doing so
1049 spreadLight(bank, light_sources, modified_blocks);
1053 Update information about whether day and night light differ
1055 for(core::map<v3s16, MapBlock*>::Iterator
1056 i = modified_blocks.getIterator();
1057 i.atEnd() == false; i++)
1059 MapBlock *block = i.getNode()->getValue();
1060 block->updateDayNightDiff();
1064 Add neighboring liquid nodes and the node itself if it is
1065 liquid (=water node was added) to transform queue.
1068 v3s16(0,0,0), // self
1069 v3s16(0,0,1), // back
1070 v3s16(0,1,0), // top
1071 v3s16(1,0,0), // right
1072 v3s16(0,0,-1), // front
1073 v3s16(0,-1,0), // bottom
1074 v3s16(-1,0,0), // left
1076 for(u16 i=0; i<7; i++)
1081 v3s16 p2 = p + dirs[i];
1083 MapNode n2 = getNode(p2);
1084 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1086 m_transforming_liquid.push_back(p2);
1089 }catch(InvalidPositionException &e)
1097 void Map::removeNodeAndUpdate(v3s16 p,
1098 core::map<v3s16, MapBlock*> &modified_blocks)
1100 INodeDefManager *nodemgr = m_gamedef->ndef();
1102 /*PrintInfo(m_dout);
1103 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1104 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1106 bool node_under_sunlight = true;
1108 v3s16 toppos = p + v3s16(0,1,0);
1110 // Node will be replaced with this
1111 content_t replace_material = CONTENT_AIR;
1114 If there is a node at top and it doesn't have sunlight,
1115 there will be no sunlight going down.
1118 MapNode topnode = getNode(toppos);
1120 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1121 node_under_sunlight = false;
1123 catch(InvalidPositionException &e)
1127 core::map<v3s16, bool> light_sources;
1129 enum LightBank banks[] =
1134 for(s32 i=0; i<2; i++)
1136 enum LightBank bank = banks[i];
1139 Unlight neighbors (in case the node is a light source)
1141 unLightNeighbors(bank, p,
1142 getNode(p).getLight(bank, nodemgr),
1143 light_sources, modified_blocks);
1147 Remove node metadata
1150 removeNodeMetadata(p);
1154 This also clears the lighting.
1158 n.setContent(replace_material);
1161 for(s32 i=0; i<2; i++)
1163 enum LightBank bank = banks[i];
1166 Recalculate lighting
1168 spreadLight(bank, light_sources, modified_blocks);
1171 // Add the block of the removed node to modified_blocks
1172 v3s16 blockpos = getNodeBlockPos(p);
1173 MapBlock * block = getBlockNoCreate(blockpos);
1174 assert(block != NULL);
1175 modified_blocks.insert(blockpos, block);
1178 If the removed node was under sunlight, propagate the
1179 sunlight down from it and then light all neighbors
1180 of the propagated blocks.
1182 if(node_under_sunlight)
1184 s16 ybottom = propagateSunlight(p, modified_blocks);
1185 /*m_dout<<DTIME<<"Node was under sunlight. "
1186 "Propagating sunlight";
1187 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1189 for(; y >= ybottom; y--)
1191 v3s16 p2(p.X, y, p.Z);
1192 /*m_dout<<DTIME<<"lighting neighbors of node ("
1193 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1195 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1200 // Set the lighting of this node to 0
1201 // TODO: Is this needed? Lighting is cleared up there already.
1203 MapNode n = getNode(p);
1204 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1207 catch(InvalidPositionException &e)
1213 for(s32 i=0; i<2; i++)
1215 enum LightBank bank = banks[i];
1217 // Get the brightest neighbour node and propagate light from it
1218 v3s16 n2p = getBrightestNeighbour(bank, p);
1220 MapNode n2 = getNode(n2p);
1221 lightNeighbors(bank, n2p, modified_blocks);
1223 catch(InvalidPositionException &e)
1229 Update information about whether day and night light differ
1231 for(core::map<v3s16, MapBlock*>::Iterator
1232 i = modified_blocks.getIterator();
1233 i.atEnd() == false; i++)
1235 MapBlock *block = i.getNode()->getValue();
1236 block->updateDayNightDiff();
1240 Add neighboring liquid nodes and this node to transform queue.
1241 (it's vital for the node itself to get updated last.)
1244 v3s16(0,0,1), // back
1245 v3s16(0,1,0), // top
1246 v3s16(1,0,0), // right
1247 v3s16(0,0,-1), // front
1248 v3s16(0,-1,0), // bottom
1249 v3s16(-1,0,0), // left
1250 v3s16(0,0,0), // self
1252 for(u16 i=0; i<7; i++)
1257 v3s16 p2 = p + dirs[i];
1259 MapNode n2 = getNode(p2);
1260 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1262 m_transforming_liquid.push_back(p2);
1265 }catch(InvalidPositionException &e)
1271 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1274 event.type = MEET_ADDNODE;
1278 bool succeeded = true;
1280 core::map<v3s16, MapBlock*> modified_blocks;
1281 std::string st = std::string("");
1282 addNodeAndUpdate(p, n, modified_blocks, st);
1284 // Copy modified_blocks to event
1285 for(core::map<v3s16, MapBlock*>::Iterator
1286 i = modified_blocks.getIterator();
1287 i.atEnd()==false; i++)
1289 event.modified_blocks.insert(i.getNode()->getKey(), false);
1292 catch(InvalidPositionException &e){
1296 dispatchEvent(&event);
1301 bool Map::removeNodeWithEvent(v3s16 p)
1304 event.type = MEET_REMOVENODE;
1307 bool succeeded = true;
1309 core::map<v3s16, MapBlock*> modified_blocks;
1310 removeNodeAndUpdate(p, modified_blocks);
1312 // Copy modified_blocks to event
1313 for(core::map<v3s16, MapBlock*>::Iterator
1314 i = modified_blocks.getIterator();
1315 i.atEnd()==false; i++)
1317 event.modified_blocks.insert(i.getNode()->getKey(), false);
1320 catch(InvalidPositionException &e){
1324 dispatchEvent(&event);
1329 bool Map::dayNightDiffed(v3s16 blockpos)
1332 v3s16 p = blockpos + v3s16(0,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1340 v3s16 p = blockpos + v3s16(-1,0,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1347 v3s16 p = blockpos + v3s16(0,-1,0);
1348 MapBlock *b = getBlockNoCreate(p);
1349 if(b->dayNightDiffed())
1352 catch(InvalidPositionException &e){}
1354 v3s16 p = blockpos + v3s16(0,0,-1);
1355 MapBlock *b = getBlockNoCreate(p);
1356 if(b->dayNightDiffed())
1359 catch(InvalidPositionException &e){}
1362 v3s16 p = blockpos + v3s16(1,0,0);
1363 MapBlock *b = getBlockNoCreate(p);
1364 if(b->dayNightDiffed())
1367 catch(InvalidPositionException &e){}
1369 v3s16 p = blockpos + v3s16(0,1,0);
1370 MapBlock *b = getBlockNoCreate(p);
1371 if(b->dayNightDiffed())
1374 catch(InvalidPositionException &e){}
1376 v3s16 p = blockpos + v3s16(0,0,1);
1377 MapBlock *b = getBlockNoCreate(p);
1378 if(b->dayNightDiffed())
1381 catch(InvalidPositionException &e){}
1387 Updates usage timers
1389 void Map::timerUpdate(float dtime, float unload_timeout,
1390 core::list<v3s16> *unloaded_blocks)
1392 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1394 // Profile modified reasons
1395 Profiler modprofiler;
1397 core::list<v2s16> sector_deletion_queue;
1398 u32 deleted_blocks_count = 0;
1399 u32 saved_blocks_count = 0;
1401 core::map<v2s16, MapSector*>::Iterator si;
1404 si = m_sectors.getIterator();
1405 for(; si.atEnd() == false; si++)
1407 MapSector *sector = si.getNode()->getValue();
1409 bool all_blocks_deleted = true;
1411 core::list<MapBlock*> blocks;
1412 sector->getBlocks(blocks);
1414 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1415 i != blocks.end(); i++)
1417 MapBlock *block = (*i);
1419 block->incrementUsageTimer(dtime);
1421 if(block->getUsageTimer() > unload_timeout)
1423 v3s16 p = block->getPos();
1426 if(block->getModified() != MOD_STATE_CLEAN
1427 && save_before_unloading)
1429 modprofiler.add(block->getModifiedReason(), 1);
1431 saved_blocks_count++;
1434 // Delete from memory
1435 sector->deleteBlock(block);
1438 unloaded_blocks->push_back(p);
1440 deleted_blocks_count++;
1444 all_blocks_deleted = false;
1448 if(all_blocks_deleted)
1450 sector_deletion_queue.push_back(si.getNode()->getKey());
1455 // Finally delete the empty sectors
1456 deleteSectors(sector_deletion_queue);
1458 if(deleted_blocks_count != 0)
1460 PrintInfo(infostream); // ServerMap/ClientMap:
1461 infostream<<"Unloaded "<<deleted_blocks_count
1462 <<" blocks from memory";
1463 if(save_before_unloading)
1464 infostream<<", of which "<<saved_blocks_count<<" were written";
1465 infostream<<"."<<std::endl;
1466 if(saved_blocks_count != 0){
1467 PrintInfo(infostream); // ServerMap/ClientMap:
1468 infostream<<"Blocks modified by: "<<std::endl;
1469 modprofiler.print(infostream);
1474 void Map::deleteSectors(core::list<v2s16> &list)
1476 core::list<v2s16>::Iterator j;
1477 for(j=list.begin(); j!=list.end(); j++)
1479 MapSector *sector = m_sectors[*j];
1480 // If sector is in sector cache, remove it from there
1481 if(m_sector_cache == sector)
1482 m_sector_cache = NULL;
1483 // Remove from map and delete
1484 m_sectors.remove(*j);
1490 void Map::unloadUnusedData(float timeout,
1491 core::list<v3s16> *deleted_blocks)
1493 core::list<v2s16> sector_deletion_queue;
1494 u32 deleted_blocks_count = 0;
1495 u32 saved_blocks_count = 0;
1497 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1498 for(; si.atEnd() == false; si++)
1500 MapSector *sector = si.getNode()->getValue();
1502 bool all_blocks_deleted = true;
1504 core::list<MapBlock*> blocks;
1505 sector->getBlocks(blocks);
1506 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1507 i != blocks.end(); i++)
1509 MapBlock *block = (*i);
1511 if(block->getUsageTimer() > timeout)
1514 if(block->getModified() != MOD_STATE_CLEAN)
1517 saved_blocks_count++;
1519 // Delete from memory
1520 sector->deleteBlock(block);
1521 deleted_blocks_count++;
1525 all_blocks_deleted = false;
1529 if(all_blocks_deleted)
1531 sector_deletion_queue.push_back(si.getNode()->getKey());
1535 deleteSectors(sector_deletion_queue);
1537 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1538 <<", of which "<<saved_blocks_count<<" were wr."
1541 //return sector_deletion_queue.getSize();
1542 //return deleted_blocks_count;
1546 void Map::PrintInfo(std::ostream &out)
1551 #define WATER_DROP_BOOST 4
1555 NEIGHBOR_SAME_LEVEL,
1558 struct NodeNeighbor {
1564 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1566 INodeDefManager *nodemgr = m_gamedef->ndef();
1568 DSTACK(__FUNCTION_NAME);
1569 //TimeTaker timer("transformLiquids()");
1572 u32 initial_size = m_transforming_liquid.size();
1574 /*if(initial_size != 0)
1575 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1577 // list of nodes that due to viscosity have not reached their max level height
1578 UniqueQueue<v3s16> must_reflow;
1580 // List of MapBlocks that will require a lighting update (due to lava)
1581 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1583 while(m_transforming_liquid.size() != 0)
1585 // This should be done here so that it is done when continue is used
1586 if(loopcount >= initial_size * 3)
1591 Get a queued transforming liquid node
1593 v3s16 p0 = m_transforming_liquid.pop_front();
1595 MapNode n0 = getNodeNoEx(p0);
1598 Collect information about current node
1600 s8 liquid_level = -1;
1601 u8 liquid_kind = CONTENT_IGNORE;
1602 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1603 switch (liquid_type) {
1605 liquid_level = LIQUID_LEVEL_SOURCE;
1606 liquid_kind = nodemgr->get(n0).liquid_alternative_flowing;
1608 case LIQUID_FLOWING:
1609 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1610 liquid_kind = n0.getContent();
1613 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1614 // continue with the next node.
1615 if (n0.getContent() != CONTENT_AIR)
1617 liquid_kind = CONTENT_AIR;
1622 Collect information about the environment
1624 const v3s16 *dirs = g_6dirs;
1625 NodeNeighbor sources[6]; // surrounding sources
1626 int num_sources = 0;
1627 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1629 NodeNeighbor airs[6]; // surrounding air
1631 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1632 int num_neutrals = 0;
1633 bool flowing_down = false;
1634 for (u16 i = 0; i < 6; i++) {
1635 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1638 nt = NEIGHBOR_UPPER;
1641 nt = NEIGHBOR_LOWER;
1644 v3s16 npos = p0 + dirs[i];
1645 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1646 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1648 if (nb.n.getContent() == CONTENT_AIR) {
1649 airs[num_airs++] = nb;
1650 // if the current node is a water source the neighbor
1651 // should be enqueded for transformation regardless of whether the
1652 // current node changes or not.
1653 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1654 m_transforming_liquid.push_back(npos);
1655 // if the current node happens to be a flowing node, it will start to flow down here.
1656 if (nb.t == NEIGHBOR_LOWER) {
1657 flowing_down = true;
1660 neutrals[num_neutrals++] = nb;
1664 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1665 if (liquid_kind == CONTENT_AIR)
1666 liquid_kind = nodemgr->get(nb.n.getContent()).liquid_alternative_flowing;
1667 if (nodemgr->get(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1668 neutrals[num_neutrals++] = nb;
1670 sources[num_sources++] = nb;
1673 case LIQUID_FLOWING:
1674 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1675 if (liquid_kind == CONTENT_AIR)
1676 liquid_kind = nodemgr->get(nb.n.getContent()).liquid_alternative_flowing;
1677 if (nodemgr->get(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1678 neutrals[num_neutrals++] = nb;
1680 flows[num_flows++] = nb;
1681 if (nb.t == NEIGHBOR_LOWER)
1682 flowing_down = true;
1689 decide on the type (and possibly level) of the current node
1691 content_t new_node_content;
1692 s8 new_node_level = -1;
1693 s8 max_node_level = -1;
1694 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1695 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1696 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1697 // it's perfectly safe to use liquid_kind here to determine the new node content.
1698 new_node_content = nodemgr->get(liquid_kind).liquid_alternative_source;
1699 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1700 // liquid_kind is set properly, see above
1701 new_node_content = liquid_kind;
1702 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1704 // no surrounding sources, so get the maximum level that can flow into this node
1705 for (u16 i = 0; i < num_flows; i++) {
1706 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1707 switch (flows[i].t) {
1708 case NEIGHBOR_UPPER:
1709 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1710 max_node_level = LIQUID_LEVEL_MAX;
1711 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1712 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1713 } else if (nb_liquid_level > max_node_level)
1714 max_node_level = nb_liquid_level;
1716 case NEIGHBOR_LOWER:
1718 case NEIGHBOR_SAME_LEVEL:
1719 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1720 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1721 max_node_level = nb_liquid_level - 1;
1727 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1728 if (viscosity > 1 && max_node_level != liquid_level) {
1729 // amount to gain, limited by viscosity
1730 // must be at least 1 in absolute value
1731 s8 level_inc = max_node_level - liquid_level;
1732 if (level_inc < -viscosity || level_inc > viscosity)
1733 new_node_level = liquid_level + level_inc/viscosity;
1734 else if (level_inc < 0)
1735 new_node_level = liquid_level - 1;
1736 else if (level_inc > 0)
1737 new_node_level = liquid_level + 1;
1738 if (new_node_level != max_node_level)
1739 must_reflow.push_back(p0);
1741 new_node_level = max_node_level;
1743 if (new_node_level >= 0)
1744 new_node_content = liquid_kind;
1746 new_node_content = CONTENT_AIR;
1751 check if anything has changed. if not, just continue with the next node.
1753 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1754 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1755 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1761 update the current node
1763 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1764 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1765 // set level to last 3 bits, flowing down bit to 4th bit
1766 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1768 // set the liquid level and flow bit to 0
1769 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1771 n0.setContent(new_node_content);
1773 v3s16 blockpos = getNodeBlockPos(p0);
1774 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1776 modified_blocks.insert(blockpos, block);
1777 // If node emits light, MapBlock requires lighting update
1778 if(nodemgr->get(n0).light_source != 0)
1779 lighting_modified_blocks[block->getPos()] = block;
1783 enqueue neighbors for update if neccessary
1785 switch (nodemgr->get(n0.getContent()).liquid_type) {
1787 case LIQUID_FLOWING:
1788 // make sure source flows into all neighboring nodes
1789 for (u16 i = 0; i < num_flows; i++)
1790 if (flows[i].t != NEIGHBOR_UPPER)
1791 m_transforming_liquid.push_back(flows[i].p);
1792 for (u16 i = 0; i < num_airs; i++)
1793 if (airs[i].t != NEIGHBOR_UPPER)
1794 m_transforming_liquid.push_back(airs[i].p);
1797 // this flow has turned to air; neighboring flows might need to do the same
1798 for (u16 i = 0; i < num_flows; i++)
1799 m_transforming_liquid.push_back(flows[i].p);
1803 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1804 while (must_reflow.size() > 0)
1805 m_transforming_liquid.push_back(must_reflow.pop_front());
1806 updateLighting(lighting_modified_blocks, modified_blocks);
1809 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1811 v3s16 blockpos = getNodeBlockPos(p);
1812 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1813 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1815 infostream<<"Map::getNodeMetadata(): Need to emerge "
1816 <<PP(blockpos)<<std::endl;
1817 block = emergeBlock(blockpos, false);
1821 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1825 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1829 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1831 v3s16 blockpos = getNodeBlockPos(p);
1832 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1833 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1835 infostream<<"Map::setNodeMetadata(): Need to emerge "
1836 <<PP(blockpos)<<std::endl;
1837 block = emergeBlock(blockpos, false);
1841 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1845 block->m_node_metadata->set(p_rel, meta);
1848 void Map::removeNodeMetadata(v3s16 p)
1850 v3s16 blockpos = getNodeBlockPos(p);
1851 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1852 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1855 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1859 block->m_node_metadata->remove(p_rel);
1862 void Map::nodeMetadataStep(float dtime,
1863 core::map<v3s16, MapBlock*> &changed_blocks)
1867 Currently there is no way to ensure that all the necessary
1868 blocks are loaded when this is run. (They might get unloaded)
1869 NOTE: ^- Actually, that might not be so. In a quick test it
1870 reloaded a block with a furnace when I walked back to it from
1873 core::map<v2s16, MapSector*>::Iterator si;
1874 si = m_sectors.getIterator();
1875 for(; si.atEnd() == false; si++)
1877 MapSector *sector = si.getNode()->getValue();
1878 core::list< MapBlock * > sectorblocks;
1879 sector->getBlocks(sectorblocks);
1880 core::list< MapBlock * >::Iterator i;
1881 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1883 MapBlock *block = *i;
1884 bool changed = block->m_node_metadata->step(dtime);
1886 changed_blocks[block->getPos()] = block;
1895 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1896 Map(dout_server, gamedef),
1898 m_map_metadata_changed(true),
1900 m_database_read(NULL),
1901 m_database_write(NULL)
1903 infostream<<__FUNCTION_NAME<<std::endl;
1905 //m_chunksize = 8; // Takes a few seconds
1907 if (g_settings->get("fixed_map_seed").empty())
1909 m_seed = (((u64)(myrand()%0xffff)<<0)
1910 + ((u64)(myrand()%0xffff)<<16)
1911 + ((u64)(myrand()%0xffff)<<32)
1912 + ((u64)(myrand()%0xffff)<<48));
1916 m_seed = g_settings->getU64("fixed_map_seed");
1920 Experimental and debug stuff
1927 Try to load map; if not found, create a new one.
1930 m_savedir = savedir;
1931 m_map_saving_enabled = false;
1935 // If directory exists, check contents and load if possible
1936 if(fs::PathExists(m_savedir))
1938 // If directory is empty, it is safe to save into it.
1939 if(fs::GetDirListing(m_savedir).size() == 0)
1941 infostream<<"Server: Empty save directory is valid."
1943 m_map_saving_enabled = true;
1948 // Load map metadata (seed, chunksize)
1951 catch(FileNotGoodException &e){
1952 infostream<<"WARNING: Could not load map metadata"
1953 //<<" Disabling chunk-based generator."
1959 // Load chunk metadata
1962 catch(FileNotGoodException &e){
1963 infostream<<"WARNING: Could not load chunk metadata."
1964 <<" Disabling chunk-based generator."
1969 /*infostream<<"Server: Successfully loaded chunk "
1970 "metadata and sector (0,0) from "<<savedir<<
1971 ", assuming valid save directory."
1974 infostream<<"Server: Successfully loaded map "
1975 <<"and chunk metadata from "<<savedir
1976 <<", assuming valid save directory."
1979 m_map_saving_enabled = true;
1980 // Map loaded, not creating new one
1984 // If directory doesn't exist, it is safe to save to it
1986 m_map_saving_enabled = true;
1989 catch(std::exception &e)
1991 infostream<<"WARNING: Server: Failed to load map from "<<savedir
1992 <<", exception: "<<e.what()<<std::endl;
1993 infostream<<"Please remove the map or fix it."<<std::endl;
1994 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1997 infostream<<"Initializing new map."<<std::endl;
1999 // Create zero sector
2000 emergeSector(v2s16(0,0));
2002 // Initially write whole map
2006 ServerMap::~ServerMap()
2008 infostream<<__FUNCTION_NAME<<std::endl;
2012 if(m_map_saving_enabled)
2014 // Save only changed parts
2016 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2020 infostream<<"Server: map not saved"<<std::endl;
2023 catch(std::exception &e)
2025 infostream<<"Server: Failed to save map to "<<m_savedir
2026 <<", exception: "<<e.what()<<std::endl;
2030 Close database if it was opened
2033 sqlite3_finalize(m_database_read);
2034 if(m_database_write)
2035 sqlite3_finalize(m_database_write);
2037 sqlite3_close(m_database);
2043 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2044 for(; i.atEnd() == false; i++)
2046 MapChunk *chunk = i.getNode()->getValue();
2052 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2054 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2055 if(enable_mapgen_debug_info)
2056 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2057 <<blockpos.Z<<")"<<std::endl;
2059 // Do nothing if not inside limits (+-1 because of neighbors)
2060 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2061 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2067 data->no_op = false;
2068 data->seed = m_seed;
2069 data->blockpos = blockpos;
2070 data->nodedef = m_gamedef->ndef();
2073 Create the whole area of this and the neighboring blocks
2076 //TimeTaker timer("initBlockMake() create area");
2078 for(s16 x=-1; x<=1; x++)
2079 for(s16 z=-1; z<=1; z++)
2081 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2082 // Sector metadata is loaded from disk if not already loaded.
2083 ServerMapSector *sector = createSector(sectorpos);
2086 for(s16 y=-1; y<=1; y++)
2088 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2089 //MapBlock *block = createBlock(p);
2090 // 1) get from memory, 2) load from disk
2091 MapBlock *block = emergeBlock(p, false);
2092 // 3) create a blank one
2095 block = createBlock(p);
2098 Block gets sunlight if this is true.
2100 Refer to the map generator heuristics.
2102 bool ug = mapgen::block_is_underground(data->seed, p);
2103 block->setIsUnderground(ug);
2106 // Lighting will not be valid after make_chunk is called
2107 block->setLightingExpired(true);
2108 // Lighting will be calculated
2109 //block->setLightingExpired(false);
2115 Now we have a big empty area.
2117 Make a ManualMapVoxelManipulator that contains this and the
2121 // The area that contains this block and it's neighbors
2122 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2123 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2125 data->vmanip = new ManualMapVoxelManipulator(this);
2126 //data->vmanip->setMap(this);
2130 //TimeTaker timer("initBlockMake() initialEmerge");
2131 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2134 // Data is ready now.
2137 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2138 core::map<v3s16, MapBlock*> &changed_blocks)
2140 v3s16 blockpos = data->blockpos;
2141 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2142 <<blockpos.Z<<")"<<std::endl;*/
2146 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2150 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2152 /*infostream<<"Resulting vmanip:"<<std::endl;
2153 data->vmanip.print(infostream);*/
2156 Blit generated stuff to map
2157 NOTE: blitBackAll adds nearly everything to changed_blocks
2161 //TimeTaker timer("finishBlockMake() blitBackAll");
2162 data->vmanip->blitBackAll(&changed_blocks);
2165 if(enable_mapgen_debug_info)
2166 infostream<<"finishBlockMake: changed_blocks.size()="
2167 <<changed_blocks.size()<<std::endl;
2170 Copy transforming liquid information
2172 while(data->transforming_liquid.size() > 0)
2174 v3s16 p = data->transforming_liquid.pop_front();
2175 m_transforming_liquid.push_back(p);
2181 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2185 Set is_underground flag for lighting with sunlight.
2187 Refer to map generator heuristics.
2189 NOTE: This is done in initChunkMake
2191 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2195 Add sunlight to central block.
2196 This makes in-dark-spawning monsters to not flood the whole thing.
2197 Do not spread the light, though.
2199 /*core::map<v3s16, bool> light_sources;
2200 bool black_air_left = false;
2201 block->propagateSunlight(light_sources, true, &black_air_left);*/
2204 NOTE: Lighting and object adding shouldn't really be here, but
2205 lighting is a bit tricky to move properly to makeBlock.
2206 TODO: Do this the right way anyway, that is, move it to makeBlock.
2207 - There needs to be some way for makeBlock to report back if
2208 the lighting update is going further down because of the
2209 new block blocking light
2214 NOTE: This takes ~60ms, TODO: Investigate why
2217 TimeTaker t("finishBlockMake lighting update");
2219 core::map<v3s16, MapBlock*> lighting_update_blocks;
2222 lighting_update_blocks.insert(block->getPos(), block);
2227 v3s16 p = block->getPos()+v3s16(x,1,z);
2228 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2232 // All modified blocks
2233 // NOTE: Should this be done? If this is not done, then the lighting
2234 // of the others will be updated in a different place, one by one, i
2235 // think... or they might not? Well, at least they are left marked as
2236 // "lighting expired"; it seems that is not handled at all anywhere,
2237 // so enabling this will slow it down A LOT because otherwise it
2238 // would not do this at all. This causes the black trees.
2239 for(core::map<v3s16, MapBlock*>::Iterator
2240 i = changed_blocks.getIterator();
2241 i.atEnd() == false; i++)
2243 lighting_update_blocks.insert(i.getNode()->getKey(),
2244 i.getNode()->getValue());
2246 /*// Also force-add all the upmost blocks for proper sunlight
2247 for(s16 x=-1; x<=1; x++)
2248 for(s16 z=-1; z<=1; z++)
2250 v3s16 p = block->getPos()+v3s16(x,1,z);
2251 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2254 updateLighting(lighting_update_blocks, changed_blocks);
2257 Set lighting to non-expired state in all of them.
2258 This is cheating, but it is not fast enough if all of them
2259 would actually be updated.
2261 for(s16 x=-1; x<=1; x++)
2262 for(s16 y=-1; y<=1; y++)
2263 for(s16 z=-1; z<=1; z++)
2265 v3s16 p = block->getPos()+v3s16(x,y,z);
2266 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2269 if(enable_mapgen_debug_info == false)
2270 t.stop(true); // Hide output
2274 Add random objects to block
2276 mapgen::add_random_objects(block);
2279 Go through changed blocks
2281 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2282 i.atEnd() == false; i++)
2284 MapBlock *block = i.getNode()->getValue();
2287 Update day/night difference cache of the MapBlocks
2289 block->updateDayNightDiff();
2291 Set block as modified
2293 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2294 "finishBlockMake updateDayNightDiff");
2298 Set central block as generated
2300 block->setGenerated(true);
2303 Save changed parts of map
2304 NOTE: Will be saved later.
2308 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2309 <<blockpos.Z<<")"<<std::endl;*/
2311 if(enable_mapgen_debug_info)
2314 Analyze resulting blocks
2316 for(s16 x=-1; x<=1; x++)
2317 for(s16 y=-1; y<=1; y++)
2318 for(s16 z=-1; z<=1; z++)
2320 v3s16 p = block->getPos()+v3s16(x,y,z);
2321 MapBlock *block = getBlockNoCreateNoEx(p);
2323 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2324 infostream<<"Generated "<<spos<<": "
2325 <<analyze_block(block)<<std::endl;
2333 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2335 DSTACKF("%s: p2d=(%d,%d)",
2340 Check if it exists already in memory
2342 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2347 Try to load it from disk (with blocks)
2349 //if(loadSectorFull(p2d) == true)
2352 Try to load metadata from disk
2355 if(loadSectorMeta(p2d) == true)
2357 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2360 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2361 throw InvalidPositionException("");
2367 Do not create over-limit
2369 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2370 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2371 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2372 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2373 throw InvalidPositionException("createSector(): pos. over limit");
2376 Generate blank sector
2379 sector = new ServerMapSector(this, p2d, m_gamedef);
2381 // Sector position on map in nodes
2382 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2387 m_sectors.insert(p2d, sector);
2393 This is a quick-hand function for calling makeBlock().
2395 MapBlock * ServerMap::generateBlock(
2397 core::map<v3s16, MapBlock*> &modified_blocks
2400 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2402 /*infostream<<"generateBlock(): "
2403 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2406 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2408 TimeTaker timer("generateBlock");
2410 //MapBlock *block = original_dummy;
2412 v2s16 p2d(p.X, p.Z);
2413 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2416 Do not generate over-limit
2418 if(blockpos_over_limit(p))
2420 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2421 throw InvalidPositionException("generateBlock(): pos. over limit");
2425 Create block make data
2427 mapgen::BlockMakeData data;
2428 initBlockMake(&data, p);
2434 TimeTaker t("mapgen::make_block()");
2435 mapgen::make_block(&data);
2437 if(enable_mapgen_debug_info == false)
2438 t.stop(true); // Hide output
2442 Blit data back on map, update lighting, add mobs and whatever this does
2444 finishBlockMake(&data, modified_blocks);
2449 MapBlock *block = getBlockNoCreateNoEx(p);
2457 bool erroneus_content = false;
2458 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2459 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2460 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2463 MapNode n = block->getNode(p);
2464 if(n.getContent() == CONTENT_IGNORE)
2466 infostream<<"CONTENT_IGNORE at "
2467 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2469 erroneus_content = true;
2473 if(erroneus_content)
2482 Generate a completely empty block
2486 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2487 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2489 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2492 n.setContent(CONTENT_AIR);
2493 block->setNode(v3s16(x0,y0,z0), n);
2499 if(enable_mapgen_debug_info == false)
2500 timer.stop(true); // Hide output
2505 MapBlock * ServerMap::createBlock(v3s16 p)
2507 DSTACKF("%s: p=(%d,%d,%d)",
2508 __FUNCTION_NAME, p.X, p.Y, p.Z);
2511 Do not create over-limit
2513 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2514 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2515 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2516 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2517 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2518 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2519 throw InvalidPositionException("createBlock(): pos. over limit");
2521 v2s16 p2d(p.X, p.Z);
2524 This will create or load a sector if not found in memory.
2525 If block exists on disk, it will be loaded.
2527 NOTE: On old save formats, this will be slow, as it generates
2528 lighting on blocks for them.
2530 ServerMapSector *sector;
2532 sector = (ServerMapSector*)createSector(p2d);
2533 assert(sector->getId() == MAPSECTOR_SERVER);
2535 catch(InvalidPositionException &e)
2537 infostream<<"createBlock: createSector() failed"<<std::endl;
2541 NOTE: This should not be done, or at least the exception
2542 should not be passed on as std::exception, because it
2543 won't be catched at all.
2545 /*catch(std::exception &e)
2547 infostream<<"createBlock: createSector() failed: "
2548 <<e.what()<<std::endl;
2553 Try to get a block from the sector
2556 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2559 if(block->isDummy())
2564 block = sector->createBlankBlock(block_y);
2568 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2570 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2572 p.X, p.Y, p.Z, allow_generate);
2575 MapBlock *block = getBlockNoCreateNoEx(p);
2576 if(block && block->isDummy() == false)
2581 MapBlock *block = loadBlock(p);
2588 core::map<v3s16, MapBlock*> modified_blocks;
2589 MapBlock *block = generateBlock(p, modified_blocks);
2593 event.type = MEET_OTHER;
2596 // Copy modified_blocks to event
2597 for(core::map<v3s16, MapBlock*>::Iterator
2598 i = modified_blocks.getIterator();
2599 i.atEnd()==false; i++)
2601 event.modified_blocks.insert(i.getNode()->getKey(), false);
2605 dispatchEvent(&event);
2614 s16 ServerMap::findGroundLevel(v2s16 p2d)
2618 Uh, just do something random...
2620 // Find existing map from top to down
2623 v3s16 p(p2d.X, max, p2d.Y);
2624 for(; p.Y>min; p.Y--)
2626 MapNode n = getNodeNoEx(p);
2627 if(n.getContent() != CONTENT_IGNORE)
2632 // If this node is not air, go to plan b
2633 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2635 // Search existing walkable and return it
2636 for(; p.Y>min; p.Y--)
2638 MapNode n = getNodeNoEx(p);
2639 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2648 Determine from map generator noise functions
2651 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2654 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2655 //return (s16)level;
2658 void ServerMap::createDatabase() {
2661 e = sqlite3_exec(m_database,
2662 "CREATE TABLE IF NOT EXISTS `blocks` ("
2663 "`pos` INT NOT NULL PRIMARY KEY,"
2666 , NULL, NULL, NULL);
2667 if(e == SQLITE_ABORT)
2668 throw FileNotGoodException("Could not create database structure");
2670 infostream<<"Server: Database structure was created";
2673 void ServerMap::verifyDatabase() {
2678 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2679 bool needs_create = false;
2683 Open the database connection
2686 createDirs(m_savedir);
2688 if(!fs::PathExists(dbp))
2689 needs_create = true;
2691 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2692 if(d != SQLITE_OK) {
2693 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2694 throw FileNotGoodException("Cannot open database file");
2700 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2701 if(d != SQLITE_OK) {
2702 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2703 throw FileNotGoodException("Cannot prepare read statement");
2706 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2707 if(d != SQLITE_OK) {
2708 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2709 throw FileNotGoodException("Cannot prepare write statement");
2712 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2713 if(d != SQLITE_OK) {
2714 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2715 throw FileNotGoodException("Cannot prepare read statement");
2718 infostream<<"Server: Database opened"<<std::endl;
2722 bool ServerMap::loadFromFolders() {
2723 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2728 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2729 return (sqlite3_int64)pos.Z*16777216 +
2730 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2733 void ServerMap::createDirs(std::string path)
2735 if(fs::CreateAllDirs(path) == false)
2737 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2738 <<"\""<<path<<"\""<<std::endl;
2739 throw BaseException("ServerMap failed to create directory");
2743 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2749 snprintf(cc, 9, "%.4x%.4x",
2750 (unsigned int)pos.X&0xffff,
2751 (unsigned int)pos.Y&0xffff);
2753 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2755 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2756 (unsigned int)pos.X&0xfff,
2757 (unsigned int)pos.Y&0xfff);
2759 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2765 v2s16 ServerMap::getSectorPos(std::string dirname)
2769 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2770 assert(spos != std::string::npos);
2771 if(dirname.size() - spos == 8)
2774 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2776 else if(dirname.size() - spos == 3)
2779 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2780 // Sign-extend the 12 bit values up to 16 bits...
2781 if(x&0x800) x|=0xF000;
2782 if(y&0x800) y|=0xF000;
2789 v2s16 pos((s16)x, (s16)y);
2793 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2795 v2s16 p2d = getSectorPos(sectordir);
2797 if(blockfile.size() != 4){
2798 throw InvalidFilenameException("Invalid block filename");
2801 int r = sscanf(blockfile.c_str(), "%4x", &y);
2803 throw InvalidFilenameException("Invalid block filename");
2804 return v3s16(p2d.X, y, p2d.Y);
2807 std::string ServerMap::getBlockFilename(v3s16 p)
2810 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2814 void ServerMap::save(bool only_changed)
2816 DSTACK(__FUNCTION_NAME);
2817 if(m_map_saving_enabled == false)
2819 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2823 if(only_changed == false)
2824 infostream<<"ServerMap: Saving whole map, this can take time."
2827 if(only_changed == false || m_map_metadata_changed)
2832 // Profile modified reasons
2833 Profiler modprofiler;
2835 u32 sector_meta_count = 0;
2836 u32 block_count = 0;
2837 u32 block_count_all = 0; // Number of blocks in memory
2840 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2841 for(; i.atEnd() == false; i++)
2843 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2844 assert(sector->getId() == MAPSECTOR_SERVER);
2846 if(sector->differs_from_disk || only_changed == false)
2848 saveSectorMeta(sector);
2849 sector_meta_count++;
2851 core::list<MapBlock*> blocks;
2852 sector->getBlocks(blocks);
2853 core::list<MapBlock*>::Iterator j;
2855 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2856 for(j=blocks.begin(); j!=blocks.end(); j++)
2858 MapBlock *block = *j;
2862 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2863 || only_changed == false)
2865 modprofiler.add(block->getModifiedReason(), 1);
2869 /*infostream<<"ServerMap: Written block ("
2870 <<block->getPos().X<<","
2871 <<block->getPos().Y<<","
2872 <<block->getPos().Z<<")"
2875 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
2881 Only print if something happened or saved whole map
2883 if(only_changed == false || sector_meta_count != 0
2884 || block_count != 0)
2886 infostream<<"ServerMap: Written: "
2887 <<sector_meta_count<<" sector metadata files, "
2888 <<block_count<<" block files"
2889 <<", "<<block_count_all<<" blocks in memory."
2891 PrintInfo(infostream); // ServerMap/ClientMap:
2892 infostream<<"Blocks modified by: "<<std::endl;
2893 modprofiler.print(infostream);
2897 static s32 unsignedToSigned(s32 i, s32 max_positive)
2899 if(i < max_positive)
2902 return i - 2*max_positive;
2905 // modulo of a negative number does not work consistently in C
2906 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2910 return mod - ((-i) % mod);
2913 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2915 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2917 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2919 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2920 return v3s16(x,y,z);
2923 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2925 if(loadFromFolders()){
2926 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2927 <<"all blocks that are stored in flat files"<<std::endl;
2933 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2935 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2936 v3s16 p = getIntegerAsBlock(block_i);
2937 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2943 void ServerMap::saveMapMeta()
2945 DSTACK(__FUNCTION_NAME);
2947 infostream<<"ServerMap::saveMapMeta(): "
2951 createDirs(m_savedir);
2953 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2954 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2955 if(os.good() == false)
2957 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2958 <<"could not open"<<fullpath<<std::endl;
2959 throw FileNotGoodException("Cannot open chunk metadata");
2963 params.setU64("seed", m_seed);
2965 params.writeLines(os);
2967 os<<"[end_of_params]\n";
2969 m_map_metadata_changed = false;
2972 void ServerMap::loadMapMeta()
2974 DSTACK(__FUNCTION_NAME);
2976 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2979 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2980 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2981 if(is.good() == false)
2983 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2984 <<"could not open"<<fullpath<<std::endl;
2985 throw FileNotGoodException("Cannot open map metadata");
2993 throw SerializationError
2994 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2996 std::getline(is, line);
2997 std::string trimmedline = trim(line);
2998 if(trimmedline == "[end_of_params]")
3000 params.parseConfigLine(line);
3003 m_seed = params.getU64("seed");
3005 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3008 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3010 DSTACK(__FUNCTION_NAME);
3011 // Format used for writing
3012 u8 version = SER_FMT_VER_HIGHEST;
3014 v2s16 pos = sector->getPos();
3015 std::string dir = getSectorDir(pos);
3018 std::string fullpath = dir + DIR_DELIM + "meta";
3019 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3020 if(o.good() == false)
3021 throw FileNotGoodException("Cannot open sector metafile");
3023 sector->serialize(o, version);
3025 sector->differs_from_disk = false;
3028 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3030 DSTACK(__FUNCTION_NAME);
3032 v2s16 p2d = getSectorPos(sectordir);
3034 ServerMapSector *sector = NULL;
3036 std::string fullpath = sectordir + DIR_DELIM + "meta";
3037 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3038 if(is.good() == false)
3040 // If the directory exists anyway, it probably is in some old
3041 // format. Just go ahead and create the sector.
3042 if(fs::PathExists(sectordir))
3044 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3045 <<fullpath<<" doesn't exist but directory does."
3046 <<" Continuing with a sector with no metadata."
3048 sector = new ServerMapSector(this, p2d, m_gamedef);
3049 m_sectors.insert(p2d, sector);
3053 throw FileNotGoodException("Cannot open sector metafile");
3058 sector = ServerMapSector::deSerialize
3059 (is, this, p2d, m_sectors, m_gamedef);
3061 saveSectorMeta(sector);
3064 sector->differs_from_disk = false;
3069 bool ServerMap::loadSectorMeta(v2s16 p2d)
3071 DSTACK(__FUNCTION_NAME);
3073 MapSector *sector = NULL;
3075 // The directory layout we're going to load from.
3076 // 1 - original sectors/xxxxzzzz/
3077 // 2 - new sectors2/xxx/zzz/
3078 // If we load from anything but the latest structure, we will
3079 // immediately save to the new one, and remove the old.
3081 std::string sectordir1 = getSectorDir(p2d, 1);
3082 std::string sectordir;
3083 if(fs::PathExists(sectordir1))
3085 sectordir = sectordir1;
3090 sectordir = getSectorDir(p2d, 2);
3094 sector = loadSectorMeta(sectordir, loadlayout != 2);
3096 catch(InvalidFilenameException &e)
3100 catch(FileNotGoodException &e)
3104 catch(std::exception &e)
3113 bool ServerMap::loadSectorFull(v2s16 p2d)
3115 DSTACK(__FUNCTION_NAME);
3117 MapSector *sector = NULL;
3119 // The directory layout we're going to load from.
3120 // 1 - original sectors/xxxxzzzz/
3121 // 2 - new sectors2/xxx/zzz/
3122 // If we load from anything but the latest structure, we will
3123 // immediately save to the new one, and remove the old.
3125 std::string sectordir1 = getSectorDir(p2d, 1);
3126 std::string sectordir;
3127 if(fs::PathExists(sectordir1))
3129 sectordir = sectordir1;
3134 sectordir = getSectorDir(p2d, 2);
3138 sector = loadSectorMeta(sectordir, loadlayout != 2);
3140 catch(InvalidFilenameException &e)
3144 catch(FileNotGoodException &e)
3148 catch(std::exception &e)
3156 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3158 std::vector<fs::DirListNode>::iterator i2;
3159 for(i2=list2.begin(); i2!=list2.end(); i2++)
3165 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3167 catch(InvalidFilenameException &e)
3169 // This catches unknown crap in directory
3175 infostream<<"Sector converted to new layout - deleting "<<
3176 sectordir1<<std::endl;
3177 fs::RecursiveDelete(sectordir1);
3184 void ServerMap::beginSave() {
3186 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3187 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3190 void ServerMap::endSave() {
3192 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3193 infostream<<"WARNING: endSave() failed, map might not have saved.";
3196 void ServerMap::saveBlock(MapBlock *block)
3198 DSTACK(__FUNCTION_NAME);
3200 Dummy blocks are not written
3202 if(block->isDummy())
3204 /*v3s16 p = block->getPos();
3205 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3206 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3210 // Format used for writing
3211 u8 version = SER_FMT_VER_HIGHEST;
3213 v3s16 p3d = block->getPos();
3217 v2s16 p2d(p3d.X, p3d.Z);
3218 std::string sectordir = getSectorDir(p2d);
3220 createDirs(sectordir);
3222 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3223 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3224 if(o.good() == false)
3225 throw FileNotGoodException("Cannot open block data");
3228 [0] u8 serialization version
3234 std::ostringstream o(std::ios_base::binary);
3236 o.write((char*)&version, 1);
3239 block->serialize(o, version);
3241 // Write extra data stored on disk
3242 block->serializeDiskExtra(o, version);
3244 // Write block to database
3246 std::string tmp = o.str();
3247 const char *bytes = tmp.c_str();
3249 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3250 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3251 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3252 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3253 int written = sqlite3_step(m_database_write);
3254 if(written != SQLITE_DONE)
3255 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3256 <<sqlite3_errmsg(m_database)<<std::endl;
3257 // Make ready for later reuse
3258 sqlite3_reset(m_database_write);
3260 // We just wrote it to the disk so clear modified flag
3261 block->resetModified();
3264 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3266 DSTACK(__FUNCTION_NAME);
3268 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3271 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3272 if(is.good() == false)
3273 throw FileNotGoodException("Cannot open block file");
3275 v3s16 p3d = getBlockPos(sectordir, blockfile);
3276 v2s16 p2d(p3d.X, p3d.Z);
3278 assert(sector->getPos() == p2d);
3280 u8 version = SER_FMT_VER_INVALID;
3281 is.read((char*)&version, 1);
3284 throw SerializationError("ServerMap::loadBlock(): Failed"
3285 " to read MapBlock version");
3287 /*u32 block_size = MapBlock::serializedLength(version);
3288 SharedBuffer<u8> data(block_size);
3289 is.read((char*)*data, block_size);*/
3291 // This will always return a sector because we're the server
3292 //MapSector *sector = emergeSector(p2d);
3294 MapBlock *block = NULL;
3295 bool created_new = false;
3296 block = sector->getBlockNoCreateNoEx(p3d.Y);
3299 block = sector->createBlankBlockNoInsert(p3d.Y);
3304 block->deSerialize(is, version);
3306 // Read extra data stored on disk
3307 block->deSerializeDiskExtra(is, version);
3309 // If it's a new block, insert it to the map
3311 sector->insertBlock(block);
3314 Save blocks loaded in old format in new format
3317 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3321 // Should be in database now, so delete the old file
3322 fs::RecursiveDelete(fullpath);
3325 // We just loaded it from the disk, so it's up-to-date.
3326 block->resetModified();
3329 catch(SerializationError &e)
3331 infostream<<"WARNING: Invalid block data on disk "
3332 <<"fullpath="<<fullpath
3333 <<" (SerializationError). "
3334 <<"what()="<<e.what()
3336 //" Ignoring. A new one will be generated.
3339 // TODO: Backup file; name is in fullpath.
3343 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3345 DSTACK(__FUNCTION_NAME);
3348 std::istringstream is(*blob, std::ios_base::binary);
3350 u8 version = SER_FMT_VER_INVALID;
3351 is.read((char*)&version, 1);
3354 throw SerializationError("ServerMap::loadBlock(): Failed"
3355 " to read MapBlock version");
3357 /*u32 block_size = MapBlock::serializedLength(version);
3358 SharedBuffer<u8> data(block_size);
3359 is.read((char*)*data, block_size);*/
3361 // This will always return a sector because we're the server
3362 //MapSector *sector = emergeSector(p2d);
3364 MapBlock *block = NULL;
3365 bool created_new = false;
3366 block = sector->getBlockNoCreateNoEx(p3d.Y);
3369 block = sector->createBlankBlockNoInsert(p3d.Y);
3374 block->deSerialize(is, version);
3376 // Read extra data stored on disk
3377 block->deSerializeDiskExtra(is, version);
3379 // If it's a new block, insert it to the map
3381 sector->insertBlock(block);
3384 Save blocks loaded in old format in new format
3387 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3388 // Only save if asked to; no need to update version
3392 // We just loaded it from, so it's up-to-date.
3393 block->resetModified();
3396 catch(SerializationError &e)
3398 infostream<<"WARNING: Invalid block data in database "
3399 <<" (SerializationError). "
3400 <<"what()="<<e.what()
3402 //" Ignoring. A new one will be generated.
3405 // TODO: Copy to a backup database.
3409 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3411 DSTACK(__FUNCTION_NAME);
3413 v2s16 p2d(blockpos.X, blockpos.Z);
3415 if(!loadFromFolders()) {
3418 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3419 infostream<<"WARNING: Could not bind block position for load: "
3420 <<sqlite3_errmsg(m_database)<<std::endl;
3421 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3423 Make sure sector is loaded
3425 MapSector *sector = createSector(p2d);
3430 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3431 size_t len = sqlite3_column_bytes(m_database_read, 0);
3433 std::string datastr(data, len);
3435 loadBlock(&datastr, blockpos, sector, false);
3437 sqlite3_step(m_database_read);
3438 // We should never get more than 1 row, so ok to reset
3439 sqlite3_reset(m_database_read);
3441 return getBlockNoCreateNoEx(blockpos);
3443 sqlite3_reset(m_database_read);
3445 // Not found in database, try the files
3448 // The directory layout we're going to load from.
3449 // 1 - original sectors/xxxxzzzz/
3450 // 2 - new sectors2/xxx/zzz/
3451 // If we load from anything but the latest structure, we will
3452 // immediately save to the new one, and remove the old.
3454 std::string sectordir1 = getSectorDir(p2d, 1);
3455 std::string sectordir;
3456 if(fs::PathExists(sectordir1))
3458 sectordir = sectordir1;
3463 sectordir = getSectorDir(p2d, 2);
3467 Make sure sector is loaded
3469 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3473 sector = loadSectorMeta(sectordir, loadlayout != 2);
3475 catch(InvalidFilenameException &e)
3479 catch(FileNotGoodException &e)
3483 catch(std::exception &e)
3490 Make sure file exists
3493 std::string blockfilename = getBlockFilename(blockpos);
3494 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3498 Load block and save it to the database
3500 loadBlock(sectordir, blockfilename, sector, true);
3501 return getBlockNoCreateNoEx(blockpos);
3504 void ServerMap::PrintInfo(std::ostream &out)
3515 ClientMap::ClientMap(
3518 MapDrawControl &control,
3519 scene::ISceneNode* parent,
3520 scene::ISceneManager* mgr,
3523 Map(dout_client, gamedef),
3524 scene::ISceneNode(parent, mgr, id),
3527 m_camera_position(0,0,0),
3528 m_camera_direction(0,0,1),
3531 m_camera_mutex.Init();
3532 assert(m_camera_mutex.IsInitialized());
3534 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3535 BS*1000000,BS*1000000,BS*1000000);
3538 ClientMap::~ClientMap()
3540 /*JMutexAutoLock lock(mesh_mutex);
3549 MapSector * ClientMap::emergeSector(v2s16 p2d)
3551 DSTACK(__FUNCTION_NAME);
3552 // Check that it doesn't exist already
3554 return getSectorNoGenerate(p2d);
3556 catch(InvalidPositionException &e)
3561 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3564 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3565 m_sectors.insert(p2d, sector);
3572 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3574 DSTACK(__FUNCTION_NAME);
3575 ClientMapSector *sector = NULL;
3577 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3579 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3583 sector = (ClientMapSector*)n->getValue();
3584 assert(sector->getId() == MAPSECTOR_CLIENT);
3588 sector = new ClientMapSector(this, p2d);
3590 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3591 m_sectors.insert(p2d, sector);
3595 sector->deSerialize(is);
3599 void ClientMap::OnRegisterSceneNode()
3603 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3604 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3607 ISceneNode::OnRegisterSceneNode();
3610 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3611 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3613 float d0 = (float)BS * p0.getDistanceFrom(p1);
3615 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3617 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3619 for(float s=start_off; s<d0+end_off; s+=step){
3620 v3f pf = p0f + uf * s;
3621 v3s16 p = floatToInt(pf, BS);
3622 MapNode n = map->getNodeNoEx(p);
3623 bool is_transparent = false;
3624 const ContentFeatures &f = nodemgr->get(n);
3625 if(f.solidness == 0)
3626 is_transparent = (f.visual_solidness != 2);
3628 is_transparent = (f.solidness != 2);
3629 if(!is_transparent){
3631 if(count >= needed_count)
3639 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3641 INodeDefManager *nodemgr = m_gamedef->ndef();
3643 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3644 DSTACK(__FUNCTION_NAME);
3646 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3649 if(pass == scene::ESNRP_SOLID)
3650 prefix = "CM: solid: ";
3652 prefix = "CM: transparent: ";
3655 This is called two times per frame, reset on the non-transparent one
3657 if(pass == scene::ESNRP_SOLID)
3659 m_last_drawn_sectors.clear();
3663 Get time for measuring timeout.
3665 Measuring time is very useful for long delays when the
3666 machine is swapping a lot.
3668 int time1 = time(0);
3670 //u32 daynight_ratio = m_client->getDayNightRatio();
3672 m_camera_mutex.Lock();
3673 v3f camera_position = m_camera_position;
3674 v3f camera_direction = m_camera_direction;
3675 f32 camera_fov = m_camera_fov;
3676 m_camera_mutex.Unlock();
3679 Get all blocks and draw all visible ones
3682 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3684 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3686 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3687 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3689 // Take a fair amount as we will be dropping more out later
3690 // Umm... these additions are a bit strange but they are needed.
3692 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3693 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3694 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3696 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3697 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3698 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3700 u32 vertex_count = 0;
3701 u32 meshbuffer_count = 0;
3703 // For limiting number of mesh updates per frame
3704 u32 mesh_update_count = 0;
3706 // Number of blocks in rendering range
3707 u32 blocks_in_range = 0;
3708 // Number of blocks occlusion culled
3709 u32 blocks_occlusion_culled = 0;
3710 // Number of blocks in rendering range but don't have a mesh
3711 u32 blocks_in_range_without_mesh = 0;
3712 // Blocks that had mesh that would have been drawn according to
3713 // rendering range (if max blocks limit didn't kick in)
3714 u32 blocks_would_have_drawn = 0;
3715 // Blocks that were drawn and had a mesh
3716 u32 blocks_drawn = 0;
3717 // Blocks which had a corresponding meshbuffer for this pass
3718 u32 blocks_had_pass_meshbuf = 0;
3719 // Blocks from which stuff was actually drawn
3720 u32 blocks_without_stuff = 0;
3723 Collect a set of blocks for drawing
3726 core::map<v3s16, MapBlock*> drawset;
3729 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3731 for(core::map<v2s16, MapSector*>::Iterator
3732 si = m_sectors.getIterator();
3733 si.atEnd() == false; si++)
3735 MapSector *sector = si.getNode()->getValue();
3736 v2s16 sp = sector->getPos();
3738 if(m_control.range_all == false)
3740 if(sp.X < p_blocks_min.X
3741 || sp.X > p_blocks_max.X
3742 || sp.Y < p_blocks_min.Z
3743 || sp.Y > p_blocks_max.Z)
3747 core::list< MapBlock * > sectorblocks;
3748 sector->getBlocks(sectorblocks);
3751 Loop through blocks in sector
3754 u32 sector_blocks_drawn = 0;
3756 core::list< MapBlock * >::Iterator i;
3757 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3759 MapBlock *block = *i;
3762 Compare block position to camera position, skip
3763 if not seen on display
3766 float range = 100000 * BS;
3767 if(m_control.range_all == false)
3768 range = m_control.wanted_range * BS;
3771 if(isBlockInSight(block->getPos(), camera_position,
3772 camera_direction, camera_fov,
3773 range, &d) == false)
3778 // This is ugly (spherical distance limit?)
3779 /*if(m_control.range_all == false &&
3780 d - 0.5*BS*MAP_BLOCKSIZE > range)
3787 Update expired mesh (used for day/night change)
3789 It doesn't work exactly like it should now with the
3790 tasked mesh update but whatever.
3793 bool mesh_expired = false;
3796 JMutexAutoLock lock(block->mesh_mutex);
3798 mesh_expired = block->getMeshExpired();
3800 // Mesh has not been expired and there is no mesh:
3801 // block has no content
3802 if(block->mesh == NULL && mesh_expired == false){
3803 blocks_in_range_without_mesh++;
3808 f32 faraway = BS*50;
3809 //f32 faraway = m_control.wanted_range * BS;
3812 This has to be done with the mesh_mutex unlocked
3814 // Pretty random but this should work somewhat nicely
3815 if(mesh_expired && (
3816 (mesh_update_count < 3
3817 && (d < faraway || mesh_update_count < 2)
3820 (m_control.range_all && mesh_update_count < 20)
3823 /*if(mesh_expired && mesh_update_count < 6
3824 && (d < faraway || mesh_update_count < 3))*/
3826 mesh_update_count++;
3828 // Mesh has been expired: generate new mesh
3829 //block->updateMesh(daynight_ratio);
3830 m_client->addUpdateMeshTask(block->getPos());
3832 mesh_expired = false;
3840 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3841 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3843 float stepfac = 1.1;
3844 float startoff = BS*1;
3845 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3846 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3847 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3848 u32 needed_count = 1;
3850 isOccluded(this, spn, cpn + v3s16(0,0,0),
3851 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3852 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3853 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3854 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3855 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3856 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3857 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3858 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3859 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3860 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3861 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3862 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3863 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3864 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3865 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3866 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3867 step, stepfac, startoff, endoff, needed_count, nodemgr)
3870 blocks_occlusion_culled++;
3874 // This block is in range. Reset usage timer.
3875 block->resetUsageTimer();
3878 Ignore if mesh doesn't exist
3881 JMutexAutoLock lock(block->mesh_mutex);
3883 scene::SMesh *mesh = block->mesh;
3886 blocks_in_range_without_mesh++;
3891 // Limit block count in case of a sudden increase
3892 blocks_would_have_drawn++;
3893 if(blocks_drawn >= m_control.wanted_max_blocks
3894 && m_control.range_all == false
3895 && d > m_control.wanted_min_range * BS)
3899 drawset[block->getPos()] = block;
3901 sector_blocks_drawn++;
3904 } // foreach sectorblocks
3906 if(sector_blocks_drawn != 0)
3907 m_last_drawn_sectors[sp] = true;
3912 Draw the selected MapBlocks
3916 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3918 int timecheck_counter = 0;
3919 for(core::map<v3s16, MapBlock*>::Iterator
3920 i = drawset.getIterator();
3921 i.atEnd() == false; i++)
3924 timecheck_counter++;
3925 if(timecheck_counter > 50)
3927 timecheck_counter = 0;
3928 int time2 = time(0);
3929 if(time2 > time1 + 4)
3931 infostream<<"ClientMap::renderMap(): "
3932 "Rendering takes ages, returning."
3939 MapBlock *block = i.getNode()->getValue();
3942 Draw the faces of the block
3945 JMutexAutoLock lock(block->mesh_mutex);
3947 scene::SMesh *mesh = block->mesh;
3950 u32 c = mesh->getMeshBufferCount();
3951 bool stuff_actually_drawn = false;
3952 for(u32 i=0; i<c; i++)
3954 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3955 const video::SMaterial& material = buf->getMaterial();
3956 video::IMaterialRenderer* rnd =
3957 driver->getMaterialRenderer(material.MaterialType);
3958 bool transparent = (rnd && rnd->isTransparent());
3959 // Render transparent on transparent pass and likewise.
3960 if(transparent == is_transparent_pass)
3962 if(buf->getVertexCount() == 0)
3963 errorstream<<"Block ["<<analyze_block(block)
3964 <<"] contains an empty meshbuf"<<std::endl;
3966 This *shouldn't* hurt too much because Irrlicht
3967 doesn't change opengl textures if the old
3968 material has the same texture.
3970 driver->setMaterial(buf->getMaterial());
3971 driver->drawMeshBuffer(buf);
3972 vertex_count += buf->getVertexCount();
3974 stuff_actually_drawn = true;
3977 if(stuff_actually_drawn)
3978 blocks_had_pass_meshbuf++;
3980 blocks_without_stuff++;
3985 // Log only on solid pass because values are the same
3986 if(pass == scene::ESNRP_SOLID){
3987 g_profiler->avg("CM: blocks in range", blocks_in_range);
3988 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3989 if(blocks_in_range != 0)
3990 g_profiler->avg("CM: blocks in range without mesh (frac)",
3991 (float)blocks_in_range_without_mesh/blocks_in_range);
3992 g_profiler->avg("CM: blocks drawn", blocks_drawn);
3995 g_profiler->avg(prefix+"vertices drawn", vertex_count);
3996 if(blocks_had_pass_meshbuf != 0)
3997 g_profiler->avg(prefix+"meshbuffers per block",
3998 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
3999 if(blocks_drawn != 0)
4000 g_profiler->avg(prefix+"empty blocks (frac)",
4001 (float)blocks_without_stuff / blocks_drawn);
4003 m_control.blocks_drawn = blocks_drawn;
4004 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4006 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4007 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4010 void ClientMap::renderPostFx()
4012 INodeDefManager *nodemgr = m_gamedef->ndef();
4014 // Sadly ISceneManager has no "post effects" render pass, in that case we
4015 // could just register for that and handle it in renderMap().
4017 m_camera_mutex.Lock();
4018 v3f camera_position = m_camera_position;
4019 m_camera_mutex.Unlock();
4021 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4023 // - If the player is in a solid node, make everything black.
4024 // - If the player is in liquid, draw a semi-transparent overlay.
4025 const ContentFeatures& features = nodemgr->get(n);
4026 video::SColor post_effect_color = features.post_effect_color;
4027 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4029 post_effect_color = video::SColor(255, 0, 0, 0);
4031 if (post_effect_color.getAlpha() != 0)
4033 // Draw a full-screen rectangle
4034 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4035 v2u32 ss = driver->getScreenSize();
4036 core::rect<s32> rect(0,0, ss.X, ss.Y);
4037 driver->draw2DRectangle(post_effect_color, rect);
4041 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4042 core::map<v3s16, MapBlock*> *affected_blocks)
4044 bool changed = false;
4046 Add it to all blocks touching it
4049 v3s16(0,0,0), // this
4050 v3s16(0,0,1), // back
4051 v3s16(0,1,0), // top
4052 v3s16(1,0,0), // right
4053 v3s16(0,0,-1), // front
4054 v3s16(0,-1,0), // bottom
4055 v3s16(-1,0,0), // left
4057 for(u16 i=0; i<7; i++)
4059 v3s16 p2 = p + dirs[i];
4060 // Block position of neighbor (or requested) node
4061 v3s16 blockpos = getNodeBlockPos(p2);
4062 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4063 if(blockref == NULL)
4065 // Relative position of requested node
4066 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4067 if(blockref->setTempMod(relpos, mod))
4072 if(changed && affected_blocks!=NULL)
4074 for(u16 i=0; i<7; i++)
4076 v3s16 p2 = p + dirs[i];
4077 // Block position of neighbor (or requested) node
4078 v3s16 blockpos = getNodeBlockPos(p2);
4079 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4080 if(blockref == NULL)
4082 affected_blocks->insert(blockpos, blockref);
4088 bool ClientMap::clearTempMod(v3s16 p,
4089 core::map<v3s16, MapBlock*> *affected_blocks)
4091 bool changed = false;
4093 v3s16(0,0,0), // this
4094 v3s16(0,0,1), // back
4095 v3s16(0,1,0), // top
4096 v3s16(1,0,0), // right
4097 v3s16(0,0,-1), // front
4098 v3s16(0,-1,0), // bottom
4099 v3s16(-1,0,0), // left
4101 for(u16 i=0; i<7; i++)
4103 v3s16 p2 = p + dirs[i];
4104 // Block position of neighbor (or requested) node
4105 v3s16 blockpos = getNodeBlockPos(p2);
4106 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4107 if(blockref == NULL)
4109 // Relative position of requested node
4110 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4111 if(blockref->clearTempMod(relpos))
4116 if(changed && affected_blocks!=NULL)
4118 for(u16 i=0; i<7; i++)
4120 v3s16 p2 = p + dirs[i];
4121 // Block position of neighbor (or requested) node
4122 v3s16 blockpos = getNodeBlockPos(p2);
4123 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4124 if(blockref == NULL)
4126 affected_blocks->insert(blockpos, blockref);
4132 void ClientMap::expireMeshes(bool only_daynight_diffed)
4134 TimeTaker timer("expireMeshes()");
4136 core::map<v2s16, MapSector*>::Iterator si;
4137 si = m_sectors.getIterator();
4138 for(; si.atEnd() == false; si++)
4140 MapSector *sector = si.getNode()->getValue();
4142 core::list< MapBlock * > sectorblocks;
4143 sector->getBlocks(sectorblocks);
4145 core::list< MapBlock * >::Iterator i;
4146 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4148 MapBlock *block = *i;
4150 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4156 JMutexAutoLock lock(block->mesh_mutex);
4157 if(block->mesh != NULL)
4159 /*block->mesh->drop();
4160 block->mesh = NULL;*/
4161 block->setMeshExpired(true);
4168 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4170 assert(mapType() == MAPTYPE_CLIENT);
4173 v3s16 p = blockpos + v3s16(0,0,0);
4174 MapBlock *b = getBlockNoCreate(p);
4175 b->updateMesh(daynight_ratio);
4176 //b->setMeshExpired(true);
4178 catch(InvalidPositionException &e){}
4181 v3s16 p = blockpos + v3s16(-1,0,0);
4182 MapBlock *b = getBlockNoCreate(p);
4183 b->updateMesh(daynight_ratio);
4184 //b->setMeshExpired(true);
4186 catch(InvalidPositionException &e){}
4188 v3s16 p = blockpos + v3s16(0,-1,0);
4189 MapBlock *b = getBlockNoCreate(p);
4190 b->updateMesh(daynight_ratio);
4191 //b->setMeshExpired(true);
4193 catch(InvalidPositionException &e){}
4195 v3s16 p = blockpos + v3s16(0,0,-1);
4196 MapBlock *b = getBlockNoCreate(p);
4197 b->updateMesh(daynight_ratio);
4198 //b->setMeshExpired(true);
4200 catch(InvalidPositionException &e){}
4205 Update mesh of block in which the node is, and if the node is at the
4206 leading edge, update the appropriate leading blocks too.
4208 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4216 v3s16 blockposes[4];
4217 for(u32 i=0; i<4; i++)
4219 v3s16 np = nodepos + dirs[i];
4220 blockposes[i] = getNodeBlockPos(np);
4221 // Don't update mesh of block if it has been done already
4222 bool already_updated = false;
4223 for(u32 j=0; j<i; j++)
4225 if(blockposes[j] == blockposes[i])
4227 already_updated = true;
4234 MapBlock *b = getBlockNoCreate(blockposes[i]);
4235 b->updateMesh(daynight_ratio);
4240 void ClientMap::PrintInfo(std::ostream &out)
4251 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4256 MapVoxelManipulator::~MapVoxelManipulator()
4258 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4262 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4264 TimeTaker timer1("emerge", &emerge_time);
4266 // Units of these are MapBlocks
4267 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4268 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4270 VoxelArea block_area_nodes
4271 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4273 addArea(block_area_nodes);
4275 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4276 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4277 for(s32 x=p_min.X; x<=p_max.X; x++)
4280 core::map<v3s16, bool>::Node *n;
4281 n = m_loaded_blocks.find(p);
4285 bool block_data_inexistent = false;
4288 TimeTaker timer1("emerge load", &emerge_load_time);
4290 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4291 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4293 a.print(infostream);
4294 infostream<<std::endl;*/
4296 MapBlock *block = m_map->getBlockNoCreate(p);
4297 if(block->isDummy())
4298 block_data_inexistent = true;
4300 block->copyTo(*this);
4302 catch(InvalidPositionException &e)
4304 block_data_inexistent = true;
4307 if(block_data_inexistent)
4309 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4310 // Fill with VOXELFLAG_INEXISTENT
4311 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4312 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4314 s32 i = m_area.index(a.MinEdge.X,y,z);
4315 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4319 m_loaded_blocks.insert(p, !block_data_inexistent);
4322 //infostream<<"emerge done"<<std::endl;
4326 SUGG: Add an option to only update eg. water and air nodes.
4327 This will make it interfere less with important stuff if
4330 void MapVoxelManipulator::blitBack
4331 (core::map<v3s16, MapBlock*> & modified_blocks)
4333 if(m_area.getExtent() == v3s16(0,0,0))
4336 //TimeTaker timer1("blitBack");
4338 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4339 <<m_loaded_blocks.size()<<std::endl;*/
4342 Initialize block cache
4344 v3s16 blockpos_last;
4345 MapBlock *block = NULL;
4346 bool block_checked_in_modified = false;
4348 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4349 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4350 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4354 u8 f = m_flags[m_area.index(p)];
4355 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4358 MapNode &n = m_data[m_area.index(p)];
4360 v3s16 blockpos = getNodeBlockPos(p);
4365 if(block == NULL || blockpos != blockpos_last){
4366 block = m_map->getBlockNoCreate(blockpos);
4367 blockpos_last = blockpos;
4368 block_checked_in_modified = false;
4371 // Calculate relative position in block
4372 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4374 // Don't continue if nothing has changed here
4375 if(block->getNode(relpos) == n)
4378 //m_map->setNode(m_area.MinEdge + p, n);
4379 block->setNode(relpos, n);
4382 Make sure block is in modified_blocks
4384 if(block_checked_in_modified == false)
4386 modified_blocks[blockpos] = block;
4387 block_checked_in_modified = true;
4390 catch(InvalidPositionException &e)
4396 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4397 MapVoxelManipulator(map),
4398 m_create_area(false)
4402 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4406 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4408 // Just create the area so that it can be pointed to
4409 VoxelManipulator::emerge(a, caller_id);
4412 void ManualMapVoxelManipulator::initialEmerge(
4413 v3s16 blockpos_min, v3s16 blockpos_max)
4415 TimeTaker timer1("initialEmerge", &emerge_time);
4417 // Units of these are MapBlocks
4418 v3s16 p_min = blockpos_min;
4419 v3s16 p_max = blockpos_max;
4421 VoxelArea block_area_nodes
4422 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4424 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4427 infostream<<"initialEmerge: area: ";
4428 block_area_nodes.print(infostream);
4429 infostream<<" ("<<size_MB<<"MB)";
4430 infostream<<std::endl;
4433 addArea(block_area_nodes);
4435 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4436 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4437 for(s32 x=p_min.X; x<=p_max.X; x++)
4440 core::map<v3s16, bool>::Node *n;
4441 n = m_loaded_blocks.find(p);
4445 bool block_data_inexistent = false;
4448 TimeTaker timer1("emerge load", &emerge_load_time);
4450 MapBlock *block = m_map->getBlockNoCreate(p);
4451 if(block->isDummy())
4452 block_data_inexistent = true;
4454 block->copyTo(*this);
4456 catch(InvalidPositionException &e)
4458 block_data_inexistent = true;
4461 if(block_data_inexistent)
4464 Mark area inexistent
4466 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4467 // Fill with VOXELFLAG_INEXISTENT
4468 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4469 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4471 s32 i = m_area.index(a.MinEdge.X,y,z);
4472 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4476 m_loaded_blocks.insert(p, !block_data_inexistent);
4480 void ManualMapVoxelManipulator::blitBackAll(
4481 core::map<v3s16, MapBlock*> * modified_blocks)
4483 if(m_area.getExtent() == v3s16(0,0,0))
4487 Copy data of all blocks
4489 for(core::map<v3s16, bool>::Iterator
4490 i = m_loaded_blocks.getIterator();
4491 i.atEnd() == false; i++)
4493 v3s16 p = i.getNode()->getKey();
4494 bool existed = i.getNode()->getValue();
4495 if(existed == false)
4497 // The Great Bug was found using this
4498 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4499 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4503 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4506 infostream<<"WARNING: "<<__FUNCTION_NAME
4507 <<": got NULL block "
4508 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4513 block->copyFrom(*this);
4516 modified_blocks->insert(p, block);