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 std::string metadata_name = nodemgr->get(n).metadata_name;
999 if(metadata_name != ""){
1000 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1001 meta->setOwner(player_name);
1002 setNodeMetadata(p, meta);
1006 If node is under sunlight and doesn't let sunlight through,
1007 take all sunlighted nodes under it and clear light from them
1008 and from where the light has been spread.
1009 TODO: This could be optimized by mass-unlighting instead
1012 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1016 //m_dout<<DTIME<<"y="<<y<<std::endl;
1017 v3s16 n2pos(p.X, y, p.Z);
1021 n2 = getNode(n2pos);
1023 catch(InvalidPositionException &e)
1028 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1030 unLightNeighbors(LIGHTBANK_DAY,
1031 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1032 light_sources, modified_blocks);
1033 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1041 for(s32 i=0; i<2; i++)
1043 enum LightBank bank = banks[i];
1046 Spread light from all nodes that might be capable of doing so
1048 spreadLight(bank, light_sources, modified_blocks);
1052 Update information about whether day and night light differ
1054 for(core::map<v3s16, MapBlock*>::Iterator
1055 i = modified_blocks.getIterator();
1056 i.atEnd() == false; i++)
1058 MapBlock *block = i.getNode()->getValue();
1059 block->updateDayNightDiff();
1063 Add neighboring liquid nodes and the node itself if it is
1064 liquid (=water node was added) to transform queue.
1067 v3s16(0,0,0), // self
1068 v3s16(0,0,1), // back
1069 v3s16(0,1,0), // top
1070 v3s16(1,0,0), // right
1071 v3s16(0,0,-1), // front
1072 v3s16(0,-1,0), // bottom
1073 v3s16(-1,0,0), // left
1075 for(u16 i=0; i<7; i++)
1080 v3s16 p2 = p + dirs[i];
1082 MapNode n2 = getNode(p2);
1083 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1085 m_transforming_liquid.push_back(p2);
1088 }catch(InvalidPositionException &e)
1096 void Map::removeNodeAndUpdate(v3s16 p,
1097 core::map<v3s16, MapBlock*> &modified_blocks)
1099 INodeDefManager *nodemgr = m_gamedef->ndef();
1101 /*PrintInfo(m_dout);
1102 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1103 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1105 bool node_under_sunlight = true;
1107 v3s16 toppos = p + v3s16(0,1,0);
1109 // Node will be replaced with this
1110 content_t replace_material = CONTENT_AIR;
1113 If there is a node at top and it doesn't have sunlight,
1114 there will be no sunlight going down.
1117 MapNode topnode = getNode(toppos);
1119 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1120 node_under_sunlight = false;
1122 catch(InvalidPositionException &e)
1126 core::map<v3s16, bool> light_sources;
1128 enum LightBank banks[] =
1133 for(s32 i=0; i<2; i++)
1135 enum LightBank bank = banks[i];
1138 Unlight neighbors (in case the node is a light source)
1140 unLightNeighbors(bank, p,
1141 getNode(p).getLight(bank, nodemgr),
1142 light_sources, modified_blocks);
1146 Remove node metadata
1149 removeNodeMetadata(p);
1153 This also clears the lighting.
1157 n.setContent(replace_material);
1160 for(s32 i=0; i<2; i++)
1162 enum LightBank bank = banks[i];
1165 Recalculate lighting
1167 spreadLight(bank, light_sources, modified_blocks);
1170 // Add the block of the removed node to modified_blocks
1171 v3s16 blockpos = getNodeBlockPos(p);
1172 MapBlock * block = getBlockNoCreate(blockpos);
1173 assert(block != NULL);
1174 modified_blocks.insert(blockpos, block);
1177 If the removed node was under sunlight, propagate the
1178 sunlight down from it and then light all neighbors
1179 of the propagated blocks.
1181 if(node_under_sunlight)
1183 s16 ybottom = propagateSunlight(p, modified_blocks);
1184 /*m_dout<<DTIME<<"Node was under sunlight. "
1185 "Propagating sunlight";
1186 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1188 for(; y >= ybottom; y--)
1190 v3s16 p2(p.X, y, p.Z);
1191 /*m_dout<<DTIME<<"lighting neighbors of node ("
1192 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1194 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1199 // Set the lighting of this node to 0
1200 // TODO: Is this needed? Lighting is cleared up there already.
1202 MapNode n = getNode(p);
1203 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1206 catch(InvalidPositionException &e)
1212 for(s32 i=0; i<2; i++)
1214 enum LightBank bank = banks[i];
1216 // Get the brightest neighbour node and propagate light from it
1217 v3s16 n2p = getBrightestNeighbour(bank, p);
1219 MapNode n2 = getNode(n2p);
1220 lightNeighbors(bank, n2p, modified_blocks);
1222 catch(InvalidPositionException &e)
1228 Update information about whether day and night light differ
1230 for(core::map<v3s16, MapBlock*>::Iterator
1231 i = modified_blocks.getIterator();
1232 i.atEnd() == false; i++)
1234 MapBlock *block = i.getNode()->getValue();
1235 block->updateDayNightDiff();
1239 Add neighboring liquid nodes and this node to transform queue.
1240 (it's vital for the node itself to get updated last.)
1243 v3s16(0,0,1), // back
1244 v3s16(0,1,0), // top
1245 v3s16(1,0,0), // right
1246 v3s16(0,0,-1), // front
1247 v3s16(0,-1,0), // bottom
1248 v3s16(-1,0,0), // left
1249 v3s16(0,0,0), // self
1251 for(u16 i=0; i<7; i++)
1256 v3s16 p2 = p + dirs[i];
1258 MapNode n2 = getNode(p2);
1259 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1261 m_transforming_liquid.push_back(p2);
1264 }catch(InvalidPositionException &e)
1270 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1273 event.type = MEET_ADDNODE;
1277 bool succeeded = true;
1279 core::map<v3s16, MapBlock*> modified_blocks;
1280 std::string st = std::string("");
1281 addNodeAndUpdate(p, n, modified_blocks, st);
1283 // Copy modified_blocks to event
1284 for(core::map<v3s16, MapBlock*>::Iterator
1285 i = modified_blocks.getIterator();
1286 i.atEnd()==false; i++)
1288 event.modified_blocks.insert(i.getNode()->getKey(), false);
1291 catch(InvalidPositionException &e){
1295 dispatchEvent(&event);
1300 bool Map::removeNodeWithEvent(v3s16 p)
1303 event.type = MEET_REMOVENODE;
1306 bool succeeded = true;
1308 core::map<v3s16, MapBlock*> modified_blocks;
1309 removeNodeAndUpdate(p, modified_blocks);
1311 // Copy modified_blocks to event
1312 for(core::map<v3s16, MapBlock*>::Iterator
1313 i = modified_blocks.getIterator();
1314 i.atEnd()==false; i++)
1316 event.modified_blocks.insert(i.getNode()->getKey(), false);
1319 catch(InvalidPositionException &e){
1323 dispatchEvent(&event);
1328 bool Map::dayNightDiffed(v3s16 blockpos)
1331 v3s16 p = blockpos + v3s16(0,0,0);
1332 MapBlock *b = getBlockNoCreate(p);
1333 if(b->dayNightDiffed())
1336 catch(InvalidPositionException &e){}
1339 v3s16 p = blockpos + v3s16(-1,0,0);
1340 MapBlock *b = getBlockNoCreate(p);
1341 if(b->dayNightDiffed())
1344 catch(InvalidPositionException &e){}
1346 v3s16 p = blockpos + v3s16(0,-1,0);
1347 MapBlock *b = getBlockNoCreate(p);
1348 if(b->dayNightDiffed())
1351 catch(InvalidPositionException &e){}
1353 v3s16 p = blockpos + v3s16(0,0,-1);
1354 MapBlock *b = getBlockNoCreate(p);
1355 if(b->dayNightDiffed())
1358 catch(InvalidPositionException &e){}
1361 v3s16 p = blockpos + v3s16(1,0,0);
1362 MapBlock *b = getBlockNoCreate(p);
1363 if(b->dayNightDiffed())
1366 catch(InvalidPositionException &e){}
1368 v3s16 p = blockpos + v3s16(0,1,0);
1369 MapBlock *b = getBlockNoCreate(p);
1370 if(b->dayNightDiffed())
1373 catch(InvalidPositionException &e){}
1375 v3s16 p = blockpos + v3s16(0,0,1);
1376 MapBlock *b = getBlockNoCreate(p);
1377 if(b->dayNightDiffed())
1380 catch(InvalidPositionException &e){}
1386 Updates usage timers
1388 void Map::timerUpdate(float dtime, float unload_timeout,
1389 core::list<v3s16> *unloaded_blocks)
1391 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1393 // Profile modified reasons
1394 Profiler modprofiler;
1396 core::list<v2s16> sector_deletion_queue;
1397 u32 deleted_blocks_count = 0;
1398 u32 saved_blocks_count = 0;
1400 core::map<v2s16, MapSector*>::Iterator si;
1403 si = m_sectors.getIterator();
1404 for(; si.atEnd() == false; si++)
1406 MapSector *sector = si.getNode()->getValue();
1408 bool all_blocks_deleted = true;
1410 core::list<MapBlock*> blocks;
1411 sector->getBlocks(blocks);
1413 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1414 i != blocks.end(); i++)
1416 MapBlock *block = (*i);
1418 block->incrementUsageTimer(dtime);
1420 if(block->getUsageTimer() > unload_timeout)
1422 v3s16 p = block->getPos();
1425 if(block->getModified() != MOD_STATE_CLEAN
1426 && save_before_unloading)
1428 modprofiler.add(block->getModifiedReason(), 1);
1430 saved_blocks_count++;
1433 // Delete from memory
1434 sector->deleteBlock(block);
1437 unloaded_blocks->push_back(p);
1439 deleted_blocks_count++;
1443 all_blocks_deleted = false;
1447 if(all_blocks_deleted)
1449 sector_deletion_queue.push_back(si.getNode()->getKey());
1454 // Finally delete the empty sectors
1455 deleteSectors(sector_deletion_queue);
1457 if(deleted_blocks_count != 0)
1459 PrintInfo(infostream); // ServerMap/ClientMap:
1460 infostream<<"Unloaded "<<deleted_blocks_count
1461 <<" blocks from memory";
1462 if(save_before_unloading)
1463 infostream<<", of which "<<saved_blocks_count<<" were written";
1464 infostream<<"."<<std::endl;
1465 if(saved_blocks_count != 0){
1466 PrintInfo(infostream); // ServerMap/ClientMap:
1467 infostream<<"Blocks modified by: "<<std::endl;
1468 modprofiler.print(infostream);
1473 void Map::deleteSectors(core::list<v2s16> &list)
1475 core::list<v2s16>::Iterator j;
1476 for(j=list.begin(); j!=list.end(); j++)
1478 MapSector *sector = m_sectors[*j];
1479 // If sector is in sector cache, remove it from there
1480 if(m_sector_cache == sector)
1481 m_sector_cache = NULL;
1482 // Remove from map and delete
1483 m_sectors.remove(*j);
1489 void Map::unloadUnusedData(float timeout,
1490 core::list<v3s16> *deleted_blocks)
1492 core::list<v2s16> sector_deletion_queue;
1493 u32 deleted_blocks_count = 0;
1494 u32 saved_blocks_count = 0;
1496 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1497 for(; si.atEnd() == false; si++)
1499 MapSector *sector = si.getNode()->getValue();
1501 bool all_blocks_deleted = true;
1503 core::list<MapBlock*> blocks;
1504 sector->getBlocks(blocks);
1505 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1506 i != blocks.end(); i++)
1508 MapBlock *block = (*i);
1510 if(block->getUsageTimer() > timeout)
1513 if(block->getModified() != MOD_STATE_CLEAN)
1516 saved_blocks_count++;
1518 // Delete from memory
1519 sector->deleteBlock(block);
1520 deleted_blocks_count++;
1524 all_blocks_deleted = false;
1528 if(all_blocks_deleted)
1530 sector_deletion_queue.push_back(si.getNode()->getKey());
1534 deleteSectors(sector_deletion_queue);
1536 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1537 <<", of which "<<saved_blocks_count<<" were wr."
1540 //return sector_deletion_queue.getSize();
1541 //return deleted_blocks_count;
1545 void Map::PrintInfo(std::ostream &out)
1550 #define WATER_DROP_BOOST 4
1554 NEIGHBOR_SAME_LEVEL,
1557 struct NodeNeighbor {
1563 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1565 INodeDefManager *nodemgr = m_gamedef->ndef();
1567 DSTACK(__FUNCTION_NAME);
1568 //TimeTaker timer("transformLiquids()");
1571 u32 initial_size = m_transforming_liquid.size();
1573 /*if(initial_size != 0)
1574 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1576 // list of nodes that due to viscosity have not reached their max level height
1577 UniqueQueue<v3s16> must_reflow;
1579 // List of MapBlocks that will require a lighting update (due to lava)
1580 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1582 while(m_transforming_liquid.size() != 0)
1584 // This should be done here so that it is done when continue is used
1585 if(loopcount >= initial_size * 3)
1590 Get a queued transforming liquid node
1592 v3s16 p0 = m_transforming_liquid.pop_front();
1594 MapNode n0 = getNodeNoEx(p0);
1597 Collect information about current node
1599 s8 liquid_level = -1;
1600 u8 liquid_kind = CONTENT_IGNORE;
1601 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1602 switch (liquid_type) {
1604 liquid_level = LIQUID_LEVEL_SOURCE;
1605 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1607 case LIQUID_FLOWING:
1608 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1609 liquid_kind = n0.getContent();
1612 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1613 // continue with the next node.
1614 if (n0.getContent() != CONTENT_AIR)
1616 liquid_kind = CONTENT_AIR;
1621 Collect information about the environment
1623 const v3s16 *dirs = g_6dirs;
1624 NodeNeighbor sources[6]; // surrounding sources
1625 int num_sources = 0;
1626 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1628 NodeNeighbor airs[6]; // surrounding air
1630 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1631 int num_neutrals = 0;
1632 bool flowing_down = false;
1633 for (u16 i = 0; i < 6; i++) {
1634 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1637 nt = NEIGHBOR_UPPER;
1640 nt = NEIGHBOR_LOWER;
1643 v3s16 npos = p0 + dirs[i];
1644 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1645 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1647 if (nb.n.getContent() == CONTENT_AIR) {
1648 airs[num_airs++] = nb;
1649 // if the current node is a water source the neighbor
1650 // should be enqueded for transformation regardless of whether the
1651 // current node changes or not.
1652 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1653 m_transforming_liquid.push_back(npos);
1654 // if the current node happens to be a flowing node, it will start to flow down here.
1655 if (nb.t == NEIGHBOR_LOWER) {
1656 flowing_down = true;
1659 neutrals[num_neutrals++] = nb;
1663 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1664 if (liquid_kind == CONTENT_AIR)
1665 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1666 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1667 neutrals[num_neutrals++] = nb;
1669 sources[num_sources++] = nb;
1672 case LIQUID_FLOWING:
1673 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1674 if (liquid_kind == CONTENT_AIR)
1675 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1676 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1677 neutrals[num_neutrals++] = nb;
1679 flows[num_flows++] = nb;
1680 if (nb.t == NEIGHBOR_LOWER)
1681 flowing_down = true;
1688 decide on the type (and possibly level) of the current node
1690 content_t new_node_content;
1691 s8 new_node_level = -1;
1692 s8 max_node_level = -1;
1693 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1694 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1695 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1696 // it's perfectly safe to use liquid_kind here to determine the new node content.
1697 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1698 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1699 // liquid_kind is set properly, see above
1700 new_node_content = liquid_kind;
1701 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1703 // no surrounding sources, so get the maximum level that can flow into this node
1704 for (u16 i = 0; i < num_flows; i++) {
1705 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1706 switch (flows[i].t) {
1707 case NEIGHBOR_UPPER:
1708 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1709 max_node_level = LIQUID_LEVEL_MAX;
1710 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1711 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1712 } else if (nb_liquid_level > max_node_level)
1713 max_node_level = nb_liquid_level;
1715 case NEIGHBOR_LOWER:
1717 case NEIGHBOR_SAME_LEVEL:
1718 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1719 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1720 max_node_level = nb_liquid_level - 1;
1726 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1727 if (viscosity > 1 && max_node_level != liquid_level) {
1728 // amount to gain, limited by viscosity
1729 // must be at least 1 in absolute value
1730 s8 level_inc = max_node_level - liquid_level;
1731 if (level_inc < -viscosity || level_inc > viscosity)
1732 new_node_level = liquid_level + level_inc/viscosity;
1733 else if (level_inc < 0)
1734 new_node_level = liquid_level - 1;
1735 else if (level_inc > 0)
1736 new_node_level = liquid_level + 1;
1737 if (new_node_level != max_node_level)
1738 must_reflow.push_back(p0);
1740 new_node_level = max_node_level;
1742 if (new_node_level >= 0)
1743 new_node_content = liquid_kind;
1745 new_node_content = CONTENT_AIR;
1750 check if anything has changed. if not, just continue with the next node.
1752 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1753 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1754 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1760 update the current node
1762 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1763 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1764 // set level to last 3 bits, flowing down bit to 4th bit
1765 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1767 // set the liquid level and flow bit to 0
1768 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1770 n0.setContent(new_node_content);
1772 v3s16 blockpos = getNodeBlockPos(p0);
1773 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1775 modified_blocks.insert(blockpos, block);
1776 // If node emits light, MapBlock requires lighting update
1777 if(nodemgr->get(n0).light_source != 0)
1778 lighting_modified_blocks[block->getPos()] = block;
1782 enqueue neighbors for update if neccessary
1784 switch (nodemgr->get(n0.getContent()).liquid_type) {
1786 case LIQUID_FLOWING:
1787 // make sure source flows into all neighboring nodes
1788 for (u16 i = 0; i < num_flows; i++)
1789 if (flows[i].t != NEIGHBOR_UPPER)
1790 m_transforming_liquid.push_back(flows[i].p);
1791 for (u16 i = 0; i < num_airs; i++)
1792 if (airs[i].t != NEIGHBOR_UPPER)
1793 m_transforming_liquid.push_back(airs[i].p);
1796 // this flow has turned to air; neighboring flows might need to do the same
1797 for (u16 i = 0; i < num_flows; i++)
1798 m_transforming_liquid.push_back(flows[i].p);
1802 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1803 while (must_reflow.size() > 0)
1804 m_transforming_liquid.push_back(must_reflow.pop_front());
1805 updateLighting(lighting_modified_blocks, modified_blocks);
1808 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1810 v3s16 blockpos = getNodeBlockPos(p);
1811 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1812 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1814 infostream<<"Map::getNodeMetadata(): Need to emerge "
1815 <<PP(blockpos)<<std::endl;
1816 block = emergeBlock(blockpos, false);
1820 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1824 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1828 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1830 v3s16 blockpos = getNodeBlockPos(p);
1831 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1832 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1834 infostream<<"Map::setNodeMetadata(): Need to emerge "
1835 <<PP(blockpos)<<std::endl;
1836 block = emergeBlock(blockpos, false);
1840 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1844 block->m_node_metadata->set(p_rel, meta);
1847 void Map::removeNodeMetadata(v3s16 p)
1849 v3s16 blockpos = getNodeBlockPos(p);
1850 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1851 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1854 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1858 block->m_node_metadata->remove(p_rel);
1861 void Map::nodeMetadataStep(float dtime,
1862 core::map<v3s16, MapBlock*> &changed_blocks)
1866 Currently there is no way to ensure that all the necessary
1867 blocks are loaded when this is run. (They might get unloaded)
1868 NOTE: ^- Actually, that might not be so. In a quick test it
1869 reloaded a block with a furnace when I walked back to it from
1872 core::map<v2s16, MapSector*>::Iterator si;
1873 si = m_sectors.getIterator();
1874 for(; si.atEnd() == false; si++)
1876 MapSector *sector = si.getNode()->getValue();
1877 core::list< MapBlock * > sectorblocks;
1878 sector->getBlocks(sectorblocks);
1879 core::list< MapBlock * >::Iterator i;
1880 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1882 MapBlock *block = *i;
1883 bool changed = block->m_node_metadata->step(dtime);
1885 changed_blocks[block->getPos()] = block;
1894 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1895 Map(dout_server, gamedef),
1897 m_map_metadata_changed(true),
1899 m_database_read(NULL),
1900 m_database_write(NULL)
1902 infostream<<__FUNCTION_NAME<<std::endl;
1904 //m_chunksize = 8; // Takes a few seconds
1906 if (g_settings->get("fixed_map_seed").empty())
1908 m_seed = (((u64)(myrand()%0xffff)<<0)
1909 + ((u64)(myrand()%0xffff)<<16)
1910 + ((u64)(myrand()%0xffff)<<32)
1911 + ((u64)(myrand()%0xffff)<<48));
1915 m_seed = g_settings->getU64("fixed_map_seed");
1919 Experimental and debug stuff
1926 Try to load map; if not found, create a new one.
1929 m_savedir = savedir;
1930 m_map_saving_enabled = false;
1934 // If directory exists, check contents and load if possible
1935 if(fs::PathExists(m_savedir))
1937 // If directory is empty, it is safe to save into it.
1938 if(fs::GetDirListing(m_savedir).size() == 0)
1940 infostream<<"Server: Empty save directory is valid."
1942 m_map_saving_enabled = true;
1947 // Load map metadata (seed, chunksize)
1950 catch(FileNotGoodException &e){
1951 infostream<<"WARNING: Could not load map metadata"
1952 //<<" Disabling chunk-based generator."
1958 // Load chunk metadata
1961 catch(FileNotGoodException &e){
1962 infostream<<"WARNING: Could not load chunk metadata."
1963 <<" Disabling chunk-based generator."
1968 /*infostream<<"Server: Successfully loaded chunk "
1969 "metadata and sector (0,0) from "<<savedir<<
1970 ", assuming valid save directory."
1973 infostream<<"Server: Successfully loaded map "
1974 <<"and chunk metadata from "<<savedir
1975 <<", assuming valid save directory."
1978 m_map_saving_enabled = true;
1979 // Map loaded, not creating new one
1983 // If directory doesn't exist, it is safe to save to it
1985 m_map_saving_enabled = true;
1988 catch(std::exception &e)
1990 infostream<<"WARNING: Server: Failed to load map from "<<savedir
1991 <<", exception: "<<e.what()<<std::endl;
1992 infostream<<"Please remove the map or fix it."<<std::endl;
1993 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1996 infostream<<"Initializing new map."<<std::endl;
1998 // Create zero sector
1999 emergeSector(v2s16(0,0));
2001 // Initially write whole map
2005 ServerMap::~ServerMap()
2007 infostream<<__FUNCTION_NAME<<std::endl;
2011 if(m_map_saving_enabled)
2013 // Save only changed parts
2015 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2019 infostream<<"Server: map not saved"<<std::endl;
2022 catch(std::exception &e)
2024 infostream<<"Server: Failed to save map to "<<m_savedir
2025 <<", exception: "<<e.what()<<std::endl;
2029 Close database if it was opened
2032 sqlite3_finalize(m_database_read);
2033 if(m_database_write)
2034 sqlite3_finalize(m_database_write);
2036 sqlite3_close(m_database);
2042 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2043 for(; i.atEnd() == false; i++)
2045 MapChunk *chunk = i.getNode()->getValue();
2051 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2053 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2054 if(enable_mapgen_debug_info)
2055 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2056 <<blockpos.Z<<")"<<std::endl;
2058 // Do nothing if not inside limits (+-1 because of neighbors)
2059 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2060 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2066 data->no_op = false;
2067 data->seed = m_seed;
2068 data->blockpos = blockpos;
2069 data->nodedef = m_gamedef->ndef();
2072 Create the whole area of this and the neighboring blocks
2075 //TimeTaker timer("initBlockMake() create area");
2077 for(s16 x=-1; x<=1; x++)
2078 for(s16 z=-1; z<=1; z++)
2080 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2081 // Sector metadata is loaded from disk if not already loaded.
2082 ServerMapSector *sector = createSector(sectorpos);
2085 for(s16 y=-1; y<=1; y++)
2087 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2088 //MapBlock *block = createBlock(p);
2089 // 1) get from memory, 2) load from disk
2090 MapBlock *block = emergeBlock(p, false);
2091 // 3) create a blank one
2094 block = createBlock(p);
2097 Block gets sunlight if this is true.
2099 Refer to the map generator heuristics.
2101 bool ug = mapgen::block_is_underground(data->seed, p);
2102 block->setIsUnderground(ug);
2105 // Lighting will not be valid after make_chunk is called
2106 block->setLightingExpired(true);
2107 // Lighting will be calculated
2108 //block->setLightingExpired(false);
2114 Now we have a big empty area.
2116 Make a ManualMapVoxelManipulator that contains this and the
2120 // The area that contains this block and it's neighbors
2121 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2122 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2124 data->vmanip = new ManualMapVoxelManipulator(this);
2125 //data->vmanip->setMap(this);
2129 //TimeTaker timer("initBlockMake() initialEmerge");
2130 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2133 // Data is ready now.
2136 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2137 core::map<v3s16, MapBlock*> &changed_blocks)
2139 v3s16 blockpos = data->blockpos;
2140 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2141 <<blockpos.Z<<")"<<std::endl;*/
2145 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2149 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2151 /*infostream<<"Resulting vmanip:"<<std::endl;
2152 data->vmanip.print(infostream);*/
2155 Blit generated stuff to map
2156 NOTE: blitBackAll adds nearly everything to changed_blocks
2160 //TimeTaker timer("finishBlockMake() blitBackAll");
2161 data->vmanip->blitBackAll(&changed_blocks);
2164 if(enable_mapgen_debug_info)
2165 infostream<<"finishBlockMake: changed_blocks.size()="
2166 <<changed_blocks.size()<<std::endl;
2169 Copy transforming liquid information
2171 while(data->transforming_liquid.size() > 0)
2173 v3s16 p = data->transforming_liquid.pop_front();
2174 m_transforming_liquid.push_back(p);
2180 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2184 Set is_underground flag for lighting with sunlight.
2186 Refer to map generator heuristics.
2188 NOTE: This is done in initChunkMake
2190 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2194 Add sunlight to central block.
2195 This makes in-dark-spawning monsters to not flood the whole thing.
2196 Do not spread the light, though.
2198 /*core::map<v3s16, bool> light_sources;
2199 bool black_air_left = false;
2200 block->propagateSunlight(light_sources, true, &black_air_left);*/
2203 NOTE: Lighting and object adding shouldn't really be here, but
2204 lighting is a bit tricky to move properly to makeBlock.
2205 TODO: Do this the right way anyway, that is, move it to makeBlock.
2206 - There needs to be some way for makeBlock to report back if
2207 the lighting update is going further down because of the
2208 new block blocking light
2213 NOTE: This takes ~60ms, TODO: Investigate why
2216 TimeTaker t("finishBlockMake lighting update");
2218 core::map<v3s16, MapBlock*> lighting_update_blocks;
2221 lighting_update_blocks.insert(block->getPos(), block);
2226 v3s16 p = block->getPos()+v3s16(x,1,z);
2227 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2231 // All modified blocks
2232 // NOTE: Should this be done? If this is not done, then the lighting
2233 // of the others will be updated in a different place, one by one, i
2234 // think... or they might not? Well, at least they are left marked as
2235 // "lighting expired"; it seems that is not handled at all anywhere,
2236 // so enabling this will slow it down A LOT because otherwise it
2237 // would not do this at all. This causes the black trees.
2238 for(core::map<v3s16, MapBlock*>::Iterator
2239 i = changed_blocks.getIterator();
2240 i.atEnd() == false; i++)
2242 lighting_update_blocks.insert(i.getNode()->getKey(),
2243 i.getNode()->getValue());
2245 /*// Also force-add all the upmost blocks for proper sunlight
2246 for(s16 x=-1; x<=1; x++)
2247 for(s16 z=-1; z<=1; z++)
2249 v3s16 p = block->getPos()+v3s16(x,1,z);
2250 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2253 updateLighting(lighting_update_blocks, changed_blocks);
2256 Set lighting to non-expired state in all of them.
2257 This is cheating, but it is not fast enough if all of them
2258 would actually be updated.
2260 for(s16 x=-1; x<=1; x++)
2261 for(s16 y=-1; y<=1; y++)
2262 for(s16 z=-1; z<=1; z++)
2264 v3s16 p = block->getPos()+v3s16(x,y,z);
2265 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2268 if(enable_mapgen_debug_info == false)
2269 t.stop(true); // Hide output
2273 Add random objects to block
2275 mapgen::add_random_objects(block);
2278 Go through changed blocks
2280 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2281 i.atEnd() == false; i++)
2283 MapBlock *block = i.getNode()->getValue();
2286 Update day/night difference cache of the MapBlocks
2288 block->updateDayNightDiff();
2290 Set block as modified
2292 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2293 "finishBlockMake updateDayNightDiff");
2297 Set central block as generated
2299 block->setGenerated(true);
2302 Save changed parts of map
2303 NOTE: Will be saved later.
2307 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2308 <<blockpos.Z<<")"<<std::endl;*/
2310 if(enable_mapgen_debug_info)
2313 Analyze resulting blocks
2315 for(s16 x=-1; x<=1; x++)
2316 for(s16 y=-1; y<=1; y++)
2317 for(s16 z=-1; z<=1; z++)
2319 v3s16 p = block->getPos()+v3s16(x,y,z);
2320 MapBlock *block = getBlockNoCreateNoEx(p);
2322 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2323 infostream<<"Generated "<<spos<<": "
2324 <<analyze_block(block)<<std::endl;
2332 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2334 DSTACKF("%s: p2d=(%d,%d)",
2339 Check if it exists already in memory
2341 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2346 Try to load it from disk (with blocks)
2348 //if(loadSectorFull(p2d) == true)
2351 Try to load metadata from disk
2354 if(loadSectorMeta(p2d) == true)
2356 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2359 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2360 throw InvalidPositionException("");
2366 Do not create over-limit
2368 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2369 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2370 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2371 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2372 throw InvalidPositionException("createSector(): pos. over limit");
2375 Generate blank sector
2378 sector = new ServerMapSector(this, p2d, m_gamedef);
2380 // Sector position on map in nodes
2381 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2386 m_sectors.insert(p2d, sector);
2392 This is a quick-hand function for calling makeBlock().
2394 MapBlock * ServerMap::generateBlock(
2396 core::map<v3s16, MapBlock*> &modified_blocks
2399 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2401 /*infostream<<"generateBlock(): "
2402 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2405 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2407 TimeTaker timer("generateBlock");
2409 //MapBlock *block = original_dummy;
2411 v2s16 p2d(p.X, p.Z);
2412 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2415 Do not generate over-limit
2417 if(blockpos_over_limit(p))
2419 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2420 throw InvalidPositionException("generateBlock(): pos. over limit");
2424 Create block make data
2426 mapgen::BlockMakeData data;
2427 initBlockMake(&data, p);
2433 TimeTaker t("mapgen::make_block()");
2434 mapgen::make_block(&data);
2436 if(enable_mapgen_debug_info == false)
2437 t.stop(true); // Hide output
2441 Blit data back on map, update lighting, add mobs and whatever this does
2443 finishBlockMake(&data, modified_blocks);
2448 MapBlock *block = getBlockNoCreateNoEx(p);
2456 bool erroneus_content = false;
2457 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2458 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2459 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2462 MapNode n = block->getNode(p);
2463 if(n.getContent() == CONTENT_IGNORE)
2465 infostream<<"CONTENT_IGNORE at "
2466 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2468 erroneus_content = true;
2472 if(erroneus_content)
2481 Generate a completely empty block
2485 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2486 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2488 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2491 n.setContent(CONTENT_AIR);
2492 block->setNode(v3s16(x0,y0,z0), n);
2498 if(enable_mapgen_debug_info == false)
2499 timer.stop(true); // Hide output
2504 MapBlock * ServerMap::createBlock(v3s16 p)
2506 DSTACKF("%s: p=(%d,%d,%d)",
2507 __FUNCTION_NAME, p.X, p.Y, p.Z);
2510 Do not create over-limit
2512 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2513 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2514 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2515 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2516 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2517 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2518 throw InvalidPositionException("createBlock(): pos. over limit");
2520 v2s16 p2d(p.X, p.Z);
2523 This will create or load a sector if not found in memory.
2524 If block exists on disk, it will be loaded.
2526 NOTE: On old save formats, this will be slow, as it generates
2527 lighting on blocks for them.
2529 ServerMapSector *sector;
2531 sector = (ServerMapSector*)createSector(p2d);
2532 assert(sector->getId() == MAPSECTOR_SERVER);
2534 catch(InvalidPositionException &e)
2536 infostream<<"createBlock: createSector() failed"<<std::endl;
2540 NOTE: This should not be done, or at least the exception
2541 should not be passed on as std::exception, because it
2542 won't be catched at all.
2544 /*catch(std::exception &e)
2546 infostream<<"createBlock: createSector() failed: "
2547 <<e.what()<<std::endl;
2552 Try to get a block from the sector
2555 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2558 if(block->isDummy())
2563 block = sector->createBlankBlock(block_y);
2567 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2569 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2571 p.X, p.Y, p.Z, allow_generate);
2574 MapBlock *block = getBlockNoCreateNoEx(p);
2575 if(block && block->isDummy() == false)
2580 MapBlock *block = loadBlock(p);
2587 core::map<v3s16, MapBlock*> modified_blocks;
2588 MapBlock *block = generateBlock(p, modified_blocks);
2592 event.type = MEET_OTHER;
2595 // Copy modified_blocks to event
2596 for(core::map<v3s16, MapBlock*>::Iterator
2597 i = modified_blocks.getIterator();
2598 i.atEnd()==false; i++)
2600 event.modified_blocks.insert(i.getNode()->getKey(), false);
2604 dispatchEvent(&event);
2613 s16 ServerMap::findGroundLevel(v2s16 p2d)
2617 Uh, just do something random...
2619 // Find existing map from top to down
2622 v3s16 p(p2d.X, max, p2d.Y);
2623 for(; p.Y>min; p.Y--)
2625 MapNode n = getNodeNoEx(p);
2626 if(n.getContent() != CONTENT_IGNORE)
2631 // If this node is not air, go to plan b
2632 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2634 // Search existing walkable and return it
2635 for(; p.Y>min; p.Y--)
2637 MapNode n = getNodeNoEx(p);
2638 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2647 Determine from map generator noise functions
2650 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2653 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2654 //return (s16)level;
2657 void ServerMap::createDatabase() {
2660 e = sqlite3_exec(m_database,
2661 "CREATE TABLE IF NOT EXISTS `blocks` ("
2662 "`pos` INT NOT NULL PRIMARY KEY,"
2665 , NULL, NULL, NULL);
2666 if(e == SQLITE_ABORT)
2667 throw FileNotGoodException("Could not create database structure");
2669 infostream<<"Server: Database structure was created";
2672 void ServerMap::verifyDatabase() {
2677 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2678 bool needs_create = false;
2682 Open the database connection
2685 createDirs(m_savedir);
2687 if(!fs::PathExists(dbp))
2688 needs_create = true;
2690 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2691 if(d != SQLITE_OK) {
2692 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2693 throw FileNotGoodException("Cannot open database file");
2699 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2700 if(d != SQLITE_OK) {
2701 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2702 throw FileNotGoodException("Cannot prepare read statement");
2705 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2706 if(d != SQLITE_OK) {
2707 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2708 throw FileNotGoodException("Cannot prepare write statement");
2711 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2712 if(d != SQLITE_OK) {
2713 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2714 throw FileNotGoodException("Cannot prepare read statement");
2717 infostream<<"Server: Database opened"<<std::endl;
2721 bool ServerMap::loadFromFolders() {
2722 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2727 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2728 return (sqlite3_int64)pos.Z*16777216 +
2729 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2732 void ServerMap::createDirs(std::string path)
2734 if(fs::CreateAllDirs(path) == false)
2736 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2737 <<"\""<<path<<"\""<<std::endl;
2738 throw BaseException("ServerMap failed to create directory");
2742 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2748 snprintf(cc, 9, "%.4x%.4x",
2749 (unsigned int)pos.X&0xffff,
2750 (unsigned int)pos.Y&0xffff);
2752 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2754 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2755 (unsigned int)pos.X&0xfff,
2756 (unsigned int)pos.Y&0xfff);
2758 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2764 v2s16 ServerMap::getSectorPos(std::string dirname)
2768 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2769 assert(spos != std::string::npos);
2770 if(dirname.size() - spos == 8)
2773 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2775 else if(dirname.size() - spos == 3)
2778 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2779 // Sign-extend the 12 bit values up to 16 bits...
2780 if(x&0x800) x|=0xF000;
2781 if(y&0x800) y|=0xF000;
2788 v2s16 pos((s16)x, (s16)y);
2792 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2794 v2s16 p2d = getSectorPos(sectordir);
2796 if(blockfile.size() != 4){
2797 throw InvalidFilenameException("Invalid block filename");
2800 int r = sscanf(blockfile.c_str(), "%4x", &y);
2802 throw InvalidFilenameException("Invalid block filename");
2803 return v3s16(p2d.X, y, p2d.Y);
2806 std::string ServerMap::getBlockFilename(v3s16 p)
2809 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2813 void ServerMap::save(bool only_changed)
2815 DSTACK(__FUNCTION_NAME);
2816 if(m_map_saving_enabled == false)
2818 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2822 if(only_changed == false)
2823 infostream<<"ServerMap: Saving whole map, this can take time."
2826 if(only_changed == false || m_map_metadata_changed)
2831 // Profile modified reasons
2832 Profiler modprofiler;
2834 u32 sector_meta_count = 0;
2835 u32 block_count = 0;
2836 u32 block_count_all = 0; // Number of blocks in memory
2838 // Don't do anything with sqlite unless something is really saved
2839 bool save_started = false;
2841 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2842 for(; i.atEnd() == false; i++)
2844 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2845 assert(sector->getId() == MAPSECTOR_SERVER);
2847 if(sector->differs_from_disk || only_changed == false)
2849 saveSectorMeta(sector);
2850 sector_meta_count++;
2852 core::list<MapBlock*> blocks;
2853 sector->getBlocks(blocks);
2854 core::list<MapBlock*>::Iterator j;
2856 for(j=blocks.begin(); j!=blocks.end(); j++)
2858 MapBlock *block = *j;
2862 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2863 || only_changed == false)
2868 save_started = true;
2871 modprofiler.add(block->getModifiedReason(), 1);
2876 /*infostream<<"ServerMap: Written block ("
2877 <<block->getPos().X<<","
2878 <<block->getPos().Y<<","
2879 <<block->getPos().Z<<")"
2888 Only print if something happened or saved whole map
2890 if(only_changed == false || sector_meta_count != 0
2891 || block_count != 0)
2893 infostream<<"ServerMap: Written: "
2894 <<sector_meta_count<<" sector metadata files, "
2895 <<block_count<<" block files"
2896 <<", "<<block_count_all<<" blocks in memory."
2898 PrintInfo(infostream); // ServerMap/ClientMap:
2899 infostream<<"Blocks modified by: "<<std::endl;
2900 modprofiler.print(infostream);
2904 static s32 unsignedToSigned(s32 i, s32 max_positive)
2906 if(i < max_positive)
2909 return i - 2*max_positive;
2912 // modulo of a negative number does not work consistently in C
2913 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2917 return mod - ((-i) % mod);
2920 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2922 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2924 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2926 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2927 return v3s16(x,y,z);
2930 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2932 if(loadFromFolders()){
2933 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2934 <<"all blocks that are stored in flat files"<<std::endl;
2940 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2942 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2943 v3s16 p = getIntegerAsBlock(block_i);
2944 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2950 void ServerMap::saveMapMeta()
2952 DSTACK(__FUNCTION_NAME);
2954 infostream<<"ServerMap::saveMapMeta(): "
2958 createDirs(m_savedir);
2960 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2961 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2962 if(os.good() == false)
2964 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2965 <<"could not open"<<fullpath<<std::endl;
2966 throw FileNotGoodException("Cannot open chunk metadata");
2970 params.setU64("seed", m_seed);
2972 params.writeLines(os);
2974 os<<"[end_of_params]\n";
2976 m_map_metadata_changed = false;
2979 void ServerMap::loadMapMeta()
2981 DSTACK(__FUNCTION_NAME);
2983 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2986 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2987 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2988 if(is.good() == false)
2990 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2991 <<"could not open"<<fullpath<<std::endl;
2992 throw FileNotGoodException("Cannot open map metadata");
3000 throw SerializationError
3001 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3003 std::getline(is, line);
3004 std::string trimmedline = trim(line);
3005 if(trimmedline == "[end_of_params]")
3007 params.parseConfigLine(line);
3010 m_seed = params.getU64("seed");
3012 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3015 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3017 DSTACK(__FUNCTION_NAME);
3018 // Format used for writing
3019 u8 version = SER_FMT_VER_HIGHEST;
3021 v2s16 pos = sector->getPos();
3022 std::string dir = getSectorDir(pos);
3025 std::string fullpath = dir + DIR_DELIM + "meta";
3026 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3027 if(o.good() == false)
3028 throw FileNotGoodException("Cannot open sector metafile");
3030 sector->serialize(o, version);
3032 sector->differs_from_disk = false;
3035 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3037 DSTACK(__FUNCTION_NAME);
3039 v2s16 p2d = getSectorPos(sectordir);
3041 ServerMapSector *sector = NULL;
3043 std::string fullpath = sectordir + DIR_DELIM + "meta";
3044 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3045 if(is.good() == false)
3047 // If the directory exists anyway, it probably is in some old
3048 // format. Just go ahead and create the sector.
3049 if(fs::PathExists(sectordir))
3051 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3052 <<fullpath<<" doesn't exist but directory does."
3053 <<" Continuing with a sector with no metadata."
3055 sector = new ServerMapSector(this, p2d, m_gamedef);
3056 m_sectors.insert(p2d, sector);
3060 throw FileNotGoodException("Cannot open sector metafile");
3065 sector = ServerMapSector::deSerialize
3066 (is, this, p2d, m_sectors, m_gamedef);
3068 saveSectorMeta(sector);
3071 sector->differs_from_disk = false;
3076 bool ServerMap::loadSectorMeta(v2s16 p2d)
3078 DSTACK(__FUNCTION_NAME);
3080 MapSector *sector = NULL;
3082 // The directory layout we're going to load from.
3083 // 1 - original sectors/xxxxzzzz/
3084 // 2 - new sectors2/xxx/zzz/
3085 // If we load from anything but the latest structure, we will
3086 // immediately save to the new one, and remove the old.
3088 std::string sectordir1 = getSectorDir(p2d, 1);
3089 std::string sectordir;
3090 if(fs::PathExists(sectordir1))
3092 sectordir = sectordir1;
3097 sectordir = getSectorDir(p2d, 2);
3101 sector = loadSectorMeta(sectordir, loadlayout != 2);
3103 catch(InvalidFilenameException &e)
3107 catch(FileNotGoodException &e)
3111 catch(std::exception &e)
3120 bool ServerMap::loadSectorFull(v2s16 p2d)
3122 DSTACK(__FUNCTION_NAME);
3124 MapSector *sector = NULL;
3126 // The directory layout we're going to load from.
3127 // 1 - original sectors/xxxxzzzz/
3128 // 2 - new sectors2/xxx/zzz/
3129 // If we load from anything but the latest structure, we will
3130 // immediately save to the new one, and remove the old.
3132 std::string sectordir1 = getSectorDir(p2d, 1);
3133 std::string sectordir;
3134 if(fs::PathExists(sectordir1))
3136 sectordir = sectordir1;
3141 sectordir = getSectorDir(p2d, 2);
3145 sector = loadSectorMeta(sectordir, loadlayout != 2);
3147 catch(InvalidFilenameException &e)
3151 catch(FileNotGoodException &e)
3155 catch(std::exception &e)
3163 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3165 std::vector<fs::DirListNode>::iterator i2;
3166 for(i2=list2.begin(); i2!=list2.end(); i2++)
3172 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3174 catch(InvalidFilenameException &e)
3176 // This catches unknown crap in directory
3182 infostream<<"Sector converted to new layout - deleting "<<
3183 sectordir1<<std::endl;
3184 fs::RecursiveDelete(sectordir1);
3191 void ServerMap::beginSave() {
3193 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3194 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3197 void ServerMap::endSave() {
3199 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3200 infostream<<"WARNING: endSave() failed, map might not have saved.";
3203 void ServerMap::saveBlock(MapBlock *block)
3205 DSTACK(__FUNCTION_NAME);
3207 Dummy blocks are not written
3209 if(block->isDummy())
3211 /*v3s16 p = block->getPos();
3212 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3213 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3217 // Format used for writing
3218 u8 version = SER_FMT_VER_HIGHEST;
3220 v3s16 p3d = block->getPos();
3224 v2s16 p2d(p3d.X, p3d.Z);
3225 std::string sectordir = getSectorDir(p2d);
3227 createDirs(sectordir);
3229 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3230 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3231 if(o.good() == false)
3232 throw FileNotGoodException("Cannot open block data");
3235 [0] u8 serialization version
3241 std::ostringstream o(std::ios_base::binary);
3243 o.write((char*)&version, 1);
3246 block->serialize(o, version);
3248 // Write extra data stored on disk
3249 block->serializeDiskExtra(o, version);
3251 // Write block to database
3253 std::string tmp = o.str();
3254 const char *bytes = tmp.c_str();
3256 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3257 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3258 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3259 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3260 int written = sqlite3_step(m_database_write);
3261 if(written != SQLITE_DONE)
3262 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3263 <<sqlite3_errmsg(m_database)<<std::endl;
3264 // Make ready for later reuse
3265 sqlite3_reset(m_database_write);
3267 // We just wrote it to the disk so clear modified flag
3268 block->resetModified();
3271 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3273 DSTACK(__FUNCTION_NAME);
3275 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3278 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3279 if(is.good() == false)
3280 throw FileNotGoodException("Cannot open block file");
3282 v3s16 p3d = getBlockPos(sectordir, blockfile);
3283 v2s16 p2d(p3d.X, p3d.Z);
3285 assert(sector->getPos() == p2d);
3287 u8 version = SER_FMT_VER_INVALID;
3288 is.read((char*)&version, 1);
3291 throw SerializationError("ServerMap::loadBlock(): Failed"
3292 " to read MapBlock version");
3294 /*u32 block_size = MapBlock::serializedLength(version);
3295 SharedBuffer<u8> data(block_size);
3296 is.read((char*)*data, block_size);*/
3298 // This will always return a sector because we're the server
3299 //MapSector *sector = emergeSector(p2d);
3301 MapBlock *block = NULL;
3302 bool created_new = false;
3303 block = sector->getBlockNoCreateNoEx(p3d.Y);
3306 block = sector->createBlankBlockNoInsert(p3d.Y);
3311 block->deSerialize(is, version);
3313 // Read extra data stored on disk
3314 block->deSerializeDiskExtra(is, version);
3316 // If it's a new block, insert it to the map
3318 sector->insertBlock(block);
3321 Save blocks loaded in old format in new format
3324 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3328 // Should be in database now, so delete the old file
3329 fs::RecursiveDelete(fullpath);
3332 // We just loaded it from the disk, so it's up-to-date.
3333 block->resetModified();
3336 catch(SerializationError &e)
3338 infostream<<"WARNING: Invalid block data on disk "
3339 <<"fullpath="<<fullpath
3340 <<" (SerializationError). "
3341 <<"what()="<<e.what()
3343 //" Ignoring. A new one will be generated.
3346 // TODO: Backup file; name is in fullpath.
3350 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3352 DSTACK(__FUNCTION_NAME);
3355 std::istringstream is(*blob, std::ios_base::binary);
3357 u8 version = SER_FMT_VER_INVALID;
3358 is.read((char*)&version, 1);
3361 throw SerializationError("ServerMap::loadBlock(): Failed"
3362 " to read MapBlock version");
3364 /*u32 block_size = MapBlock::serializedLength(version);
3365 SharedBuffer<u8> data(block_size);
3366 is.read((char*)*data, block_size);*/
3368 // This will always return a sector because we're the server
3369 //MapSector *sector = emergeSector(p2d);
3371 MapBlock *block = NULL;
3372 bool created_new = false;
3373 block = sector->getBlockNoCreateNoEx(p3d.Y);
3376 block = sector->createBlankBlockNoInsert(p3d.Y);
3381 block->deSerialize(is, version);
3383 // Read extra data stored on disk
3384 block->deSerializeDiskExtra(is, version);
3386 // If it's a new block, insert it to the map
3388 sector->insertBlock(block);
3391 Save blocks loaded in old format in new format
3394 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3395 // Only save if asked to; no need to update version
3399 // We just loaded it from, so it's up-to-date.
3400 block->resetModified();
3403 catch(SerializationError &e)
3405 infostream<<"WARNING: Invalid block data in database "
3406 <<" (SerializationError). "
3407 <<"what()="<<e.what()
3409 //" Ignoring. A new one will be generated.
3412 // TODO: Copy to a backup database.
3416 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3418 DSTACK(__FUNCTION_NAME);
3420 v2s16 p2d(blockpos.X, blockpos.Z);
3422 if(!loadFromFolders()) {
3425 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3426 infostream<<"WARNING: Could not bind block position for load: "
3427 <<sqlite3_errmsg(m_database)<<std::endl;
3428 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3430 Make sure sector is loaded
3432 MapSector *sector = createSector(p2d);
3437 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3438 size_t len = sqlite3_column_bytes(m_database_read, 0);
3440 std::string datastr(data, len);
3442 loadBlock(&datastr, blockpos, sector, false);
3444 sqlite3_step(m_database_read);
3445 // We should never get more than 1 row, so ok to reset
3446 sqlite3_reset(m_database_read);
3448 return getBlockNoCreateNoEx(blockpos);
3450 sqlite3_reset(m_database_read);
3452 // Not found in database, try the files
3455 // The directory layout we're going to load from.
3456 // 1 - original sectors/xxxxzzzz/
3457 // 2 - new sectors2/xxx/zzz/
3458 // If we load from anything but the latest structure, we will
3459 // immediately save to the new one, and remove the old.
3461 std::string sectordir1 = getSectorDir(p2d, 1);
3462 std::string sectordir;
3463 if(fs::PathExists(sectordir1))
3465 sectordir = sectordir1;
3470 sectordir = getSectorDir(p2d, 2);
3474 Make sure sector is loaded
3476 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3480 sector = loadSectorMeta(sectordir, loadlayout != 2);
3482 catch(InvalidFilenameException &e)
3486 catch(FileNotGoodException &e)
3490 catch(std::exception &e)
3497 Make sure file exists
3500 std::string blockfilename = getBlockFilename(blockpos);
3501 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3505 Load block and save it to the database
3507 loadBlock(sectordir, blockfilename, sector, true);
3508 return getBlockNoCreateNoEx(blockpos);
3511 void ServerMap::PrintInfo(std::ostream &out)
3522 ClientMap::ClientMap(
3525 MapDrawControl &control,
3526 scene::ISceneNode* parent,
3527 scene::ISceneManager* mgr,
3530 Map(dout_client, gamedef),
3531 scene::ISceneNode(parent, mgr, id),
3534 m_camera_position(0,0,0),
3535 m_camera_direction(0,0,1),
3538 m_camera_mutex.Init();
3539 assert(m_camera_mutex.IsInitialized());
3541 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3542 BS*1000000,BS*1000000,BS*1000000);
3545 ClientMap::~ClientMap()
3547 /*JMutexAutoLock lock(mesh_mutex);
3556 MapSector * ClientMap::emergeSector(v2s16 p2d)
3558 DSTACK(__FUNCTION_NAME);
3559 // Check that it doesn't exist already
3561 return getSectorNoGenerate(p2d);
3563 catch(InvalidPositionException &e)
3568 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3571 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3572 m_sectors.insert(p2d, sector);
3579 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3581 DSTACK(__FUNCTION_NAME);
3582 ClientMapSector *sector = NULL;
3584 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3586 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3590 sector = (ClientMapSector*)n->getValue();
3591 assert(sector->getId() == MAPSECTOR_CLIENT);
3595 sector = new ClientMapSector(this, p2d);
3597 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3598 m_sectors.insert(p2d, sector);
3602 sector->deSerialize(is);
3606 void ClientMap::OnRegisterSceneNode()
3610 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3611 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3614 ISceneNode::OnRegisterSceneNode();
3617 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3618 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3620 float d0 = (float)BS * p0.getDistanceFrom(p1);
3622 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3624 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3626 for(float s=start_off; s<d0+end_off; s+=step){
3627 v3f pf = p0f + uf * s;
3628 v3s16 p = floatToInt(pf, BS);
3629 MapNode n = map->getNodeNoEx(p);
3630 bool is_transparent = false;
3631 const ContentFeatures &f = nodemgr->get(n);
3632 if(f.solidness == 0)
3633 is_transparent = (f.visual_solidness != 2);
3635 is_transparent = (f.solidness != 2);
3636 if(!is_transparent){
3638 if(count >= needed_count)
3646 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3648 INodeDefManager *nodemgr = m_gamedef->ndef();
3650 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3651 DSTACK(__FUNCTION_NAME);
3653 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3656 if(pass == scene::ESNRP_SOLID)
3657 prefix = "CM: solid: ";
3659 prefix = "CM: transparent: ";
3662 This is called two times per frame, reset on the non-transparent one
3664 if(pass == scene::ESNRP_SOLID)
3666 m_last_drawn_sectors.clear();
3670 Get time for measuring timeout.
3672 Measuring time is very useful for long delays when the
3673 machine is swapping a lot.
3675 int time1 = time(0);
3677 //u32 daynight_ratio = m_client->getDayNightRatio();
3679 m_camera_mutex.Lock();
3680 v3f camera_position = m_camera_position;
3681 v3f camera_direction = m_camera_direction;
3682 f32 camera_fov = m_camera_fov;
3683 m_camera_mutex.Unlock();
3686 Get all blocks and draw all visible ones
3689 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3691 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3693 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3694 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3696 // Take a fair amount as we will be dropping more out later
3697 // Umm... these additions are a bit strange but they are needed.
3699 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3700 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3701 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3703 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3704 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3705 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3707 u32 vertex_count = 0;
3708 u32 meshbuffer_count = 0;
3710 // For limiting number of mesh updates per frame
3711 u32 mesh_update_count = 0;
3713 // Number of blocks in rendering range
3714 u32 blocks_in_range = 0;
3715 // Number of blocks occlusion culled
3716 u32 blocks_occlusion_culled = 0;
3717 // Number of blocks in rendering range but don't have a mesh
3718 u32 blocks_in_range_without_mesh = 0;
3719 // Blocks that had mesh that would have been drawn according to
3720 // rendering range (if max blocks limit didn't kick in)
3721 u32 blocks_would_have_drawn = 0;
3722 // Blocks that were drawn and had a mesh
3723 u32 blocks_drawn = 0;
3724 // Blocks which had a corresponding meshbuffer for this pass
3725 u32 blocks_had_pass_meshbuf = 0;
3726 // Blocks from which stuff was actually drawn
3727 u32 blocks_without_stuff = 0;
3730 Collect a set of blocks for drawing
3733 core::map<v3s16, MapBlock*> drawset;
3736 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3738 for(core::map<v2s16, MapSector*>::Iterator
3739 si = m_sectors.getIterator();
3740 si.atEnd() == false; si++)
3742 MapSector *sector = si.getNode()->getValue();
3743 v2s16 sp = sector->getPos();
3745 if(m_control.range_all == false)
3747 if(sp.X < p_blocks_min.X
3748 || sp.X > p_blocks_max.X
3749 || sp.Y < p_blocks_min.Z
3750 || sp.Y > p_blocks_max.Z)
3754 core::list< MapBlock * > sectorblocks;
3755 sector->getBlocks(sectorblocks);
3758 Loop through blocks in sector
3761 u32 sector_blocks_drawn = 0;
3763 core::list< MapBlock * >::Iterator i;
3764 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3766 MapBlock *block = *i;
3769 Compare block position to camera position, skip
3770 if not seen on display
3773 float range = 100000 * BS;
3774 if(m_control.range_all == false)
3775 range = m_control.wanted_range * BS;
3778 if(isBlockInSight(block->getPos(), camera_position,
3779 camera_direction, camera_fov,
3780 range, &d) == false)
3785 // This is ugly (spherical distance limit?)
3786 /*if(m_control.range_all == false &&
3787 d - 0.5*BS*MAP_BLOCKSIZE > range)
3794 Update expired mesh (used for day/night change)
3796 It doesn't work exactly like it should now with the
3797 tasked mesh update but whatever.
3800 bool mesh_expired = false;
3803 JMutexAutoLock lock(block->mesh_mutex);
3805 mesh_expired = block->getMeshExpired();
3807 // Mesh has not been expired and there is no mesh:
3808 // block has no content
3809 if(block->mesh == NULL && mesh_expired == false){
3810 blocks_in_range_without_mesh++;
3815 f32 faraway = BS*50;
3816 //f32 faraway = m_control.wanted_range * BS;
3819 This has to be done with the mesh_mutex unlocked
3821 // Pretty random but this should work somewhat nicely
3822 if(mesh_expired && (
3823 (mesh_update_count < 3
3824 && (d < faraway || mesh_update_count < 2)
3827 (m_control.range_all && mesh_update_count < 20)
3830 /*if(mesh_expired && mesh_update_count < 6
3831 && (d < faraway || mesh_update_count < 3))*/
3833 mesh_update_count++;
3835 // Mesh has been expired: generate new mesh
3836 //block->updateMesh(daynight_ratio);
3837 m_client->addUpdateMeshTask(block->getPos());
3839 mesh_expired = false;
3847 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3848 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3850 float stepfac = 1.1;
3851 float startoff = BS*1;
3852 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3853 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3854 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3855 u32 needed_count = 1;
3857 isOccluded(this, spn, cpn + v3s16(0,0,0),
3858 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3859 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3860 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3861 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3862 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3863 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3864 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3865 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3866 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3867 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3868 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3869 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3870 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3871 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3872 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3873 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3874 step, stepfac, startoff, endoff, needed_count, nodemgr)
3877 blocks_occlusion_culled++;
3881 // This block is in range. Reset usage timer.
3882 block->resetUsageTimer();
3885 Ignore if mesh doesn't exist
3888 JMutexAutoLock lock(block->mesh_mutex);
3890 scene::SMesh *mesh = block->mesh;
3893 blocks_in_range_without_mesh++;
3898 // Limit block count in case of a sudden increase
3899 blocks_would_have_drawn++;
3900 if(blocks_drawn >= m_control.wanted_max_blocks
3901 && m_control.range_all == false
3902 && d > m_control.wanted_min_range * BS)
3906 drawset[block->getPos()] = block;
3908 sector_blocks_drawn++;
3911 } // foreach sectorblocks
3913 if(sector_blocks_drawn != 0)
3914 m_last_drawn_sectors[sp] = true;
3919 Draw the selected MapBlocks
3923 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3925 int timecheck_counter = 0;
3926 for(core::map<v3s16, MapBlock*>::Iterator
3927 i = drawset.getIterator();
3928 i.atEnd() == false; i++)
3931 timecheck_counter++;
3932 if(timecheck_counter > 50)
3934 timecheck_counter = 0;
3935 int time2 = time(0);
3936 if(time2 > time1 + 4)
3938 infostream<<"ClientMap::renderMap(): "
3939 "Rendering takes ages, returning."
3946 MapBlock *block = i.getNode()->getValue();
3949 Draw the faces of the block
3952 JMutexAutoLock lock(block->mesh_mutex);
3954 scene::SMesh *mesh = block->mesh;
3957 u32 c = mesh->getMeshBufferCount();
3958 bool stuff_actually_drawn = false;
3959 for(u32 i=0; i<c; i++)
3961 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3962 const video::SMaterial& material = buf->getMaterial();
3963 video::IMaterialRenderer* rnd =
3964 driver->getMaterialRenderer(material.MaterialType);
3965 bool transparent = (rnd && rnd->isTransparent());
3966 // Render transparent on transparent pass and likewise.
3967 if(transparent == is_transparent_pass)
3969 if(buf->getVertexCount() == 0)
3970 errorstream<<"Block ["<<analyze_block(block)
3971 <<"] contains an empty meshbuf"<<std::endl;
3973 This *shouldn't* hurt too much because Irrlicht
3974 doesn't change opengl textures if the old
3975 material has the same texture.
3977 driver->setMaterial(buf->getMaterial());
3978 driver->drawMeshBuffer(buf);
3979 vertex_count += buf->getVertexCount();
3981 stuff_actually_drawn = true;
3984 if(stuff_actually_drawn)
3985 blocks_had_pass_meshbuf++;
3987 blocks_without_stuff++;
3992 // Log only on solid pass because values are the same
3993 if(pass == scene::ESNRP_SOLID){
3994 g_profiler->avg("CM: blocks in range", blocks_in_range);
3995 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3996 if(blocks_in_range != 0)
3997 g_profiler->avg("CM: blocks in range without mesh (frac)",
3998 (float)blocks_in_range_without_mesh/blocks_in_range);
3999 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4002 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4003 if(blocks_had_pass_meshbuf != 0)
4004 g_profiler->avg(prefix+"meshbuffers per block",
4005 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4006 if(blocks_drawn != 0)
4007 g_profiler->avg(prefix+"empty blocks (frac)",
4008 (float)blocks_without_stuff / blocks_drawn);
4010 m_control.blocks_drawn = blocks_drawn;
4011 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4013 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4014 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4017 void ClientMap::renderPostFx()
4019 INodeDefManager *nodemgr = m_gamedef->ndef();
4021 // Sadly ISceneManager has no "post effects" render pass, in that case we
4022 // could just register for that and handle it in renderMap().
4024 m_camera_mutex.Lock();
4025 v3f camera_position = m_camera_position;
4026 m_camera_mutex.Unlock();
4028 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4030 // - If the player is in a solid node, make everything black.
4031 // - If the player is in liquid, draw a semi-transparent overlay.
4032 const ContentFeatures& features = nodemgr->get(n);
4033 video::SColor post_effect_color = features.post_effect_color;
4034 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4036 post_effect_color = video::SColor(255, 0, 0, 0);
4038 if (post_effect_color.getAlpha() != 0)
4040 // Draw a full-screen rectangle
4041 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4042 v2u32 ss = driver->getScreenSize();
4043 core::rect<s32> rect(0,0, ss.X, ss.Y);
4044 driver->draw2DRectangle(post_effect_color, rect);
4048 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4049 core::map<v3s16, MapBlock*> *affected_blocks)
4051 bool changed = false;
4053 Add it to all blocks touching it
4056 v3s16(0,0,0), // this
4057 v3s16(0,0,1), // back
4058 v3s16(0,1,0), // top
4059 v3s16(1,0,0), // right
4060 v3s16(0,0,-1), // front
4061 v3s16(0,-1,0), // bottom
4062 v3s16(-1,0,0), // left
4064 for(u16 i=0; i<7; i++)
4066 v3s16 p2 = p + dirs[i];
4067 // Block position of neighbor (or requested) node
4068 v3s16 blockpos = getNodeBlockPos(p2);
4069 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4070 if(blockref == NULL)
4072 // Relative position of requested node
4073 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4074 if(blockref->setTempMod(relpos, mod))
4079 if(changed && affected_blocks!=NULL)
4081 for(u16 i=0; i<7; i++)
4083 v3s16 p2 = p + dirs[i];
4084 // Block position of neighbor (or requested) node
4085 v3s16 blockpos = getNodeBlockPos(p2);
4086 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4087 if(blockref == NULL)
4089 affected_blocks->insert(blockpos, blockref);
4095 bool ClientMap::clearTempMod(v3s16 p,
4096 core::map<v3s16, MapBlock*> *affected_blocks)
4098 bool changed = false;
4100 v3s16(0,0,0), // this
4101 v3s16(0,0,1), // back
4102 v3s16(0,1,0), // top
4103 v3s16(1,0,0), // right
4104 v3s16(0,0,-1), // front
4105 v3s16(0,-1,0), // bottom
4106 v3s16(-1,0,0), // left
4108 for(u16 i=0; i<7; i++)
4110 v3s16 p2 = p + dirs[i];
4111 // Block position of neighbor (or requested) node
4112 v3s16 blockpos = getNodeBlockPos(p2);
4113 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4114 if(blockref == NULL)
4116 // Relative position of requested node
4117 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4118 if(blockref->clearTempMod(relpos))
4123 if(changed && affected_blocks!=NULL)
4125 for(u16 i=0; i<7; i++)
4127 v3s16 p2 = p + dirs[i];
4128 // Block position of neighbor (or requested) node
4129 v3s16 blockpos = getNodeBlockPos(p2);
4130 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4131 if(blockref == NULL)
4133 affected_blocks->insert(blockpos, blockref);
4139 void ClientMap::expireMeshes(bool only_daynight_diffed)
4141 TimeTaker timer("expireMeshes()");
4143 core::map<v2s16, MapSector*>::Iterator si;
4144 si = m_sectors.getIterator();
4145 for(; si.atEnd() == false; si++)
4147 MapSector *sector = si.getNode()->getValue();
4149 core::list< MapBlock * > sectorblocks;
4150 sector->getBlocks(sectorblocks);
4152 core::list< MapBlock * >::Iterator i;
4153 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4155 MapBlock *block = *i;
4157 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4163 JMutexAutoLock lock(block->mesh_mutex);
4164 if(block->mesh != NULL)
4166 /*block->mesh->drop();
4167 block->mesh = NULL;*/
4168 block->setMeshExpired(true);
4175 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4177 assert(mapType() == MAPTYPE_CLIENT);
4180 v3s16 p = blockpos + v3s16(0,0,0);
4181 MapBlock *b = getBlockNoCreate(p);
4182 b->updateMesh(daynight_ratio);
4183 //b->setMeshExpired(true);
4185 catch(InvalidPositionException &e){}
4188 v3s16 p = blockpos + v3s16(-1,0,0);
4189 MapBlock *b = getBlockNoCreate(p);
4190 b->updateMesh(daynight_ratio);
4191 //b->setMeshExpired(true);
4193 catch(InvalidPositionException &e){}
4195 v3s16 p = blockpos + v3s16(0,-1,0);
4196 MapBlock *b = getBlockNoCreate(p);
4197 b->updateMesh(daynight_ratio);
4198 //b->setMeshExpired(true);
4200 catch(InvalidPositionException &e){}
4202 v3s16 p = blockpos + v3s16(0,0,-1);
4203 MapBlock *b = getBlockNoCreate(p);
4204 b->updateMesh(daynight_ratio);
4205 //b->setMeshExpired(true);
4207 catch(InvalidPositionException &e){}
4212 Update mesh of block in which the node is, and if the node is at the
4213 leading edge, update the appropriate leading blocks too.
4215 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4223 v3s16 blockposes[4];
4224 for(u32 i=0; i<4; i++)
4226 v3s16 np = nodepos + dirs[i];
4227 blockposes[i] = getNodeBlockPos(np);
4228 // Don't update mesh of block if it has been done already
4229 bool already_updated = false;
4230 for(u32 j=0; j<i; j++)
4232 if(blockposes[j] == blockposes[i])
4234 already_updated = true;
4241 MapBlock *b = getBlockNoCreate(blockposes[i]);
4242 b->updateMesh(daynight_ratio);
4247 void ClientMap::PrintInfo(std::ostream &out)
4258 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4263 MapVoxelManipulator::~MapVoxelManipulator()
4265 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4269 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4271 TimeTaker timer1("emerge", &emerge_time);
4273 // Units of these are MapBlocks
4274 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4275 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4277 VoxelArea block_area_nodes
4278 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4280 addArea(block_area_nodes);
4282 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4283 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4284 for(s32 x=p_min.X; x<=p_max.X; x++)
4287 core::map<v3s16, bool>::Node *n;
4288 n = m_loaded_blocks.find(p);
4292 bool block_data_inexistent = false;
4295 TimeTaker timer1("emerge load", &emerge_load_time);
4297 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4298 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4300 a.print(infostream);
4301 infostream<<std::endl;*/
4303 MapBlock *block = m_map->getBlockNoCreate(p);
4304 if(block->isDummy())
4305 block_data_inexistent = true;
4307 block->copyTo(*this);
4309 catch(InvalidPositionException &e)
4311 block_data_inexistent = true;
4314 if(block_data_inexistent)
4316 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4317 // Fill with VOXELFLAG_INEXISTENT
4318 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4319 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4321 s32 i = m_area.index(a.MinEdge.X,y,z);
4322 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4326 m_loaded_blocks.insert(p, !block_data_inexistent);
4329 //infostream<<"emerge done"<<std::endl;
4333 SUGG: Add an option to only update eg. water and air nodes.
4334 This will make it interfere less with important stuff if
4337 void MapVoxelManipulator::blitBack
4338 (core::map<v3s16, MapBlock*> & modified_blocks)
4340 if(m_area.getExtent() == v3s16(0,0,0))
4343 //TimeTaker timer1("blitBack");
4345 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4346 <<m_loaded_blocks.size()<<std::endl;*/
4349 Initialize block cache
4351 v3s16 blockpos_last;
4352 MapBlock *block = NULL;
4353 bool block_checked_in_modified = false;
4355 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4356 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4357 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4361 u8 f = m_flags[m_area.index(p)];
4362 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4365 MapNode &n = m_data[m_area.index(p)];
4367 v3s16 blockpos = getNodeBlockPos(p);
4372 if(block == NULL || blockpos != blockpos_last){
4373 block = m_map->getBlockNoCreate(blockpos);
4374 blockpos_last = blockpos;
4375 block_checked_in_modified = false;
4378 // Calculate relative position in block
4379 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4381 // Don't continue if nothing has changed here
4382 if(block->getNode(relpos) == n)
4385 //m_map->setNode(m_area.MinEdge + p, n);
4386 block->setNode(relpos, n);
4389 Make sure block is in modified_blocks
4391 if(block_checked_in_modified == false)
4393 modified_blocks[blockpos] = block;
4394 block_checked_in_modified = true;
4397 catch(InvalidPositionException &e)
4403 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4404 MapVoxelManipulator(map),
4405 m_create_area(false)
4409 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4413 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4415 // Just create the area so that it can be pointed to
4416 VoxelManipulator::emerge(a, caller_id);
4419 void ManualMapVoxelManipulator::initialEmerge(
4420 v3s16 blockpos_min, v3s16 blockpos_max)
4422 TimeTaker timer1("initialEmerge", &emerge_time);
4424 // Units of these are MapBlocks
4425 v3s16 p_min = blockpos_min;
4426 v3s16 p_max = blockpos_max;
4428 VoxelArea block_area_nodes
4429 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4431 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4434 infostream<<"initialEmerge: area: ";
4435 block_area_nodes.print(infostream);
4436 infostream<<" ("<<size_MB<<"MB)";
4437 infostream<<std::endl;
4440 addArea(block_area_nodes);
4442 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4443 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4444 for(s32 x=p_min.X; x<=p_max.X; x++)
4447 core::map<v3s16, bool>::Node *n;
4448 n = m_loaded_blocks.find(p);
4452 bool block_data_inexistent = false;
4455 TimeTaker timer1("emerge load", &emerge_load_time);
4457 MapBlock *block = m_map->getBlockNoCreate(p);
4458 if(block->isDummy())
4459 block_data_inexistent = true;
4461 block->copyTo(*this);
4463 catch(InvalidPositionException &e)
4465 block_data_inexistent = true;
4468 if(block_data_inexistent)
4471 Mark area inexistent
4473 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4474 // Fill with VOXELFLAG_INEXISTENT
4475 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4476 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4478 s32 i = m_area.index(a.MinEdge.X,y,z);
4479 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4483 m_loaded_blocks.insert(p, !block_data_inexistent);
4487 void ManualMapVoxelManipulator::blitBackAll(
4488 core::map<v3s16, MapBlock*> * modified_blocks)
4490 if(m_area.getExtent() == v3s16(0,0,0))
4494 Copy data of all blocks
4496 for(core::map<v3s16, bool>::Iterator
4497 i = m_loaded_blocks.getIterator();
4498 i.atEnd() == false; i++)
4500 v3s16 p = i.getNode()->getKey();
4501 bool existed = i.getNode()->getValue();
4502 if(existed == false)
4504 // The Great Bug was found using this
4505 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4506 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4510 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4513 infostream<<"WARNING: "<<__FUNCTION_NAME
4514 <<": got NULL block "
4515 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4520 block->copyFrom(*this);
4523 modified_blocks->insert(p, block);