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 PrintInfo(infostream); // ServerMap/ClientMap:
1467 infostream<<"Blocks modified by: "<<std::endl;
1468 modprofiler.print(infostream);
1472 void Map::deleteSectors(core::list<v2s16> &list)
1474 core::list<v2s16>::Iterator j;
1475 for(j=list.begin(); j!=list.end(); j++)
1477 MapSector *sector = m_sectors[*j];
1478 // If sector is in sector cache, remove it from there
1479 if(m_sector_cache == sector)
1480 m_sector_cache = NULL;
1481 // Remove from map and delete
1482 m_sectors.remove(*j);
1488 void Map::unloadUnusedData(float timeout,
1489 core::list<v3s16> *deleted_blocks)
1491 core::list<v2s16> sector_deletion_queue;
1492 u32 deleted_blocks_count = 0;
1493 u32 saved_blocks_count = 0;
1495 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1496 for(; si.atEnd() == false; si++)
1498 MapSector *sector = si.getNode()->getValue();
1500 bool all_blocks_deleted = true;
1502 core::list<MapBlock*> blocks;
1503 sector->getBlocks(blocks);
1504 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1505 i != blocks.end(); i++)
1507 MapBlock *block = (*i);
1509 if(block->getUsageTimer() > timeout)
1512 if(block->getModified() != MOD_STATE_CLEAN)
1515 saved_blocks_count++;
1517 // Delete from memory
1518 sector->deleteBlock(block);
1519 deleted_blocks_count++;
1523 all_blocks_deleted = false;
1527 if(all_blocks_deleted)
1529 sector_deletion_queue.push_back(si.getNode()->getKey());
1533 deleteSectors(sector_deletion_queue);
1535 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1536 <<", of which "<<saved_blocks_count<<" were wr."
1539 //return sector_deletion_queue.getSize();
1540 //return deleted_blocks_count;
1544 void Map::PrintInfo(std::ostream &out)
1549 #define WATER_DROP_BOOST 4
1553 NEIGHBOR_SAME_LEVEL,
1556 struct NodeNeighbor {
1562 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1564 INodeDefManager *nodemgr = m_gamedef->ndef();
1566 DSTACK(__FUNCTION_NAME);
1567 //TimeTaker timer("transformLiquids()");
1570 u32 initial_size = m_transforming_liquid.size();
1572 /*if(initial_size != 0)
1573 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1575 // list of nodes that due to viscosity have not reached their max level height
1576 UniqueQueue<v3s16> must_reflow;
1578 // List of MapBlocks that will require a lighting update (due to lava)
1579 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1581 while(m_transforming_liquid.size() != 0)
1583 // This should be done here so that it is done when continue is used
1584 if(loopcount >= initial_size * 3)
1589 Get a queued transforming liquid node
1591 v3s16 p0 = m_transforming_liquid.pop_front();
1593 MapNode n0 = getNodeNoEx(p0);
1596 Collect information about current node
1598 s8 liquid_level = -1;
1599 u8 liquid_kind = CONTENT_IGNORE;
1600 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1601 switch (liquid_type) {
1603 liquid_level = LIQUID_LEVEL_SOURCE;
1604 liquid_kind = nodemgr->get(n0).liquid_alternative_flowing;
1606 case LIQUID_FLOWING:
1607 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1608 liquid_kind = n0.getContent();
1611 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1612 // continue with the next node.
1613 if (n0.getContent() != CONTENT_AIR)
1615 liquid_kind = CONTENT_AIR;
1620 Collect information about the environment
1622 const v3s16 *dirs = g_6dirs;
1623 NodeNeighbor sources[6]; // surrounding sources
1624 int num_sources = 0;
1625 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1627 NodeNeighbor airs[6]; // surrounding air
1629 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1630 int num_neutrals = 0;
1631 bool flowing_down = false;
1632 for (u16 i = 0; i < 6; i++) {
1633 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1636 nt = NEIGHBOR_UPPER;
1639 nt = NEIGHBOR_LOWER;
1642 v3s16 npos = p0 + dirs[i];
1643 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1644 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1646 if (nb.n.getContent() == CONTENT_AIR) {
1647 airs[num_airs++] = nb;
1648 // if the current node is a water source the neighbor
1649 // should be enqueded for transformation regardless of whether the
1650 // current node changes or not.
1651 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1652 m_transforming_liquid.push_back(npos);
1653 // if the current node happens to be a flowing node, it will start to flow down here.
1654 if (nb.t == NEIGHBOR_LOWER) {
1655 flowing_down = true;
1658 neutrals[num_neutrals++] = nb;
1662 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1663 if (liquid_kind == CONTENT_AIR)
1664 liquid_kind = nodemgr->get(nb.n.getContent()).liquid_alternative_flowing;
1665 if (nodemgr->get(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1666 neutrals[num_neutrals++] = nb;
1668 sources[num_sources++] = nb;
1671 case LIQUID_FLOWING:
1672 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1673 if (liquid_kind == CONTENT_AIR)
1674 liquid_kind = nodemgr->get(nb.n.getContent()).liquid_alternative_flowing;
1675 if (nodemgr->get(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1676 neutrals[num_neutrals++] = nb;
1678 flows[num_flows++] = nb;
1679 if (nb.t == NEIGHBOR_LOWER)
1680 flowing_down = true;
1687 decide on the type (and possibly level) of the current node
1689 content_t new_node_content;
1690 s8 new_node_level = -1;
1691 s8 max_node_level = -1;
1692 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1693 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1694 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1695 // it's perfectly safe to use liquid_kind here to determine the new node content.
1696 new_node_content = nodemgr->get(liquid_kind).liquid_alternative_source;
1697 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1698 // liquid_kind is set properly, see above
1699 new_node_content = liquid_kind;
1700 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1702 // no surrounding sources, so get the maximum level that can flow into this node
1703 for (u16 i = 0; i < num_flows; i++) {
1704 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1705 switch (flows[i].t) {
1706 case NEIGHBOR_UPPER:
1707 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1708 max_node_level = LIQUID_LEVEL_MAX;
1709 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1710 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1711 } else if (nb_liquid_level > max_node_level)
1712 max_node_level = nb_liquid_level;
1714 case NEIGHBOR_LOWER:
1716 case NEIGHBOR_SAME_LEVEL:
1717 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1718 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1719 max_node_level = nb_liquid_level - 1;
1725 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1726 if (viscosity > 1 && max_node_level != liquid_level) {
1727 // amount to gain, limited by viscosity
1728 // must be at least 1 in absolute value
1729 s8 level_inc = max_node_level - liquid_level;
1730 if (level_inc < -viscosity || level_inc > viscosity)
1731 new_node_level = liquid_level + level_inc/viscosity;
1732 else if (level_inc < 0)
1733 new_node_level = liquid_level - 1;
1734 else if (level_inc > 0)
1735 new_node_level = liquid_level + 1;
1736 if (new_node_level != max_node_level)
1737 must_reflow.push_back(p0);
1739 new_node_level = max_node_level;
1741 if (new_node_level >= 0)
1742 new_node_content = liquid_kind;
1744 new_node_content = CONTENT_AIR;
1749 check if anything has changed. if not, just continue with the next node.
1751 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1752 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1753 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1759 update the current node
1761 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1762 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1763 // set level to last 3 bits, flowing down bit to 4th bit
1764 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1766 // set the liquid level and flow bit to 0
1767 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1769 n0.setContent(new_node_content);
1771 v3s16 blockpos = getNodeBlockPos(p0);
1772 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1774 modified_blocks.insert(blockpos, block);
1775 // If node emits light, MapBlock requires lighting update
1776 if(nodemgr->get(n0).light_source != 0)
1777 lighting_modified_blocks[block->getPos()] = block;
1781 enqueue neighbors for update if neccessary
1783 switch (nodemgr->get(n0.getContent()).liquid_type) {
1785 case LIQUID_FLOWING:
1786 // make sure source flows into all neighboring nodes
1787 for (u16 i = 0; i < num_flows; i++)
1788 if (flows[i].t != NEIGHBOR_UPPER)
1789 m_transforming_liquid.push_back(flows[i].p);
1790 for (u16 i = 0; i < num_airs; i++)
1791 if (airs[i].t != NEIGHBOR_UPPER)
1792 m_transforming_liquid.push_back(airs[i].p);
1795 // this flow has turned to air; neighboring flows might need to do the same
1796 for (u16 i = 0; i < num_flows; i++)
1797 m_transforming_liquid.push_back(flows[i].p);
1801 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1802 while (must_reflow.size() > 0)
1803 m_transforming_liquid.push_back(must_reflow.pop_front());
1804 updateLighting(lighting_modified_blocks, modified_blocks);
1807 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1809 v3s16 blockpos = getNodeBlockPos(p);
1810 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1811 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1813 infostream<<"Map::getNodeMetadata(): Need to emerge "
1814 <<PP(blockpos)<<std::endl;
1815 block = emergeBlock(blockpos, false);
1819 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1823 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1827 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1829 v3s16 blockpos = getNodeBlockPos(p);
1830 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1831 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1833 infostream<<"Map::setNodeMetadata(): Need to emerge "
1834 <<PP(blockpos)<<std::endl;
1835 block = emergeBlock(blockpos, false);
1839 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1843 block->m_node_metadata->set(p_rel, meta);
1846 void Map::removeNodeMetadata(v3s16 p)
1848 v3s16 blockpos = getNodeBlockPos(p);
1849 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1850 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1853 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1857 block->m_node_metadata->remove(p_rel);
1860 void Map::nodeMetadataStep(float dtime,
1861 core::map<v3s16, MapBlock*> &changed_blocks)
1865 Currently there is no way to ensure that all the necessary
1866 blocks are loaded when this is run. (They might get unloaded)
1867 NOTE: ^- Actually, that might not be so. In a quick test it
1868 reloaded a block with a furnace when I walked back to it from
1871 core::map<v2s16, MapSector*>::Iterator si;
1872 si = m_sectors.getIterator();
1873 for(; si.atEnd() == false; si++)
1875 MapSector *sector = si.getNode()->getValue();
1876 core::list< MapBlock * > sectorblocks;
1877 sector->getBlocks(sectorblocks);
1878 core::list< MapBlock * >::Iterator i;
1879 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1881 MapBlock *block = *i;
1882 bool changed = block->m_node_metadata->step(dtime);
1884 changed_blocks[block->getPos()] = block;
1893 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1894 Map(dout_server, gamedef),
1896 m_map_metadata_changed(true),
1898 m_database_read(NULL),
1899 m_database_write(NULL)
1901 infostream<<__FUNCTION_NAME<<std::endl;
1903 //m_chunksize = 8; // Takes a few seconds
1905 if (g_settings->get("fixed_map_seed").empty())
1907 m_seed = (((u64)(myrand()%0xffff)<<0)
1908 + ((u64)(myrand()%0xffff)<<16)
1909 + ((u64)(myrand()%0xffff)<<32)
1910 + ((u64)(myrand()%0xffff)<<48));
1914 m_seed = g_settings->getU64("fixed_map_seed");
1918 Experimental and debug stuff
1925 Try to load map; if not found, create a new one.
1928 m_savedir = savedir;
1929 m_map_saving_enabled = false;
1933 // If directory exists, check contents and load if possible
1934 if(fs::PathExists(m_savedir))
1936 // If directory is empty, it is safe to save into it.
1937 if(fs::GetDirListing(m_savedir).size() == 0)
1939 infostream<<"Server: Empty save directory is valid."
1941 m_map_saving_enabled = true;
1946 // Load map metadata (seed, chunksize)
1949 catch(FileNotGoodException &e){
1950 infostream<<"WARNING: Could not load map metadata"
1951 //<<" Disabling chunk-based generator."
1957 // Load chunk metadata
1960 catch(FileNotGoodException &e){
1961 infostream<<"WARNING: Could not load chunk metadata."
1962 <<" Disabling chunk-based generator."
1967 /*infostream<<"Server: Successfully loaded chunk "
1968 "metadata and sector (0,0) from "<<savedir<<
1969 ", assuming valid save directory."
1972 infostream<<"Server: Successfully loaded map "
1973 <<"and chunk metadata from "<<savedir
1974 <<", assuming valid save directory."
1977 m_map_saving_enabled = true;
1978 // Map loaded, not creating new one
1982 // If directory doesn't exist, it is safe to save to it
1984 m_map_saving_enabled = true;
1987 catch(std::exception &e)
1989 infostream<<"WARNING: Server: Failed to load map from "<<savedir
1990 <<", exception: "<<e.what()<<std::endl;
1991 infostream<<"Please remove the map or fix it."<<std::endl;
1992 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1995 infostream<<"Initializing new map."<<std::endl;
1997 // Create zero sector
1998 emergeSector(v2s16(0,0));
2000 // Initially write whole map
2004 ServerMap::~ServerMap()
2006 infostream<<__FUNCTION_NAME<<std::endl;
2010 if(m_map_saving_enabled)
2012 // Save only changed parts
2014 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2018 infostream<<"Server: map not saved"<<std::endl;
2021 catch(std::exception &e)
2023 infostream<<"Server: Failed to save map to "<<m_savedir
2024 <<", exception: "<<e.what()<<std::endl;
2028 Close database if it was opened
2031 sqlite3_finalize(m_database_read);
2032 if(m_database_write)
2033 sqlite3_finalize(m_database_write);
2035 sqlite3_close(m_database);
2041 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2042 for(; i.atEnd() == false; i++)
2044 MapChunk *chunk = i.getNode()->getValue();
2050 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2052 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2053 if(enable_mapgen_debug_info)
2054 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2055 <<blockpos.Z<<")"<<std::endl;
2057 // Do nothing if not inside limits (+-1 because of neighbors)
2058 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2059 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2065 data->no_op = false;
2066 data->seed = m_seed;
2067 data->blockpos = blockpos;
2068 data->nodedef = m_gamedef->ndef();
2071 Create the whole area of this and the neighboring blocks
2074 //TimeTaker timer("initBlockMake() create area");
2076 for(s16 x=-1; x<=1; x++)
2077 for(s16 z=-1; z<=1; z++)
2079 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2080 // Sector metadata is loaded from disk if not already loaded.
2081 ServerMapSector *sector = createSector(sectorpos);
2084 for(s16 y=-1; y<=1; y++)
2086 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2087 //MapBlock *block = createBlock(p);
2088 // 1) get from memory, 2) load from disk
2089 MapBlock *block = emergeBlock(p, false);
2090 // 3) create a blank one
2093 block = createBlock(p);
2096 Block gets sunlight if this is true.
2098 Refer to the map generator heuristics.
2100 bool ug = mapgen::block_is_underground(data->seed, p);
2101 block->setIsUnderground(ug);
2104 // Lighting will not be valid after make_chunk is called
2105 block->setLightingExpired(true);
2106 // Lighting will be calculated
2107 //block->setLightingExpired(false);
2113 Now we have a big empty area.
2115 Make a ManualMapVoxelManipulator that contains this and the
2119 // The area that contains this block and it's neighbors
2120 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2121 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2123 data->vmanip = new ManualMapVoxelManipulator(this);
2124 //data->vmanip->setMap(this);
2128 //TimeTaker timer("initBlockMake() initialEmerge");
2129 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2132 // Data is ready now.
2135 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2136 core::map<v3s16, MapBlock*> &changed_blocks)
2138 v3s16 blockpos = data->blockpos;
2139 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2140 <<blockpos.Z<<")"<<std::endl;*/
2144 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2148 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2150 /*infostream<<"Resulting vmanip:"<<std::endl;
2151 data->vmanip.print(infostream);*/
2154 Blit generated stuff to map
2155 NOTE: blitBackAll adds nearly everything to changed_blocks
2159 //TimeTaker timer("finishBlockMake() blitBackAll");
2160 data->vmanip->blitBackAll(&changed_blocks);
2163 if(enable_mapgen_debug_info)
2164 infostream<<"finishBlockMake: changed_blocks.size()="
2165 <<changed_blocks.size()<<std::endl;
2168 Copy transforming liquid information
2170 while(data->transforming_liquid.size() > 0)
2172 v3s16 p = data->transforming_liquid.pop_front();
2173 m_transforming_liquid.push_back(p);
2179 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2183 Set is_underground flag for lighting with sunlight.
2185 Refer to map generator heuristics.
2187 NOTE: This is done in initChunkMake
2189 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2193 Add sunlight to central block.
2194 This makes in-dark-spawning monsters to not flood the whole thing.
2195 Do not spread the light, though.
2197 /*core::map<v3s16, bool> light_sources;
2198 bool black_air_left = false;
2199 block->propagateSunlight(light_sources, true, &black_air_left);*/
2202 NOTE: Lighting and object adding shouldn't really be here, but
2203 lighting is a bit tricky to move properly to makeBlock.
2204 TODO: Do this the right way anyway, that is, move it to makeBlock.
2205 - There needs to be some way for makeBlock to report back if
2206 the lighting update is going further down because of the
2207 new block blocking light
2212 NOTE: This takes ~60ms, TODO: Investigate why
2215 TimeTaker t("finishBlockMake lighting update");
2217 core::map<v3s16, MapBlock*> lighting_update_blocks;
2220 lighting_update_blocks.insert(block->getPos(), block);
2225 v3s16 p = block->getPos()+v3s16(x,1,z);
2226 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2230 // All modified blocks
2231 // NOTE: Should this be done? If this is not done, then the lighting
2232 // of the others will be updated in a different place, one by one, i
2233 // think... or they might not? Well, at least they are left marked as
2234 // "lighting expired"; it seems that is not handled at all anywhere,
2235 // so enabling this will slow it down A LOT because otherwise it
2236 // would not do this at all. This causes the black trees.
2237 for(core::map<v3s16, MapBlock*>::Iterator
2238 i = changed_blocks.getIterator();
2239 i.atEnd() == false; i++)
2241 lighting_update_blocks.insert(i.getNode()->getKey(),
2242 i.getNode()->getValue());
2244 /*// Also force-add all the upmost blocks for proper sunlight
2245 for(s16 x=-1; x<=1; x++)
2246 for(s16 z=-1; z<=1; z++)
2248 v3s16 p = block->getPos()+v3s16(x,1,z);
2249 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2252 updateLighting(lighting_update_blocks, changed_blocks);
2255 Set lighting to non-expired state in all of them.
2256 This is cheating, but it is not fast enough if all of them
2257 would actually be updated.
2259 for(s16 x=-1; x<=1; x++)
2260 for(s16 y=-1; y<=1; y++)
2261 for(s16 z=-1; z<=1; z++)
2263 v3s16 p = block->getPos()+v3s16(x,y,z);
2264 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2267 if(enable_mapgen_debug_info == false)
2268 t.stop(true); // Hide output
2272 Add random objects to block
2274 mapgen::add_random_objects(block);
2277 Go through changed blocks
2279 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2280 i.atEnd() == false; i++)
2282 MapBlock *block = i.getNode()->getValue();
2285 Update day/night difference cache of the MapBlocks
2287 block->updateDayNightDiff();
2289 Set block as modified
2291 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2292 "finishBlockMake updateDayNightDiff");
2296 Set central block as generated
2298 block->setGenerated(true);
2301 Save changed parts of map
2302 NOTE: Will be saved later.
2306 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2307 <<blockpos.Z<<")"<<std::endl;*/
2309 if(enable_mapgen_debug_info)
2312 Analyze resulting blocks
2314 for(s16 x=-1; x<=1; x++)
2315 for(s16 y=-1; y<=1; y++)
2316 for(s16 z=-1; z<=1; z++)
2318 v3s16 p = block->getPos()+v3s16(x,y,z);
2319 MapBlock *block = getBlockNoCreateNoEx(p);
2321 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2322 infostream<<"Generated "<<spos<<": "
2323 <<analyze_block(block)<<std::endl;
2331 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2333 DSTACKF("%s: p2d=(%d,%d)",
2338 Check if it exists already in memory
2340 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2345 Try to load it from disk (with blocks)
2347 //if(loadSectorFull(p2d) == true)
2350 Try to load metadata from disk
2353 if(loadSectorMeta(p2d) == true)
2355 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2358 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2359 throw InvalidPositionException("");
2365 Do not create over-limit
2367 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2368 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2369 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2370 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2371 throw InvalidPositionException("createSector(): pos. over limit");
2374 Generate blank sector
2377 sector = new ServerMapSector(this, p2d, m_gamedef);
2379 // Sector position on map in nodes
2380 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2385 m_sectors.insert(p2d, sector);
2391 This is a quick-hand function for calling makeBlock().
2393 MapBlock * ServerMap::generateBlock(
2395 core::map<v3s16, MapBlock*> &modified_blocks
2398 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2400 /*infostream<<"generateBlock(): "
2401 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2404 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2406 TimeTaker timer("generateBlock");
2408 //MapBlock *block = original_dummy;
2410 v2s16 p2d(p.X, p.Z);
2411 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2414 Do not generate over-limit
2416 if(blockpos_over_limit(p))
2418 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2419 throw InvalidPositionException("generateBlock(): pos. over limit");
2423 Create block make data
2425 mapgen::BlockMakeData data;
2426 initBlockMake(&data, p);
2432 TimeTaker t("mapgen::make_block()");
2433 mapgen::make_block(&data);
2435 if(enable_mapgen_debug_info == false)
2436 t.stop(true); // Hide output
2440 Blit data back on map, update lighting, add mobs and whatever this does
2442 finishBlockMake(&data, modified_blocks);
2447 MapBlock *block = getBlockNoCreateNoEx(p);
2455 bool erroneus_content = false;
2456 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2457 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2458 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2461 MapNode n = block->getNode(p);
2462 if(n.getContent() == CONTENT_IGNORE)
2464 infostream<<"CONTENT_IGNORE at "
2465 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2467 erroneus_content = true;
2471 if(erroneus_content)
2480 Generate a completely empty block
2484 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2485 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2487 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2490 n.setContent(CONTENT_AIR);
2491 block->setNode(v3s16(x0,y0,z0), n);
2497 if(enable_mapgen_debug_info == false)
2498 timer.stop(true); // Hide output
2503 MapBlock * ServerMap::createBlock(v3s16 p)
2505 DSTACKF("%s: p=(%d,%d,%d)",
2506 __FUNCTION_NAME, p.X, p.Y, p.Z);
2509 Do not create over-limit
2511 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2512 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2513 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2514 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2515 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2516 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2517 throw InvalidPositionException("createBlock(): pos. over limit");
2519 v2s16 p2d(p.X, p.Z);
2522 This will create or load a sector if not found in memory.
2523 If block exists on disk, it will be loaded.
2525 NOTE: On old save formats, this will be slow, as it generates
2526 lighting on blocks for them.
2528 ServerMapSector *sector;
2530 sector = (ServerMapSector*)createSector(p2d);
2531 assert(sector->getId() == MAPSECTOR_SERVER);
2533 catch(InvalidPositionException &e)
2535 infostream<<"createBlock: createSector() failed"<<std::endl;
2539 NOTE: This should not be done, or at least the exception
2540 should not be passed on as std::exception, because it
2541 won't be catched at all.
2543 /*catch(std::exception &e)
2545 infostream<<"createBlock: createSector() failed: "
2546 <<e.what()<<std::endl;
2551 Try to get a block from the sector
2554 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2557 if(block->isDummy())
2562 block = sector->createBlankBlock(block_y);
2566 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2568 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2570 p.X, p.Y, p.Z, allow_generate);
2573 MapBlock *block = getBlockNoCreateNoEx(p);
2574 if(block && block->isDummy() == false)
2579 MapBlock *block = loadBlock(p);
2586 core::map<v3s16, MapBlock*> modified_blocks;
2587 MapBlock *block = generateBlock(p, modified_blocks);
2591 event.type = MEET_OTHER;
2594 // Copy modified_blocks to event
2595 for(core::map<v3s16, MapBlock*>::Iterator
2596 i = modified_blocks.getIterator();
2597 i.atEnd()==false; i++)
2599 event.modified_blocks.insert(i.getNode()->getKey(), false);
2603 dispatchEvent(&event);
2612 s16 ServerMap::findGroundLevel(v2s16 p2d)
2616 Uh, just do something random...
2618 // Find existing map from top to down
2621 v3s16 p(p2d.X, max, p2d.Y);
2622 for(; p.Y>min; p.Y--)
2624 MapNode n = getNodeNoEx(p);
2625 if(n.getContent() != CONTENT_IGNORE)
2630 // If this node is not air, go to plan b
2631 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2633 // Search existing walkable and return it
2634 for(; p.Y>min; p.Y--)
2636 MapNode n = getNodeNoEx(p);
2637 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2646 Determine from map generator noise functions
2649 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2652 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2653 //return (s16)level;
2656 void ServerMap::createDatabase() {
2659 e = sqlite3_exec(m_database,
2660 "CREATE TABLE IF NOT EXISTS `blocks` ("
2661 "`pos` INT NOT NULL PRIMARY KEY,"
2664 , NULL, NULL, NULL);
2665 if(e == SQLITE_ABORT)
2666 throw FileNotGoodException("Could not create database structure");
2668 infostream<<"Server: Database structure was created";
2671 void ServerMap::verifyDatabase() {
2676 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2677 bool needs_create = false;
2681 Open the database connection
2684 createDirs(m_savedir);
2686 if(!fs::PathExists(dbp))
2687 needs_create = true;
2689 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2690 if(d != SQLITE_OK) {
2691 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2692 throw FileNotGoodException("Cannot open database file");
2698 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2699 if(d != SQLITE_OK) {
2700 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2701 throw FileNotGoodException("Cannot prepare read statement");
2704 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2705 if(d != SQLITE_OK) {
2706 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2707 throw FileNotGoodException("Cannot prepare write statement");
2710 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2711 if(d != SQLITE_OK) {
2712 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2713 throw FileNotGoodException("Cannot prepare read statement");
2716 infostream<<"Server: Database opened"<<std::endl;
2720 bool ServerMap::loadFromFolders() {
2721 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2726 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2727 return (sqlite3_int64)pos.Z*16777216 +
2728 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2731 void ServerMap::createDirs(std::string path)
2733 if(fs::CreateAllDirs(path) == false)
2735 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2736 <<"\""<<path<<"\""<<std::endl;
2737 throw BaseException("ServerMap failed to create directory");
2741 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2747 snprintf(cc, 9, "%.4x%.4x",
2748 (unsigned int)pos.X&0xffff,
2749 (unsigned int)pos.Y&0xffff);
2751 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2753 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2754 (unsigned int)pos.X&0xfff,
2755 (unsigned int)pos.Y&0xfff);
2757 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2763 v2s16 ServerMap::getSectorPos(std::string dirname)
2767 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2768 assert(spos != std::string::npos);
2769 if(dirname.size() - spos == 8)
2772 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2774 else if(dirname.size() - spos == 3)
2777 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2778 // Sign-extend the 12 bit values up to 16 bits...
2779 if(x&0x800) x|=0xF000;
2780 if(y&0x800) y|=0xF000;
2787 v2s16 pos((s16)x, (s16)y);
2791 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2793 v2s16 p2d = getSectorPos(sectordir);
2795 if(blockfile.size() != 4){
2796 throw InvalidFilenameException("Invalid block filename");
2799 int r = sscanf(blockfile.c_str(), "%4x", &y);
2801 throw InvalidFilenameException("Invalid block filename");
2802 return v3s16(p2d.X, y, p2d.Y);
2805 std::string ServerMap::getBlockFilename(v3s16 p)
2808 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2812 void ServerMap::save(bool only_changed)
2814 DSTACK(__FUNCTION_NAME);
2815 if(m_map_saving_enabled == false)
2817 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2821 if(only_changed == false)
2822 infostream<<"ServerMap: Saving whole map, this can take time."
2825 if(only_changed == false || m_map_metadata_changed)
2830 // Profile modified reasons
2831 Profiler modprofiler;
2833 u32 sector_meta_count = 0;
2834 u32 block_count = 0;
2835 u32 block_count_all = 0; // Number of blocks in memory
2838 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2839 for(; i.atEnd() == false; i++)
2841 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2842 assert(sector->getId() == MAPSECTOR_SERVER);
2844 if(sector->differs_from_disk || only_changed == false)
2846 saveSectorMeta(sector);
2847 sector_meta_count++;
2849 core::list<MapBlock*> blocks;
2850 sector->getBlocks(blocks);
2851 core::list<MapBlock*>::Iterator j;
2853 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2854 for(j=blocks.begin(); j!=blocks.end(); j++)
2856 MapBlock *block = *j;
2860 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2861 || only_changed == false)
2863 modprofiler.add(block->getModifiedReason(), 1);
2867 /*infostream<<"ServerMap: Written block ("
2868 <<block->getPos().X<<","
2869 <<block->getPos().Y<<","
2870 <<block->getPos().Z<<")"
2873 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
2879 Only print if something happened or saved whole map
2881 if(only_changed == false || sector_meta_count != 0
2882 || block_count != 0)
2884 infostream<<"ServerMap: Written: "
2885 <<sector_meta_count<<" sector metadata files, "
2886 <<block_count<<" block files"
2887 <<", "<<block_count_all<<" blocks in memory."
2889 PrintInfo(infostream); // ServerMap/ClientMap:
2890 infostream<<"Blocks modified by: "<<std::endl;
2891 modprofiler.print(infostream);
2895 static s32 unsignedToSigned(s32 i, s32 max_positive)
2897 if(i < max_positive)
2900 return i - 2*max_positive;
2903 // modulo of a negative number does not work consistently in C
2904 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2908 return mod - ((-i) % mod);
2911 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2913 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2915 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2917 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2918 return v3s16(x,y,z);
2921 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2923 if(loadFromFolders()){
2924 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2925 <<"all blocks that are stored in flat files"<<std::endl;
2931 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2933 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2934 v3s16 p = getIntegerAsBlock(block_i);
2935 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2941 void ServerMap::saveMapMeta()
2943 DSTACK(__FUNCTION_NAME);
2945 infostream<<"ServerMap::saveMapMeta(): "
2949 createDirs(m_savedir);
2951 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2952 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2953 if(os.good() == false)
2955 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2956 <<"could not open"<<fullpath<<std::endl;
2957 throw FileNotGoodException("Cannot open chunk metadata");
2961 params.setU64("seed", m_seed);
2963 params.writeLines(os);
2965 os<<"[end_of_params]\n";
2967 m_map_metadata_changed = false;
2970 void ServerMap::loadMapMeta()
2972 DSTACK(__FUNCTION_NAME);
2974 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2977 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2978 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2979 if(is.good() == false)
2981 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2982 <<"could not open"<<fullpath<<std::endl;
2983 throw FileNotGoodException("Cannot open map metadata");
2991 throw SerializationError
2992 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2994 std::getline(is, line);
2995 std::string trimmedline = trim(line);
2996 if(trimmedline == "[end_of_params]")
2998 params.parseConfigLine(line);
3001 m_seed = params.getU64("seed");
3003 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3006 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3008 DSTACK(__FUNCTION_NAME);
3009 // Format used for writing
3010 u8 version = SER_FMT_VER_HIGHEST;
3012 v2s16 pos = sector->getPos();
3013 std::string dir = getSectorDir(pos);
3016 std::string fullpath = dir + DIR_DELIM + "meta";
3017 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3018 if(o.good() == false)
3019 throw FileNotGoodException("Cannot open sector metafile");
3021 sector->serialize(o, version);
3023 sector->differs_from_disk = false;
3026 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3028 DSTACK(__FUNCTION_NAME);
3030 v2s16 p2d = getSectorPos(sectordir);
3032 ServerMapSector *sector = NULL;
3034 std::string fullpath = sectordir + DIR_DELIM + "meta";
3035 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3036 if(is.good() == false)
3038 // If the directory exists anyway, it probably is in some old
3039 // format. Just go ahead and create the sector.
3040 if(fs::PathExists(sectordir))
3042 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3043 <<fullpath<<" doesn't exist but directory does."
3044 <<" Continuing with a sector with no metadata."
3046 sector = new ServerMapSector(this, p2d, m_gamedef);
3047 m_sectors.insert(p2d, sector);
3051 throw FileNotGoodException("Cannot open sector metafile");
3056 sector = ServerMapSector::deSerialize
3057 (is, this, p2d, m_sectors, m_gamedef);
3059 saveSectorMeta(sector);
3062 sector->differs_from_disk = false;
3067 bool ServerMap::loadSectorMeta(v2s16 p2d)
3069 DSTACK(__FUNCTION_NAME);
3071 MapSector *sector = NULL;
3073 // The directory layout we're going to load from.
3074 // 1 - original sectors/xxxxzzzz/
3075 // 2 - new sectors2/xxx/zzz/
3076 // If we load from anything but the latest structure, we will
3077 // immediately save to the new one, and remove the old.
3079 std::string sectordir1 = getSectorDir(p2d, 1);
3080 std::string sectordir;
3081 if(fs::PathExists(sectordir1))
3083 sectordir = sectordir1;
3088 sectordir = getSectorDir(p2d, 2);
3092 sector = loadSectorMeta(sectordir, loadlayout != 2);
3094 catch(InvalidFilenameException &e)
3098 catch(FileNotGoodException &e)
3102 catch(std::exception &e)
3111 bool ServerMap::loadSectorFull(v2s16 p2d)
3113 DSTACK(__FUNCTION_NAME);
3115 MapSector *sector = NULL;
3117 // The directory layout we're going to load from.
3118 // 1 - original sectors/xxxxzzzz/
3119 // 2 - new sectors2/xxx/zzz/
3120 // If we load from anything but the latest structure, we will
3121 // immediately save to the new one, and remove the old.
3123 std::string sectordir1 = getSectorDir(p2d, 1);
3124 std::string sectordir;
3125 if(fs::PathExists(sectordir1))
3127 sectordir = sectordir1;
3132 sectordir = getSectorDir(p2d, 2);
3136 sector = loadSectorMeta(sectordir, loadlayout != 2);
3138 catch(InvalidFilenameException &e)
3142 catch(FileNotGoodException &e)
3146 catch(std::exception &e)
3154 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3156 std::vector<fs::DirListNode>::iterator i2;
3157 for(i2=list2.begin(); i2!=list2.end(); i2++)
3163 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3165 catch(InvalidFilenameException &e)
3167 // This catches unknown crap in directory
3173 infostream<<"Sector converted to new layout - deleting "<<
3174 sectordir1<<std::endl;
3175 fs::RecursiveDelete(sectordir1);
3182 void ServerMap::beginSave() {
3184 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3185 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3188 void ServerMap::endSave() {
3190 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3191 infostream<<"WARNING: endSave() failed, map might not have saved.";
3194 void ServerMap::saveBlock(MapBlock *block)
3196 DSTACK(__FUNCTION_NAME);
3198 Dummy blocks are not written
3200 if(block->isDummy())
3202 /*v3s16 p = block->getPos();
3203 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3204 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3208 // Format used for writing
3209 u8 version = SER_FMT_VER_HIGHEST;
3211 v3s16 p3d = block->getPos();
3215 v2s16 p2d(p3d.X, p3d.Z);
3216 std::string sectordir = getSectorDir(p2d);
3218 createDirs(sectordir);
3220 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3221 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3222 if(o.good() == false)
3223 throw FileNotGoodException("Cannot open block data");
3226 [0] u8 serialization version
3232 std::ostringstream o(std::ios_base::binary);
3234 o.write((char*)&version, 1);
3237 block->serialize(o, version);
3239 // Write extra data stored on disk
3240 block->serializeDiskExtra(o, version);
3242 // Write block to database
3244 std::string tmp = o.str();
3245 const char *bytes = tmp.c_str();
3247 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3248 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3249 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3250 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3251 int written = sqlite3_step(m_database_write);
3252 if(written != SQLITE_DONE)
3253 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3254 <<sqlite3_errmsg(m_database)<<std::endl;
3255 // Make ready for later reuse
3256 sqlite3_reset(m_database_write);
3258 // We just wrote it to the disk so clear modified flag
3259 block->resetModified();
3262 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3264 DSTACK(__FUNCTION_NAME);
3266 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3269 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3270 if(is.good() == false)
3271 throw FileNotGoodException("Cannot open block file");
3273 v3s16 p3d = getBlockPos(sectordir, blockfile);
3274 v2s16 p2d(p3d.X, p3d.Z);
3276 assert(sector->getPos() == p2d);
3278 u8 version = SER_FMT_VER_INVALID;
3279 is.read((char*)&version, 1);
3282 throw SerializationError("ServerMap::loadBlock(): Failed"
3283 " to read MapBlock version");
3285 /*u32 block_size = MapBlock::serializedLength(version);
3286 SharedBuffer<u8> data(block_size);
3287 is.read((char*)*data, block_size);*/
3289 // This will always return a sector because we're the server
3290 //MapSector *sector = emergeSector(p2d);
3292 MapBlock *block = NULL;
3293 bool created_new = false;
3294 block = sector->getBlockNoCreateNoEx(p3d.Y);
3297 block = sector->createBlankBlockNoInsert(p3d.Y);
3302 block->deSerialize(is, version);
3304 // Read extra data stored on disk
3305 block->deSerializeDiskExtra(is, version);
3307 // If it's a new block, insert it to the map
3309 sector->insertBlock(block);
3312 Save blocks loaded in old format in new format
3315 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3319 // Should be in database now, so delete the old file
3320 fs::RecursiveDelete(fullpath);
3323 // We just loaded it from the disk, so it's up-to-date.
3324 block->resetModified();
3327 catch(SerializationError &e)
3329 infostream<<"WARNING: Invalid block data on disk "
3330 <<"fullpath="<<fullpath
3331 <<" (SerializationError). "
3332 <<"what()="<<e.what()
3334 //" Ignoring. A new one will be generated.
3337 // TODO: Backup file; name is in fullpath.
3341 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3343 DSTACK(__FUNCTION_NAME);
3346 std::istringstream is(*blob, std::ios_base::binary);
3348 u8 version = SER_FMT_VER_INVALID;
3349 is.read((char*)&version, 1);
3352 throw SerializationError("ServerMap::loadBlock(): Failed"
3353 " to read MapBlock version");
3355 /*u32 block_size = MapBlock::serializedLength(version);
3356 SharedBuffer<u8> data(block_size);
3357 is.read((char*)*data, block_size);*/
3359 // This will always return a sector because we're the server
3360 //MapSector *sector = emergeSector(p2d);
3362 MapBlock *block = NULL;
3363 bool created_new = false;
3364 block = sector->getBlockNoCreateNoEx(p3d.Y);
3367 block = sector->createBlankBlockNoInsert(p3d.Y);
3372 block->deSerialize(is, version);
3374 // Read extra data stored on disk
3375 block->deSerializeDiskExtra(is, version);
3377 // If it's a new block, insert it to the map
3379 sector->insertBlock(block);
3382 Save blocks loaded in old format in new format
3385 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3386 // Only save if asked to; no need to update version
3390 // We just loaded it from, so it's up-to-date.
3391 block->resetModified();
3394 catch(SerializationError &e)
3396 infostream<<"WARNING: Invalid block data in database "
3397 <<" (SerializationError). "
3398 <<"what()="<<e.what()
3400 //" Ignoring. A new one will be generated.
3403 // TODO: Copy to a backup database.
3407 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3409 DSTACK(__FUNCTION_NAME);
3411 v2s16 p2d(blockpos.X, blockpos.Z);
3413 if(!loadFromFolders()) {
3416 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3417 infostream<<"WARNING: Could not bind block position for load: "
3418 <<sqlite3_errmsg(m_database)<<std::endl;
3419 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3421 Make sure sector is loaded
3423 MapSector *sector = createSector(p2d);
3428 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3429 size_t len = sqlite3_column_bytes(m_database_read, 0);
3431 std::string datastr(data, len);
3433 loadBlock(&datastr, blockpos, sector, false);
3435 sqlite3_step(m_database_read);
3436 // We should never get more than 1 row, so ok to reset
3437 sqlite3_reset(m_database_read);
3439 return getBlockNoCreateNoEx(blockpos);
3441 sqlite3_reset(m_database_read);
3443 // Not found in database, try the files
3446 // The directory layout we're going to load from.
3447 // 1 - original sectors/xxxxzzzz/
3448 // 2 - new sectors2/xxx/zzz/
3449 // If we load from anything but the latest structure, we will
3450 // immediately save to the new one, and remove the old.
3452 std::string sectordir1 = getSectorDir(p2d, 1);
3453 std::string sectordir;
3454 if(fs::PathExists(sectordir1))
3456 sectordir = sectordir1;
3461 sectordir = getSectorDir(p2d, 2);
3465 Make sure sector is loaded
3467 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3471 sector = loadSectorMeta(sectordir, loadlayout != 2);
3473 catch(InvalidFilenameException &e)
3477 catch(FileNotGoodException &e)
3481 catch(std::exception &e)
3488 Make sure file exists
3491 std::string blockfilename = getBlockFilename(blockpos);
3492 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3496 Load block and save it to the database
3498 loadBlock(sectordir, blockfilename, sector, true);
3499 return getBlockNoCreateNoEx(blockpos);
3502 void ServerMap::PrintInfo(std::ostream &out)
3513 ClientMap::ClientMap(
3516 MapDrawControl &control,
3517 scene::ISceneNode* parent,
3518 scene::ISceneManager* mgr,
3521 Map(dout_client, gamedef),
3522 scene::ISceneNode(parent, mgr, id),
3525 m_camera_position(0,0,0),
3526 m_camera_direction(0,0,1),
3529 m_camera_mutex.Init();
3530 assert(m_camera_mutex.IsInitialized());
3532 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3533 BS*1000000,BS*1000000,BS*1000000);
3536 ClientMap::~ClientMap()
3538 /*JMutexAutoLock lock(mesh_mutex);
3547 MapSector * ClientMap::emergeSector(v2s16 p2d)
3549 DSTACK(__FUNCTION_NAME);
3550 // Check that it doesn't exist already
3552 return getSectorNoGenerate(p2d);
3554 catch(InvalidPositionException &e)
3559 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3562 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3563 m_sectors.insert(p2d, sector);
3570 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3572 DSTACK(__FUNCTION_NAME);
3573 ClientMapSector *sector = NULL;
3575 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3577 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3581 sector = (ClientMapSector*)n->getValue();
3582 assert(sector->getId() == MAPSECTOR_CLIENT);
3586 sector = new ClientMapSector(this, p2d);
3588 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3589 m_sectors.insert(p2d, sector);
3593 sector->deSerialize(is);
3597 void ClientMap::OnRegisterSceneNode()
3601 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3602 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3605 ISceneNode::OnRegisterSceneNode();
3608 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3609 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3611 float d0 = (float)BS * p0.getDistanceFrom(p1);
3613 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3615 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3617 for(float s=start_off; s<d0+end_off; s+=step){
3618 v3f pf = p0f + uf * s;
3619 v3s16 p = floatToInt(pf, BS);
3620 MapNode n = map->getNodeNoEx(p);
3621 bool is_transparent = false;
3622 const ContentFeatures &f = nodemgr->get(n);
3623 if(f.solidness == 0)
3624 is_transparent = (f.visual_solidness != 2);
3626 is_transparent = (f.solidness != 2);
3627 if(!is_transparent){
3629 if(count >= needed_count)
3637 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3639 INodeDefManager *nodemgr = m_gamedef->ndef();
3641 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3642 DSTACK(__FUNCTION_NAME);
3644 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3647 if(pass == scene::ESNRP_SOLID)
3648 prefix = "CM: solid: ";
3650 prefix = "CM: transparent: ";
3653 This is called two times per frame, reset on the non-transparent one
3655 if(pass == scene::ESNRP_SOLID)
3657 m_last_drawn_sectors.clear();
3661 Get time for measuring timeout.
3663 Measuring time is very useful for long delays when the
3664 machine is swapping a lot.
3666 int time1 = time(0);
3668 //u32 daynight_ratio = m_client->getDayNightRatio();
3670 m_camera_mutex.Lock();
3671 v3f camera_position = m_camera_position;
3672 v3f camera_direction = m_camera_direction;
3673 f32 camera_fov = m_camera_fov;
3674 m_camera_mutex.Unlock();
3677 Get all blocks and draw all visible ones
3680 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3682 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3684 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3685 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3687 // Take a fair amount as we will be dropping more out later
3688 // Umm... these additions are a bit strange but they are needed.
3690 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3691 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3692 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3694 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3695 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3696 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3698 u32 vertex_count = 0;
3699 u32 meshbuffer_count = 0;
3701 // For limiting number of mesh updates per frame
3702 u32 mesh_update_count = 0;
3704 // Number of blocks in rendering range
3705 u32 blocks_in_range = 0;
3706 // Number of blocks occlusion culled
3707 u32 blocks_occlusion_culled = 0;
3708 // Number of blocks in rendering range but don't have a mesh
3709 u32 blocks_in_range_without_mesh = 0;
3710 // Blocks that had mesh that would have been drawn according to
3711 // rendering range (if max blocks limit didn't kick in)
3712 u32 blocks_would_have_drawn = 0;
3713 // Blocks that were drawn and had a mesh
3714 u32 blocks_drawn = 0;
3715 // Blocks which had a corresponding meshbuffer for this pass
3716 u32 blocks_had_pass_meshbuf = 0;
3717 // Blocks from which stuff was actually drawn
3718 u32 blocks_without_stuff = 0;
3721 Collect a set of blocks for drawing
3724 core::map<v3s16, MapBlock*> drawset;
3727 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3729 for(core::map<v2s16, MapSector*>::Iterator
3730 si = m_sectors.getIterator();
3731 si.atEnd() == false; si++)
3733 MapSector *sector = si.getNode()->getValue();
3734 v2s16 sp = sector->getPos();
3736 if(m_control.range_all == false)
3738 if(sp.X < p_blocks_min.X
3739 || sp.X > p_blocks_max.X
3740 || sp.Y < p_blocks_min.Z
3741 || sp.Y > p_blocks_max.Z)
3745 core::list< MapBlock * > sectorblocks;
3746 sector->getBlocks(sectorblocks);
3749 Loop through blocks in sector
3752 u32 sector_blocks_drawn = 0;
3754 core::list< MapBlock * >::Iterator i;
3755 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3757 MapBlock *block = *i;
3760 Compare block position to camera position, skip
3761 if not seen on display
3764 float range = 100000 * BS;
3765 if(m_control.range_all == false)
3766 range = m_control.wanted_range * BS;
3769 if(isBlockInSight(block->getPos(), camera_position,
3770 camera_direction, camera_fov,
3771 range, &d) == false)
3776 // This is ugly (spherical distance limit?)
3777 /*if(m_control.range_all == false &&
3778 d - 0.5*BS*MAP_BLOCKSIZE > range)
3785 Update expired mesh (used for day/night change)
3787 It doesn't work exactly like it should now with the
3788 tasked mesh update but whatever.
3791 bool mesh_expired = false;
3794 JMutexAutoLock lock(block->mesh_mutex);
3796 mesh_expired = block->getMeshExpired();
3798 // Mesh has not been expired and there is no mesh:
3799 // block has no content
3800 if(block->mesh == NULL && mesh_expired == false){
3801 blocks_in_range_without_mesh++;
3806 f32 faraway = BS*50;
3807 //f32 faraway = m_control.wanted_range * BS;
3810 This has to be done with the mesh_mutex unlocked
3812 // Pretty random but this should work somewhat nicely
3813 if(mesh_expired && (
3814 (mesh_update_count < 3
3815 && (d < faraway || mesh_update_count < 2)
3818 (m_control.range_all && mesh_update_count < 20)
3821 /*if(mesh_expired && mesh_update_count < 6
3822 && (d < faraway || mesh_update_count < 3))*/
3824 mesh_update_count++;
3826 // Mesh has been expired: generate new mesh
3827 //block->updateMesh(daynight_ratio);
3828 m_client->addUpdateMeshTask(block->getPos());
3830 mesh_expired = false;
3838 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3839 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3841 float stepfac = 1.1;
3842 float startoff = BS*1;
3843 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3844 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3845 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3846 u32 needed_count = 1;
3848 isOccluded(this, spn, cpn + v3s16(0,0,0),
3849 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3850 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3851 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3852 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3853 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3854 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3855 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3856 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3857 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3858 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3859 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3860 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3861 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3862 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3863 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3864 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3865 step, stepfac, startoff, endoff, needed_count, nodemgr)
3868 blocks_occlusion_culled++;
3872 // This block is in range. Reset usage timer.
3873 block->resetUsageTimer();
3876 Ignore if mesh doesn't exist
3879 JMutexAutoLock lock(block->mesh_mutex);
3881 scene::SMesh *mesh = block->mesh;
3884 blocks_in_range_without_mesh++;
3889 // Limit block count in case of a sudden increase
3890 blocks_would_have_drawn++;
3891 if(blocks_drawn >= m_control.wanted_max_blocks
3892 && m_control.range_all == false
3893 && d > m_control.wanted_min_range * BS)
3897 drawset[block->getPos()] = block;
3899 sector_blocks_drawn++;
3902 } // foreach sectorblocks
3904 if(sector_blocks_drawn != 0)
3905 m_last_drawn_sectors[sp] = true;
3910 Draw the selected MapBlocks
3914 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3916 int timecheck_counter = 0;
3917 for(core::map<v3s16, MapBlock*>::Iterator
3918 i = drawset.getIterator();
3919 i.atEnd() == false; i++)
3922 timecheck_counter++;
3923 if(timecheck_counter > 50)
3925 timecheck_counter = 0;
3926 int time2 = time(0);
3927 if(time2 > time1 + 4)
3929 infostream<<"ClientMap::renderMap(): "
3930 "Rendering takes ages, returning."
3937 MapBlock *block = i.getNode()->getValue();
3940 Draw the faces of the block
3943 JMutexAutoLock lock(block->mesh_mutex);
3945 scene::SMesh *mesh = block->mesh;
3948 u32 c = mesh->getMeshBufferCount();
3949 bool stuff_actually_drawn = false;
3950 for(u32 i=0; i<c; i++)
3952 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3953 const video::SMaterial& material = buf->getMaterial();
3954 video::IMaterialRenderer* rnd =
3955 driver->getMaterialRenderer(material.MaterialType);
3956 bool transparent = (rnd && rnd->isTransparent());
3957 // Render transparent on transparent pass and likewise.
3958 if(transparent == is_transparent_pass)
3960 if(buf->getVertexCount() == 0)
3961 errorstream<<"Block ["<<analyze_block(block)
3962 <<"] contains an empty meshbuf"<<std::endl;
3964 This *shouldn't* hurt too much because Irrlicht
3965 doesn't change opengl textures if the old
3966 material has the same texture.
3968 driver->setMaterial(buf->getMaterial());
3969 driver->drawMeshBuffer(buf);
3970 vertex_count += buf->getVertexCount();
3972 stuff_actually_drawn = true;
3975 if(stuff_actually_drawn)
3976 blocks_had_pass_meshbuf++;
3978 blocks_without_stuff++;
3983 // Log only on solid pass because values are the same
3984 if(pass == scene::ESNRP_SOLID){
3985 g_profiler->avg("CM: blocks in range", blocks_in_range);
3986 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3987 if(blocks_in_range != 0)
3988 g_profiler->avg("CM: blocks in range without mesh (frac)",
3989 (float)blocks_in_range_without_mesh/blocks_in_range);
3990 g_profiler->avg("CM: blocks drawn", blocks_drawn);
3993 g_profiler->avg(prefix+"vertices drawn", vertex_count);
3994 if(blocks_had_pass_meshbuf != 0)
3995 g_profiler->avg(prefix+"meshbuffers per block",
3996 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
3997 if(blocks_drawn != 0)
3998 g_profiler->avg(prefix+"empty blocks (frac)",
3999 (float)blocks_without_stuff / blocks_drawn);
4001 m_control.blocks_drawn = blocks_drawn;
4002 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4004 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4005 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4008 void ClientMap::renderPostFx()
4010 INodeDefManager *nodemgr = m_gamedef->ndef();
4012 // Sadly ISceneManager has no "post effects" render pass, in that case we
4013 // could just register for that and handle it in renderMap().
4015 m_camera_mutex.Lock();
4016 v3f camera_position = m_camera_position;
4017 m_camera_mutex.Unlock();
4019 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4021 // - If the player is in a solid node, make everything black.
4022 // - If the player is in liquid, draw a semi-transparent overlay.
4023 const ContentFeatures& features = nodemgr->get(n);
4024 video::SColor post_effect_color = features.post_effect_color;
4025 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4027 post_effect_color = video::SColor(255, 0, 0, 0);
4029 if (post_effect_color.getAlpha() != 0)
4031 // Draw a full-screen rectangle
4032 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4033 v2u32 ss = driver->getScreenSize();
4034 core::rect<s32> rect(0,0, ss.X, ss.Y);
4035 driver->draw2DRectangle(post_effect_color, rect);
4039 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4040 core::map<v3s16, MapBlock*> *affected_blocks)
4042 bool changed = false;
4044 Add it to all blocks touching it
4047 v3s16(0,0,0), // this
4048 v3s16(0,0,1), // back
4049 v3s16(0,1,0), // top
4050 v3s16(1,0,0), // right
4051 v3s16(0,0,-1), // front
4052 v3s16(0,-1,0), // bottom
4053 v3s16(-1,0,0), // left
4055 for(u16 i=0; i<7; i++)
4057 v3s16 p2 = p + dirs[i];
4058 // Block position of neighbor (or requested) node
4059 v3s16 blockpos = getNodeBlockPos(p2);
4060 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4061 if(blockref == NULL)
4063 // Relative position of requested node
4064 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4065 if(blockref->setTempMod(relpos, mod))
4070 if(changed && affected_blocks!=NULL)
4072 for(u16 i=0; i<7; i++)
4074 v3s16 p2 = p + dirs[i];
4075 // Block position of neighbor (or requested) node
4076 v3s16 blockpos = getNodeBlockPos(p2);
4077 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4078 if(blockref == NULL)
4080 affected_blocks->insert(blockpos, blockref);
4086 bool ClientMap::clearTempMod(v3s16 p,
4087 core::map<v3s16, MapBlock*> *affected_blocks)
4089 bool changed = false;
4091 v3s16(0,0,0), // this
4092 v3s16(0,0,1), // back
4093 v3s16(0,1,0), // top
4094 v3s16(1,0,0), // right
4095 v3s16(0,0,-1), // front
4096 v3s16(0,-1,0), // bottom
4097 v3s16(-1,0,0), // left
4099 for(u16 i=0; i<7; i++)
4101 v3s16 p2 = p + dirs[i];
4102 // Block position of neighbor (or requested) node
4103 v3s16 blockpos = getNodeBlockPos(p2);
4104 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4105 if(blockref == NULL)
4107 // Relative position of requested node
4108 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4109 if(blockref->clearTempMod(relpos))
4114 if(changed && affected_blocks!=NULL)
4116 for(u16 i=0; i<7; i++)
4118 v3s16 p2 = p + dirs[i];
4119 // Block position of neighbor (or requested) node
4120 v3s16 blockpos = getNodeBlockPos(p2);
4121 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4122 if(blockref == NULL)
4124 affected_blocks->insert(blockpos, blockref);
4130 void ClientMap::expireMeshes(bool only_daynight_diffed)
4132 TimeTaker timer("expireMeshes()");
4134 core::map<v2s16, MapSector*>::Iterator si;
4135 si = m_sectors.getIterator();
4136 for(; si.atEnd() == false; si++)
4138 MapSector *sector = si.getNode()->getValue();
4140 core::list< MapBlock * > sectorblocks;
4141 sector->getBlocks(sectorblocks);
4143 core::list< MapBlock * >::Iterator i;
4144 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4146 MapBlock *block = *i;
4148 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4154 JMutexAutoLock lock(block->mesh_mutex);
4155 if(block->mesh != NULL)
4157 /*block->mesh->drop();
4158 block->mesh = NULL;*/
4159 block->setMeshExpired(true);
4166 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4168 assert(mapType() == MAPTYPE_CLIENT);
4171 v3s16 p = blockpos + v3s16(0,0,0);
4172 MapBlock *b = getBlockNoCreate(p);
4173 b->updateMesh(daynight_ratio);
4174 //b->setMeshExpired(true);
4176 catch(InvalidPositionException &e){}
4179 v3s16 p = blockpos + v3s16(-1,0,0);
4180 MapBlock *b = getBlockNoCreate(p);
4181 b->updateMesh(daynight_ratio);
4182 //b->setMeshExpired(true);
4184 catch(InvalidPositionException &e){}
4186 v3s16 p = blockpos + v3s16(0,-1,0);
4187 MapBlock *b = getBlockNoCreate(p);
4188 b->updateMesh(daynight_ratio);
4189 //b->setMeshExpired(true);
4191 catch(InvalidPositionException &e){}
4193 v3s16 p = blockpos + v3s16(0,0,-1);
4194 MapBlock *b = getBlockNoCreate(p);
4195 b->updateMesh(daynight_ratio);
4196 //b->setMeshExpired(true);
4198 catch(InvalidPositionException &e){}
4203 Update mesh of block in which the node is, and if the node is at the
4204 leading edge, update the appropriate leading blocks too.
4206 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4214 v3s16 blockposes[4];
4215 for(u32 i=0; i<4; i++)
4217 v3s16 np = nodepos + dirs[i];
4218 blockposes[i] = getNodeBlockPos(np);
4219 // Don't update mesh of block if it has been done already
4220 bool already_updated = false;
4221 for(u32 j=0; j<i; j++)
4223 if(blockposes[j] == blockposes[i])
4225 already_updated = true;
4232 MapBlock *b = getBlockNoCreate(blockposes[i]);
4233 b->updateMesh(daynight_ratio);
4238 void ClientMap::PrintInfo(std::ostream &out)
4249 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4254 MapVoxelManipulator::~MapVoxelManipulator()
4256 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4260 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4262 TimeTaker timer1("emerge", &emerge_time);
4264 // Units of these are MapBlocks
4265 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4266 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4268 VoxelArea block_area_nodes
4269 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4271 addArea(block_area_nodes);
4273 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4274 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4275 for(s32 x=p_min.X; x<=p_max.X; x++)
4278 core::map<v3s16, bool>::Node *n;
4279 n = m_loaded_blocks.find(p);
4283 bool block_data_inexistent = false;
4286 TimeTaker timer1("emerge load", &emerge_load_time);
4288 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4289 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4291 a.print(infostream);
4292 infostream<<std::endl;*/
4294 MapBlock *block = m_map->getBlockNoCreate(p);
4295 if(block->isDummy())
4296 block_data_inexistent = true;
4298 block->copyTo(*this);
4300 catch(InvalidPositionException &e)
4302 block_data_inexistent = true;
4305 if(block_data_inexistent)
4307 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4308 // Fill with VOXELFLAG_INEXISTENT
4309 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4310 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4312 s32 i = m_area.index(a.MinEdge.X,y,z);
4313 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4317 m_loaded_blocks.insert(p, !block_data_inexistent);
4320 //infostream<<"emerge done"<<std::endl;
4324 SUGG: Add an option to only update eg. water and air nodes.
4325 This will make it interfere less with important stuff if
4328 void MapVoxelManipulator::blitBack
4329 (core::map<v3s16, MapBlock*> & modified_blocks)
4331 if(m_area.getExtent() == v3s16(0,0,0))
4334 //TimeTaker timer1("blitBack");
4336 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4337 <<m_loaded_blocks.size()<<std::endl;*/
4340 Initialize block cache
4342 v3s16 blockpos_last;
4343 MapBlock *block = NULL;
4344 bool block_checked_in_modified = false;
4346 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4347 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4348 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4352 u8 f = m_flags[m_area.index(p)];
4353 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4356 MapNode &n = m_data[m_area.index(p)];
4358 v3s16 blockpos = getNodeBlockPos(p);
4363 if(block == NULL || blockpos != blockpos_last){
4364 block = m_map->getBlockNoCreate(blockpos);
4365 blockpos_last = blockpos;
4366 block_checked_in_modified = false;
4369 // Calculate relative position in block
4370 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4372 // Don't continue if nothing has changed here
4373 if(block->getNode(relpos) == n)
4376 //m_map->setNode(m_area.MinEdge + p, n);
4377 block->setNode(relpos, n);
4380 Make sure block is in modified_blocks
4382 if(block_checked_in_modified == false)
4384 modified_blocks[blockpos] = block;
4385 block_checked_in_modified = true;
4388 catch(InvalidPositionException &e)
4394 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4395 MapVoxelManipulator(map),
4396 m_create_area(false)
4400 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4404 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4406 // Just create the area so that it can be pointed to
4407 VoxelManipulator::emerge(a, caller_id);
4410 void ManualMapVoxelManipulator::initialEmerge(
4411 v3s16 blockpos_min, v3s16 blockpos_max)
4413 TimeTaker timer1("initialEmerge", &emerge_time);
4415 // Units of these are MapBlocks
4416 v3s16 p_min = blockpos_min;
4417 v3s16 p_max = blockpos_max;
4419 VoxelArea block_area_nodes
4420 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4422 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4425 infostream<<"initialEmerge: area: ";
4426 block_area_nodes.print(infostream);
4427 infostream<<" ("<<size_MB<<"MB)";
4428 infostream<<std::endl;
4431 addArea(block_area_nodes);
4433 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4434 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4435 for(s32 x=p_min.X; x<=p_max.X; x++)
4438 core::map<v3s16, bool>::Node *n;
4439 n = m_loaded_blocks.find(p);
4443 bool block_data_inexistent = false;
4446 TimeTaker timer1("emerge load", &emerge_load_time);
4448 MapBlock *block = m_map->getBlockNoCreate(p);
4449 if(block->isDummy())
4450 block_data_inexistent = true;
4452 block->copyTo(*this);
4454 catch(InvalidPositionException &e)
4456 block_data_inexistent = true;
4459 if(block_data_inexistent)
4462 Mark area inexistent
4464 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4465 // Fill with VOXELFLAG_INEXISTENT
4466 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4467 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4469 s32 i = m_area.index(a.MinEdge.X,y,z);
4470 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4474 m_loaded_blocks.insert(p, !block_data_inexistent);
4478 void ManualMapVoxelManipulator::blitBackAll(
4479 core::map<v3s16, MapBlock*> * modified_blocks)
4481 if(m_area.getExtent() == v3s16(0,0,0))
4485 Copy data of all blocks
4487 for(core::map<v3s16, bool>::Iterator
4488 i = m_loaded_blocks.getIterator();
4489 i.atEnd() == false; i++)
4491 v3s16 p = i.getNode()->getKey();
4492 bool existed = i.getNode()->getValue();
4493 if(existed == false)
4495 // The Great Bug was found using this
4496 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4497 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4501 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4504 infostream<<"WARNING: "<<__FUNCTION_NAME
4505 <<": got NULL block "
4506 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4511 block->copyFrom(*this);
4514 modified_blocks->insert(p, block);