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
2839 // Don't do anything with sqlite unless something is really saved
2840 bool save_started = false;
2842 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2843 for(; i.atEnd() == false; i++)
2845 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2846 assert(sector->getId() == MAPSECTOR_SERVER);
2848 if(sector->differs_from_disk || only_changed == false)
2850 saveSectorMeta(sector);
2851 sector_meta_count++;
2853 core::list<MapBlock*> blocks;
2854 sector->getBlocks(blocks);
2855 core::list<MapBlock*>::Iterator j;
2857 for(j=blocks.begin(); j!=blocks.end(); j++)
2859 MapBlock *block = *j;
2863 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2864 || only_changed == false)
2869 save_started = true;
2872 modprofiler.add(block->getModifiedReason(), 1);
2877 /*infostream<<"ServerMap: Written block ("
2878 <<block->getPos().X<<","
2879 <<block->getPos().Y<<","
2880 <<block->getPos().Z<<")"
2889 Only print if something happened or saved whole map
2891 if(only_changed == false || sector_meta_count != 0
2892 || block_count != 0)
2894 infostream<<"ServerMap: Written: "
2895 <<sector_meta_count<<" sector metadata files, "
2896 <<block_count<<" block files"
2897 <<", "<<block_count_all<<" blocks in memory."
2899 PrintInfo(infostream); // ServerMap/ClientMap:
2900 infostream<<"Blocks modified by: "<<std::endl;
2901 modprofiler.print(infostream);
2905 static s32 unsignedToSigned(s32 i, s32 max_positive)
2907 if(i < max_positive)
2910 return i - 2*max_positive;
2913 // modulo of a negative number does not work consistently in C
2914 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2918 return mod - ((-i) % mod);
2921 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2923 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2925 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2927 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2928 return v3s16(x,y,z);
2931 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2933 if(loadFromFolders()){
2934 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2935 <<"all blocks that are stored in flat files"<<std::endl;
2941 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2943 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2944 v3s16 p = getIntegerAsBlock(block_i);
2945 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2951 void ServerMap::saveMapMeta()
2953 DSTACK(__FUNCTION_NAME);
2955 infostream<<"ServerMap::saveMapMeta(): "
2959 createDirs(m_savedir);
2961 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2962 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2963 if(os.good() == false)
2965 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2966 <<"could not open"<<fullpath<<std::endl;
2967 throw FileNotGoodException("Cannot open chunk metadata");
2971 params.setU64("seed", m_seed);
2973 params.writeLines(os);
2975 os<<"[end_of_params]\n";
2977 m_map_metadata_changed = false;
2980 void ServerMap::loadMapMeta()
2982 DSTACK(__FUNCTION_NAME);
2984 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2987 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2988 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2989 if(is.good() == false)
2991 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2992 <<"could not open"<<fullpath<<std::endl;
2993 throw FileNotGoodException("Cannot open map metadata");
3001 throw SerializationError
3002 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3004 std::getline(is, line);
3005 std::string trimmedline = trim(line);
3006 if(trimmedline == "[end_of_params]")
3008 params.parseConfigLine(line);
3011 m_seed = params.getU64("seed");
3013 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3016 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3018 DSTACK(__FUNCTION_NAME);
3019 // Format used for writing
3020 u8 version = SER_FMT_VER_HIGHEST;
3022 v2s16 pos = sector->getPos();
3023 std::string dir = getSectorDir(pos);
3026 std::string fullpath = dir + DIR_DELIM + "meta";
3027 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3028 if(o.good() == false)
3029 throw FileNotGoodException("Cannot open sector metafile");
3031 sector->serialize(o, version);
3033 sector->differs_from_disk = false;
3036 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3038 DSTACK(__FUNCTION_NAME);
3040 v2s16 p2d = getSectorPos(sectordir);
3042 ServerMapSector *sector = NULL;
3044 std::string fullpath = sectordir + DIR_DELIM + "meta";
3045 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3046 if(is.good() == false)
3048 // If the directory exists anyway, it probably is in some old
3049 // format. Just go ahead and create the sector.
3050 if(fs::PathExists(sectordir))
3052 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3053 <<fullpath<<" doesn't exist but directory does."
3054 <<" Continuing with a sector with no metadata."
3056 sector = new ServerMapSector(this, p2d, m_gamedef);
3057 m_sectors.insert(p2d, sector);
3061 throw FileNotGoodException("Cannot open sector metafile");
3066 sector = ServerMapSector::deSerialize
3067 (is, this, p2d, m_sectors, m_gamedef);
3069 saveSectorMeta(sector);
3072 sector->differs_from_disk = false;
3077 bool ServerMap::loadSectorMeta(v2s16 p2d)
3079 DSTACK(__FUNCTION_NAME);
3081 MapSector *sector = NULL;
3083 // The directory layout we're going to load from.
3084 // 1 - original sectors/xxxxzzzz/
3085 // 2 - new sectors2/xxx/zzz/
3086 // If we load from anything but the latest structure, we will
3087 // immediately save to the new one, and remove the old.
3089 std::string sectordir1 = getSectorDir(p2d, 1);
3090 std::string sectordir;
3091 if(fs::PathExists(sectordir1))
3093 sectordir = sectordir1;
3098 sectordir = getSectorDir(p2d, 2);
3102 sector = loadSectorMeta(sectordir, loadlayout != 2);
3104 catch(InvalidFilenameException &e)
3108 catch(FileNotGoodException &e)
3112 catch(std::exception &e)
3121 bool ServerMap::loadSectorFull(v2s16 p2d)
3123 DSTACK(__FUNCTION_NAME);
3125 MapSector *sector = NULL;
3127 // The directory layout we're going to load from.
3128 // 1 - original sectors/xxxxzzzz/
3129 // 2 - new sectors2/xxx/zzz/
3130 // If we load from anything but the latest structure, we will
3131 // immediately save to the new one, and remove the old.
3133 std::string sectordir1 = getSectorDir(p2d, 1);
3134 std::string sectordir;
3135 if(fs::PathExists(sectordir1))
3137 sectordir = sectordir1;
3142 sectordir = getSectorDir(p2d, 2);
3146 sector = loadSectorMeta(sectordir, loadlayout != 2);
3148 catch(InvalidFilenameException &e)
3152 catch(FileNotGoodException &e)
3156 catch(std::exception &e)
3164 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3166 std::vector<fs::DirListNode>::iterator i2;
3167 for(i2=list2.begin(); i2!=list2.end(); i2++)
3173 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3175 catch(InvalidFilenameException &e)
3177 // This catches unknown crap in directory
3183 infostream<<"Sector converted to new layout - deleting "<<
3184 sectordir1<<std::endl;
3185 fs::RecursiveDelete(sectordir1);
3192 void ServerMap::beginSave() {
3194 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3195 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3198 void ServerMap::endSave() {
3200 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3201 infostream<<"WARNING: endSave() failed, map might not have saved.";
3204 void ServerMap::saveBlock(MapBlock *block)
3206 DSTACK(__FUNCTION_NAME);
3208 Dummy blocks are not written
3210 if(block->isDummy())
3212 /*v3s16 p = block->getPos();
3213 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3214 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3218 // Format used for writing
3219 u8 version = SER_FMT_VER_HIGHEST;
3221 v3s16 p3d = block->getPos();
3225 v2s16 p2d(p3d.X, p3d.Z);
3226 std::string sectordir = getSectorDir(p2d);
3228 createDirs(sectordir);
3230 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3231 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3232 if(o.good() == false)
3233 throw FileNotGoodException("Cannot open block data");
3236 [0] u8 serialization version
3242 std::ostringstream o(std::ios_base::binary);
3244 o.write((char*)&version, 1);
3247 block->serialize(o, version);
3249 // Write extra data stored on disk
3250 block->serializeDiskExtra(o, version);
3252 // Write block to database
3254 std::string tmp = o.str();
3255 const char *bytes = tmp.c_str();
3257 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3258 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3259 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3260 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3261 int written = sqlite3_step(m_database_write);
3262 if(written != SQLITE_DONE)
3263 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3264 <<sqlite3_errmsg(m_database)<<std::endl;
3265 // Make ready for later reuse
3266 sqlite3_reset(m_database_write);
3268 // We just wrote it to the disk so clear modified flag
3269 block->resetModified();
3272 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3274 DSTACK(__FUNCTION_NAME);
3276 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3279 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3280 if(is.good() == false)
3281 throw FileNotGoodException("Cannot open block file");
3283 v3s16 p3d = getBlockPos(sectordir, blockfile);
3284 v2s16 p2d(p3d.X, p3d.Z);
3286 assert(sector->getPos() == p2d);
3288 u8 version = SER_FMT_VER_INVALID;
3289 is.read((char*)&version, 1);
3292 throw SerializationError("ServerMap::loadBlock(): Failed"
3293 " to read MapBlock version");
3295 /*u32 block_size = MapBlock::serializedLength(version);
3296 SharedBuffer<u8> data(block_size);
3297 is.read((char*)*data, block_size);*/
3299 // This will always return a sector because we're the server
3300 //MapSector *sector = emergeSector(p2d);
3302 MapBlock *block = NULL;
3303 bool created_new = false;
3304 block = sector->getBlockNoCreateNoEx(p3d.Y);
3307 block = sector->createBlankBlockNoInsert(p3d.Y);
3312 block->deSerialize(is, version);
3314 // Read extra data stored on disk
3315 block->deSerializeDiskExtra(is, version);
3317 // If it's a new block, insert it to the map
3319 sector->insertBlock(block);
3322 Save blocks loaded in old format in new format
3325 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3329 // Should be in database now, so delete the old file
3330 fs::RecursiveDelete(fullpath);
3333 // We just loaded it from the disk, so it's up-to-date.
3334 block->resetModified();
3337 catch(SerializationError &e)
3339 infostream<<"WARNING: Invalid block data on disk "
3340 <<"fullpath="<<fullpath
3341 <<" (SerializationError). "
3342 <<"what()="<<e.what()
3344 //" Ignoring. A new one will be generated.
3347 // TODO: Backup file; name is in fullpath.
3351 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3353 DSTACK(__FUNCTION_NAME);
3356 std::istringstream is(*blob, std::ios_base::binary);
3358 u8 version = SER_FMT_VER_INVALID;
3359 is.read((char*)&version, 1);
3362 throw SerializationError("ServerMap::loadBlock(): Failed"
3363 " to read MapBlock version");
3365 /*u32 block_size = MapBlock::serializedLength(version);
3366 SharedBuffer<u8> data(block_size);
3367 is.read((char*)*data, block_size);*/
3369 // This will always return a sector because we're the server
3370 //MapSector *sector = emergeSector(p2d);
3372 MapBlock *block = NULL;
3373 bool created_new = false;
3374 block = sector->getBlockNoCreateNoEx(p3d.Y);
3377 block = sector->createBlankBlockNoInsert(p3d.Y);
3382 block->deSerialize(is, version);
3384 // Read extra data stored on disk
3385 block->deSerializeDiskExtra(is, version);
3387 // If it's a new block, insert it to the map
3389 sector->insertBlock(block);
3392 Save blocks loaded in old format in new format
3395 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3396 // Only save if asked to; no need to update version
3400 // We just loaded it from, so it's up-to-date.
3401 block->resetModified();
3404 catch(SerializationError &e)
3406 infostream<<"WARNING: Invalid block data in database "
3407 <<" (SerializationError). "
3408 <<"what()="<<e.what()
3410 //" Ignoring. A new one will be generated.
3413 // TODO: Copy to a backup database.
3417 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3419 DSTACK(__FUNCTION_NAME);
3421 v2s16 p2d(blockpos.X, blockpos.Z);
3423 if(!loadFromFolders()) {
3426 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3427 infostream<<"WARNING: Could not bind block position for load: "
3428 <<sqlite3_errmsg(m_database)<<std::endl;
3429 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3431 Make sure sector is loaded
3433 MapSector *sector = createSector(p2d);
3438 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3439 size_t len = sqlite3_column_bytes(m_database_read, 0);
3441 std::string datastr(data, len);
3443 loadBlock(&datastr, blockpos, sector, false);
3445 sqlite3_step(m_database_read);
3446 // We should never get more than 1 row, so ok to reset
3447 sqlite3_reset(m_database_read);
3449 return getBlockNoCreateNoEx(blockpos);
3451 sqlite3_reset(m_database_read);
3453 // Not found in database, try the files
3456 // The directory layout we're going to load from.
3457 // 1 - original sectors/xxxxzzzz/
3458 // 2 - new sectors2/xxx/zzz/
3459 // If we load from anything but the latest structure, we will
3460 // immediately save to the new one, and remove the old.
3462 std::string sectordir1 = getSectorDir(p2d, 1);
3463 std::string sectordir;
3464 if(fs::PathExists(sectordir1))
3466 sectordir = sectordir1;
3471 sectordir = getSectorDir(p2d, 2);
3475 Make sure sector is loaded
3477 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3481 sector = loadSectorMeta(sectordir, loadlayout != 2);
3483 catch(InvalidFilenameException &e)
3487 catch(FileNotGoodException &e)
3491 catch(std::exception &e)
3498 Make sure file exists
3501 std::string blockfilename = getBlockFilename(blockpos);
3502 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3506 Load block and save it to the database
3508 loadBlock(sectordir, blockfilename, sector, true);
3509 return getBlockNoCreateNoEx(blockpos);
3512 void ServerMap::PrintInfo(std::ostream &out)
3523 ClientMap::ClientMap(
3526 MapDrawControl &control,
3527 scene::ISceneNode* parent,
3528 scene::ISceneManager* mgr,
3531 Map(dout_client, gamedef),
3532 scene::ISceneNode(parent, mgr, id),
3535 m_camera_position(0,0,0),
3536 m_camera_direction(0,0,1),
3539 m_camera_mutex.Init();
3540 assert(m_camera_mutex.IsInitialized());
3542 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3543 BS*1000000,BS*1000000,BS*1000000);
3546 ClientMap::~ClientMap()
3548 /*JMutexAutoLock lock(mesh_mutex);
3557 MapSector * ClientMap::emergeSector(v2s16 p2d)
3559 DSTACK(__FUNCTION_NAME);
3560 // Check that it doesn't exist already
3562 return getSectorNoGenerate(p2d);
3564 catch(InvalidPositionException &e)
3569 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3572 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3573 m_sectors.insert(p2d, sector);
3580 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3582 DSTACK(__FUNCTION_NAME);
3583 ClientMapSector *sector = NULL;
3585 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3587 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3591 sector = (ClientMapSector*)n->getValue();
3592 assert(sector->getId() == MAPSECTOR_CLIENT);
3596 sector = new ClientMapSector(this, p2d);
3598 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3599 m_sectors.insert(p2d, sector);
3603 sector->deSerialize(is);
3607 void ClientMap::OnRegisterSceneNode()
3611 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3612 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3615 ISceneNode::OnRegisterSceneNode();
3618 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3619 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3621 float d0 = (float)BS * p0.getDistanceFrom(p1);
3623 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3625 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3627 for(float s=start_off; s<d0+end_off; s+=step){
3628 v3f pf = p0f + uf * s;
3629 v3s16 p = floatToInt(pf, BS);
3630 MapNode n = map->getNodeNoEx(p);
3631 bool is_transparent = false;
3632 const ContentFeatures &f = nodemgr->get(n);
3633 if(f.solidness == 0)
3634 is_transparent = (f.visual_solidness != 2);
3636 is_transparent = (f.solidness != 2);
3637 if(!is_transparent){
3639 if(count >= needed_count)
3647 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3649 INodeDefManager *nodemgr = m_gamedef->ndef();
3651 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3652 DSTACK(__FUNCTION_NAME);
3654 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3657 if(pass == scene::ESNRP_SOLID)
3658 prefix = "CM: solid: ";
3660 prefix = "CM: transparent: ";
3663 This is called two times per frame, reset on the non-transparent one
3665 if(pass == scene::ESNRP_SOLID)
3667 m_last_drawn_sectors.clear();
3671 Get time for measuring timeout.
3673 Measuring time is very useful for long delays when the
3674 machine is swapping a lot.
3676 int time1 = time(0);
3678 //u32 daynight_ratio = m_client->getDayNightRatio();
3680 m_camera_mutex.Lock();
3681 v3f camera_position = m_camera_position;
3682 v3f camera_direction = m_camera_direction;
3683 f32 camera_fov = m_camera_fov;
3684 m_camera_mutex.Unlock();
3687 Get all blocks and draw all visible ones
3690 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3692 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3694 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3695 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3697 // Take a fair amount as we will be dropping more out later
3698 // Umm... these additions are a bit strange but they are needed.
3700 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3701 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3702 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3704 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3705 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3706 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3708 u32 vertex_count = 0;
3709 u32 meshbuffer_count = 0;
3711 // For limiting number of mesh updates per frame
3712 u32 mesh_update_count = 0;
3714 // Number of blocks in rendering range
3715 u32 blocks_in_range = 0;
3716 // Number of blocks occlusion culled
3717 u32 blocks_occlusion_culled = 0;
3718 // Number of blocks in rendering range but don't have a mesh
3719 u32 blocks_in_range_without_mesh = 0;
3720 // Blocks that had mesh that would have been drawn according to
3721 // rendering range (if max blocks limit didn't kick in)
3722 u32 blocks_would_have_drawn = 0;
3723 // Blocks that were drawn and had a mesh
3724 u32 blocks_drawn = 0;
3725 // Blocks which had a corresponding meshbuffer for this pass
3726 u32 blocks_had_pass_meshbuf = 0;
3727 // Blocks from which stuff was actually drawn
3728 u32 blocks_without_stuff = 0;
3731 Collect a set of blocks for drawing
3734 core::map<v3s16, MapBlock*> drawset;
3737 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3739 for(core::map<v2s16, MapSector*>::Iterator
3740 si = m_sectors.getIterator();
3741 si.atEnd() == false; si++)
3743 MapSector *sector = si.getNode()->getValue();
3744 v2s16 sp = sector->getPos();
3746 if(m_control.range_all == false)
3748 if(sp.X < p_blocks_min.X
3749 || sp.X > p_blocks_max.X
3750 || sp.Y < p_blocks_min.Z
3751 || sp.Y > p_blocks_max.Z)
3755 core::list< MapBlock * > sectorblocks;
3756 sector->getBlocks(sectorblocks);
3759 Loop through blocks in sector
3762 u32 sector_blocks_drawn = 0;
3764 core::list< MapBlock * >::Iterator i;
3765 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3767 MapBlock *block = *i;
3770 Compare block position to camera position, skip
3771 if not seen on display
3774 float range = 100000 * BS;
3775 if(m_control.range_all == false)
3776 range = m_control.wanted_range * BS;
3779 if(isBlockInSight(block->getPos(), camera_position,
3780 camera_direction, camera_fov,
3781 range, &d) == false)
3786 // This is ugly (spherical distance limit?)
3787 /*if(m_control.range_all == false &&
3788 d - 0.5*BS*MAP_BLOCKSIZE > range)
3795 Update expired mesh (used for day/night change)
3797 It doesn't work exactly like it should now with the
3798 tasked mesh update but whatever.
3801 bool mesh_expired = false;
3804 JMutexAutoLock lock(block->mesh_mutex);
3806 mesh_expired = block->getMeshExpired();
3808 // Mesh has not been expired and there is no mesh:
3809 // block has no content
3810 if(block->mesh == NULL && mesh_expired == false){
3811 blocks_in_range_without_mesh++;
3816 f32 faraway = BS*50;
3817 //f32 faraway = m_control.wanted_range * BS;
3820 This has to be done with the mesh_mutex unlocked
3822 // Pretty random but this should work somewhat nicely
3823 if(mesh_expired && (
3824 (mesh_update_count < 3
3825 && (d < faraway || mesh_update_count < 2)
3828 (m_control.range_all && mesh_update_count < 20)
3831 /*if(mesh_expired && mesh_update_count < 6
3832 && (d < faraway || mesh_update_count < 3))*/
3834 mesh_update_count++;
3836 // Mesh has been expired: generate new mesh
3837 //block->updateMesh(daynight_ratio);
3838 m_client->addUpdateMeshTask(block->getPos());
3840 mesh_expired = false;
3848 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3849 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3851 float stepfac = 1.1;
3852 float startoff = BS*1;
3853 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3854 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3855 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3856 u32 needed_count = 1;
3858 isOccluded(this, spn, cpn + v3s16(0,0,0),
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) &&
3868 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3869 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3870 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3871 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3872 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3873 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3874 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3875 step, stepfac, startoff, endoff, needed_count, nodemgr)
3878 blocks_occlusion_culled++;
3882 // This block is in range. Reset usage timer.
3883 block->resetUsageTimer();
3886 Ignore if mesh doesn't exist
3889 JMutexAutoLock lock(block->mesh_mutex);
3891 scene::SMesh *mesh = block->mesh;
3894 blocks_in_range_without_mesh++;
3899 // Limit block count in case of a sudden increase
3900 blocks_would_have_drawn++;
3901 if(blocks_drawn >= m_control.wanted_max_blocks
3902 && m_control.range_all == false
3903 && d > m_control.wanted_min_range * BS)
3907 drawset[block->getPos()] = block;
3909 sector_blocks_drawn++;
3912 } // foreach sectorblocks
3914 if(sector_blocks_drawn != 0)
3915 m_last_drawn_sectors[sp] = true;
3920 Draw the selected MapBlocks
3924 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3926 int timecheck_counter = 0;
3927 for(core::map<v3s16, MapBlock*>::Iterator
3928 i = drawset.getIterator();
3929 i.atEnd() == false; i++)
3932 timecheck_counter++;
3933 if(timecheck_counter > 50)
3935 timecheck_counter = 0;
3936 int time2 = time(0);
3937 if(time2 > time1 + 4)
3939 infostream<<"ClientMap::renderMap(): "
3940 "Rendering takes ages, returning."
3947 MapBlock *block = i.getNode()->getValue();
3950 Draw the faces of the block
3953 JMutexAutoLock lock(block->mesh_mutex);
3955 scene::SMesh *mesh = block->mesh;
3958 u32 c = mesh->getMeshBufferCount();
3959 bool stuff_actually_drawn = false;
3960 for(u32 i=0; i<c; i++)
3962 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3963 const video::SMaterial& material = buf->getMaterial();
3964 video::IMaterialRenderer* rnd =
3965 driver->getMaterialRenderer(material.MaterialType);
3966 bool transparent = (rnd && rnd->isTransparent());
3967 // Render transparent on transparent pass and likewise.
3968 if(transparent == is_transparent_pass)
3970 if(buf->getVertexCount() == 0)
3971 errorstream<<"Block ["<<analyze_block(block)
3972 <<"] contains an empty meshbuf"<<std::endl;
3974 This *shouldn't* hurt too much because Irrlicht
3975 doesn't change opengl textures if the old
3976 material has the same texture.
3978 driver->setMaterial(buf->getMaterial());
3979 driver->drawMeshBuffer(buf);
3980 vertex_count += buf->getVertexCount();
3982 stuff_actually_drawn = true;
3985 if(stuff_actually_drawn)
3986 blocks_had_pass_meshbuf++;
3988 blocks_without_stuff++;
3993 // Log only on solid pass because values are the same
3994 if(pass == scene::ESNRP_SOLID){
3995 g_profiler->avg("CM: blocks in range", blocks_in_range);
3996 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3997 if(blocks_in_range != 0)
3998 g_profiler->avg("CM: blocks in range without mesh (frac)",
3999 (float)blocks_in_range_without_mesh/blocks_in_range);
4000 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4003 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4004 if(blocks_had_pass_meshbuf != 0)
4005 g_profiler->avg(prefix+"meshbuffers per block",
4006 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4007 if(blocks_drawn != 0)
4008 g_profiler->avg(prefix+"empty blocks (frac)",
4009 (float)blocks_without_stuff / blocks_drawn);
4011 m_control.blocks_drawn = blocks_drawn;
4012 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4014 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4015 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4018 void ClientMap::renderPostFx()
4020 INodeDefManager *nodemgr = m_gamedef->ndef();
4022 // Sadly ISceneManager has no "post effects" render pass, in that case we
4023 // could just register for that and handle it in renderMap().
4025 m_camera_mutex.Lock();
4026 v3f camera_position = m_camera_position;
4027 m_camera_mutex.Unlock();
4029 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4031 // - If the player is in a solid node, make everything black.
4032 // - If the player is in liquid, draw a semi-transparent overlay.
4033 const ContentFeatures& features = nodemgr->get(n);
4034 video::SColor post_effect_color = features.post_effect_color;
4035 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4037 post_effect_color = video::SColor(255, 0, 0, 0);
4039 if (post_effect_color.getAlpha() != 0)
4041 // Draw a full-screen rectangle
4042 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4043 v2u32 ss = driver->getScreenSize();
4044 core::rect<s32> rect(0,0, ss.X, ss.Y);
4045 driver->draw2DRectangle(post_effect_color, rect);
4049 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4050 core::map<v3s16, MapBlock*> *affected_blocks)
4052 bool changed = false;
4054 Add it to all blocks touching it
4057 v3s16(0,0,0), // this
4058 v3s16(0,0,1), // back
4059 v3s16(0,1,0), // top
4060 v3s16(1,0,0), // right
4061 v3s16(0,0,-1), // front
4062 v3s16(0,-1,0), // bottom
4063 v3s16(-1,0,0), // left
4065 for(u16 i=0; i<7; i++)
4067 v3s16 p2 = p + dirs[i];
4068 // Block position of neighbor (or requested) node
4069 v3s16 blockpos = getNodeBlockPos(p2);
4070 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4071 if(blockref == NULL)
4073 // Relative position of requested node
4074 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4075 if(blockref->setTempMod(relpos, mod))
4080 if(changed && affected_blocks!=NULL)
4082 for(u16 i=0; i<7; i++)
4084 v3s16 p2 = p + dirs[i];
4085 // Block position of neighbor (or requested) node
4086 v3s16 blockpos = getNodeBlockPos(p2);
4087 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4088 if(blockref == NULL)
4090 affected_blocks->insert(blockpos, blockref);
4096 bool ClientMap::clearTempMod(v3s16 p,
4097 core::map<v3s16, MapBlock*> *affected_blocks)
4099 bool changed = false;
4101 v3s16(0,0,0), // this
4102 v3s16(0,0,1), // back
4103 v3s16(0,1,0), // top
4104 v3s16(1,0,0), // right
4105 v3s16(0,0,-1), // front
4106 v3s16(0,-1,0), // bottom
4107 v3s16(-1,0,0), // left
4109 for(u16 i=0; i<7; i++)
4111 v3s16 p2 = p + dirs[i];
4112 // Block position of neighbor (or requested) node
4113 v3s16 blockpos = getNodeBlockPos(p2);
4114 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4115 if(blockref == NULL)
4117 // Relative position of requested node
4118 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4119 if(blockref->clearTempMod(relpos))
4124 if(changed && affected_blocks!=NULL)
4126 for(u16 i=0; i<7; i++)
4128 v3s16 p2 = p + dirs[i];
4129 // Block position of neighbor (or requested) node
4130 v3s16 blockpos = getNodeBlockPos(p2);
4131 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4132 if(blockref == NULL)
4134 affected_blocks->insert(blockpos, blockref);
4140 void ClientMap::expireMeshes(bool only_daynight_diffed)
4142 TimeTaker timer("expireMeshes()");
4144 core::map<v2s16, MapSector*>::Iterator si;
4145 si = m_sectors.getIterator();
4146 for(; si.atEnd() == false; si++)
4148 MapSector *sector = si.getNode()->getValue();
4150 core::list< MapBlock * > sectorblocks;
4151 sector->getBlocks(sectorblocks);
4153 core::list< MapBlock * >::Iterator i;
4154 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4156 MapBlock *block = *i;
4158 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4164 JMutexAutoLock lock(block->mesh_mutex);
4165 if(block->mesh != NULL)
4167 /*block->mesh->drop();
4168 block->mesh = NULL;*/
4169 block->setMeshExpired(true);
4176 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4178 assert(mapType() == MAPTYPE_CLIENT);
4181 v3s16 p = blockpos + v3s16(0,0,0);
4182 MapBlock *b = getBlockNoCreate(p);
4183 b->updateMesh(daynight_ratio);
4184 //b->setMeshExpired(true);
4186 catch(InvalidPositionException &e){}
4189 v3s16 p = blockpos + v3s16(-1,0,0);
4190 MapBlock *b = getBlockNoCreate(p);
4191 b->updateMesh(daynight_ratio);
4192 //b->setMeshExpired(true);
4194 catch(InvalidPositionException &e){}
4196 v3s16 p = blockpos + v3s16(0,-1,0);
4197 MapBlock *b = getBlockNoCreate(p);
4198 b->updateMesh(daynight_ratio);
4199 //b->setMeshExpired(true);
4201 catch(InvalidPositionException &e){}
4203 v3s16 p = blockpos + v3s16(0,0,-1);
4204 MapBlock *b = getBlockNoCreate(p);
4205 b->updateMesh(daynight_ratio);
4206 //b->setMeshExpired(true);
4208 catch(InvalidPositionException &e){}
4213 Update mesh of block in which the node is, and if the node is at the
4214 leading edge, update the appropriate leading blocks too.
4216 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4224 v3s16 blockposes[4];
4225 for(u32 i=0; i<4; i++)
4227 v3s16 np = nodepos + dirs[i];
4228 blockposes[i] = getNodeBlockPos(np);
4229 // Don't update mesh of block if it has been done already
4230 bool already_updated = false;
4231 for(u32 j=0; j<i; j++)
4233 if(blockposes[j] == blockposes[i])
4235 already_updated = true;
4242 MapBlock *b = getBlockNoCreate(blockposes[i]);
4243 b->updateMesh(daynight_ratio);
4248 void ClientMap::PrintInfo(std::ostream &out)
4259 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4264 MapVoxelManipulator::~MapVoxelManipulator()
4266 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4270 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4272 TimeTaker timer1("emerge", &emerge_time);
4274 // Units of these are MapBlocks
4275 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4276 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4278 VoxelArea block_area_nodes
4279 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4281 addArea(block_area_nodes);
4283 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4284 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4285 for(s32 x=p_min.X; x<=p_max.X; x++)
4288 core::map<v3s16, bool>::Node *n;
4289 n = m_loaded_blocks.find(p);
4293 bool block_data_inexistent = false;
4296 TimeTaker timer1("emerge load", &emerge_load_time);
4298 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4299 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4301 a.print(infostream);
4302 infostream<<std::endl;*/
4304 MapBlock *block = m_map->getBlockNoCreate(p);
4305 if(block->isDummy())
4306 block_data_inexistent = true;
4308 block->copyTo(*this);
4310 catch(InvalidPositionException &e)
4312 block_data_inexistent = true;
4315 if(block_data_inexistent)
4317 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4318 // Fill with VOXELFLAG_INEXISTENT
4319 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4320 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4322 s32 i = m_area.index(a.MinEdge.X,y,z);
4323 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4327 m_loaded_blocks.insert(p, !block_data_inexistent);
4330 //infostream<<"emerge done"<<std::endl;
4334 SUGG: Add an option to only update eg. water and air nodes.
4335 This will make it interfere less with important stuff if
4338 void MapVoxelManipulator::blitBack
4339 (core::map<v3s16, MapBlock*> & modified_blocks)
4341 if(m_area.getExtent() == v3s16(0,0,0))
4344 //TimeTaker timer1("blitBack");
4346 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4347 <<m_loaded_blocks.size()<<std::endl;*/
4350 Initialize block cache
4352 v3s16 blockpos_last;
4353 MapBlock *block = NULL;
4354 bool block_checked_in_modified = false;
4356 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4357 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4358 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4362 u8 f = m_flags[m_area.index(p)];
4363 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4366 MapNode &n = m_data[m_area.index(p)];
4368 v3s16 blockpos = getNodeBlockPos(p);
4373 if(block == NULL || blockpos != blockpos_last){
4374 block = m_map->getBlockNoCreate(blockpos);
4375 blockpos_last = blockpos;
4376 block_checked_in_modified = false;
4379 // Calculate relative position in block
4380 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4382 // Don't continue if nothing has changed here
4383 if(block->getNode(relpos) == n)
4386 //m_map->setNode(m_area.MinEdge + p, n);
4387 block->setNode(relpos, n);
4390 Make sure block is in modified_blocks
4392 if(block_checked_in_modified == false)
4394 modified_blocks[blockpos] = block;
4395 block_checked_in_modified = true;
4398 catch(InvalidPositionException &e)
4404 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4405 MapVoxelManipulator(map),
4406 m_create_area(false)
4410 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4414 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4416 // Just create the area so that it can be pointed to
4417 VoxelManipulator::emerge(a, caller_id);
4420 void ManualMapVoxelManipulator::initialEmerge(
4421 v3s16 blockpos_min, v3s16 blockpos_max)
4423 TimeTaker timer1("initialEmerge", &emerge_time);
4425 // Units of these are MapBlocks
4426 v3s16 p_min = blockpos_min;
4427 v3s16 p_max = blockpos_max;
4429 VoxelArea block_area_nodes
4430 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4432 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4435 infostream<<"initialEmerge: area: ";
4436 block_area_nodes.print(infostream);
4437 infostream<<" ("<<size_MB<<"MB)";
4438 infostream<<std::endl;
4441 addArea(block_area_nodes);
4443 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4444 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4445 for(s32 x=p_min.X; x<=p_max.X; x++)
4448 core::map<v3s16, bool>::Node *n;
4449 n = m_loaded_blocks.find(p);
4453 bool block_data_inexistent = false;
4456 TimeTaker timer1("emerge load", &emerge_load_time);
4458 MapBlock *block = m_map->getBlockNoCreate(p);
4459 if(block->isDummy())
4460 block_data_inexistent = true;
4462 block->copyTo(*this);
4464 catch(InvalidPositionException &e)
4466 block_data_inexistent = true;
4469 if(block_data_inexistent)
4472 Mark area inexistent
4474 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4475 // Fill with VOXELFLAG_INEXISTENT
4476 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4477 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4479 s32 i = m_area.index(a.MinEdge.X,y,z);
4480 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4484 m_loaded_blocks.insert(p, !block_data_inexistent);
4488 void ManualMapVoxelManipulator::blitBackAll(
4489 core::map<v3s16, MapBlock*> * modified_blocks)
4491 if(m_area.getExtent() == v3s16(0,0,0))
4495 Copy data of all blocks
4497 for(core::map<v3s16, bool>::Iterator
4498 i = m_loaded_blocks.getIterator();
4499 i.atEnd() == false; i++)
4501 v3s16 p = i.getNode()->getKey();
4502 bool existed = i.getNode()->getValue();
4503 if(existed == false)
4505 // The Great Bug was found using this
4506 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4507 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4511 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4514 infostream<<"WARNING: "<<__FUNCTION_NAME
4515 <<": got NULL block "
4516 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4521 block->copyFrom(*this);
4524 modified_blocks->insert(p, block);