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 // Never allow placing CONTENT_IGNORE, it fucks up stuff
212 if(n.getContent() == CONTENT_IGNORE){
213 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
214 <<" while trying to replace \""
215 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
216 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
217 debug_stacks_print_to(infostream);
220 block->setNodeNoCheck(relpos, n);
225 Goes recursively through the neighbours of the node.
227 Alters only transparent nodes.
229 If the lighting of the neighbour is lower than the lighting of
230 the node was (before changing it to 0 at the step before), the
231 lighting of the neighbour is set to 0 and then the same stuff
232 repeats for the neighbour.
234 The ending nodes of the routine are stored in light_sources.
235 This is useful when a light is removed. In such case, this
236 routine can be called for the light node and then again for
237 light_sources to re-light the area without the removed light.
239 values of from_nodes are lighting values.
241 void Map::unspreadLight(enum LightBank bank,
242 core::map<v3s16, u8> & from_nodes,
243 core::map<v3s16, bool> & light_sources,
244 core::map<v3s16, MapBlock*> & modified_blocks)
246 INodeDefManager *nodemgr = m_gamedef->ndef();
249 v3s16(0,0,1), // back
251 v3s16(1,0,0), // right
252 v3s16(0,0,-1), // front
253 v3s16(0,-1,0), // bottom
254 v3s16(-1,0,0), // left
257 if(from_nodes.size() == 0)
260 u32 blockchangecount = 0;
262 core::map<v3s16, u8> unlighted_nodes;
263 core::map<v3s16, u8>::Iterator j;
264 j = from_nodes.getIterator();
267 Initialize block cache
270 MapBlock *block = NULL;
271 // Cache this a bit, too
272 bool block_checked_in_modified = false;
274 for(; j.atEnd() == false; j++)
276 v3s16 pos = j.getNode()->getKey();
277 v3s16 blockpos = getNodeBlockPos(pos);
279 // Only fetch a new block if the block position has changed
281 if(block == NULL || blockpos != blockpos_last){
282 block = getBlockNoCreate(blockpos);
283 blockpos_last = blockpos;
285 block_checked_in_modified = false;
289 catch(InvalidPositionException &e)
297 // Calculate relative position in block
298 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
300 // Get node straight from the block
301 MapNode n = block->getNode(relpos);
303 u8 oldlight = j.getNode()->getValue();
305 // Loop through 6 neighbors
306 for(u16 i=0; i<6; i++)
308 // Get the position of the neighbor node
309 v3s16 n2pos = pos + dirs[i];
311 // Get the block where the node is located
312 v3s16 blockpos = getNodeBlockPos(n2pos);
316 // Only fetch a new block if the block position has changed
318 if(block == NULL || blockpos != blockpos_last){
319 block = getBlockNoCreate(blockpos);
320 blockpos_last = blockpos;
322 block_checked_in_modified = false;
326 catch(InvalidPositionException &e)
331 // Calculate relative position in block
332 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
333 // Get node straight from the block
334 MapNode n2 = block->getNode(relpos);
336 bool changed = false;
338 //TODO: Optimize output by optimizing light_sources?
341 If the neighbor is dimmer than what was specified
342 as oldlight (the light of the previous node)
344 if(n2.getLight(bank, nodemgr) < oldlight)
347 And the neighbor is transparent and it has some light
349 if(nodemgr->get(n2).light_propagates
350 && n2.getLight(bank, nodemgr) != 0)
353 Set light to 0 and add to queue
356 u8 current_light = n2.getLight(bank, nodemgr);
357 n2.setLight(bank, 0, nodemgr);
358 block->setNode(relpos, n2);
360 unlighted_nodes.insert(n2pos, current_light);
364 Remove from light_sources if it is there
365 NOTE: This doesn't happen nearly at all
367 /*if(light_sources.find(n2pos))
369 infostream<<"Removed from light_sources"<<std::endl;
370 light_sources.remove(n2pos);
375 if(light_sources.find(n2pos) != NULL)
376 light_sources.remove(n2pos);*/
379 light_sources.insert(n2pos, true);
382 // Add to modified_blocks
383 if(changed == true && block_checked_in_modified == false)
385 // If the block is not found in modified_blocks, add.
386 if(modified_blocks.find(blockpos) == NULL)
388 modified_blocks.insert(blockpos, block);
390 block_checked_in_modified = true;
393 catch(InvalidPositionException &e)
400 /*infostream<<"unspreadLight(): Changed block "
401 <<blockchangecount<<" times"
402 <<" for "<<from_nodes.size()<<" nodes"
405 if(unlighted_nodes.size() > 0)
406 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
410 A single-node wrapper of the above
412 void Map::unLightNeighbors(enum LightBank bank,
413 v3s16 pos, u8 lightwas,
414 core::map<v3s16, bool> & light_sources,
415 core::map<v3s16, MapBlock*> & modified_blocks)
417 core::map<v3s16, u8> from_nodes;
418 from_nodes.insert(pos, lightwas);
420 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
424 Lights neighbors of from_nodes, collects all them and then
427 void Map::spreadLight(enum LightBank bank,
428 core::map<v3s16, bool> & from_nodes,
429 core::map<v3s16, MapBlock*> & modified_blocks)
431 INodeDefManager *nodemgr = m_gamedef->ndef();
433 const v3s16 dirs[6] = {
434 v3s16(0,0,1), // back
436 v3s16(1,0,0), // right
437 v3s16(0,0,-1), // front
438 v3s16(0,-1,0), // bottom
439 v3s16(-1,0,0), // left
442 if(from_nodes.size() == 0)
445 u32 blockchangecount = 0;
447 core::map<v3s16, bool> lighted_nodes;
448 core::map<v3s16, bool>::Iterator j;
449 j = from_nodes.getIterator();
452 Initialize block cache
455 MapBlock *block = NULL;
456 // Cache this a bit, too
457 bool block_checked_in_modified = false;
459 for(; j.atEnd() == false; j++)
460 //for(; j != from_nodes.end(); j++)
462 v3s16 pos = j.getNode()->getKey();
464 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
465 v3s16 blockpos = getNodeBlockPos(pos);
467 // Only fetch a new block if the block position has changed
469 if(block == NULL || blockpos != blockpos_last){
470 block = getBlockNoCreate(blockpos);
471 blockpos_last = blockpos;
473 block_checked_in_modified = false;
477 catch(InvalidPositionException &e)
485 // Calculate relative position in block
486 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
488 // Get node straight from the block
489 MapNode n = block->getNode(relpos);
491 u8 oldlight = n.getLight(bank, nodemgr);
492 u8 newlight = diminish_light(oldlight);
494 // Loop through 6 neighbors
495 for(u16 i=0; i<6; i++){
496 // Get the position of the neighbor node
497 v3s16 n2pos = pos + dirs[i];
499 // Get the block where the node is located
500 v3s16 blockpos = getNodeBlockPos(n2pos);
504 // Only fetch a new block if the block position has changed
506 if(block == NULL || blockpos != blockpos_last){
507 block = getBlockNoCreate(blockpos);
508 blockpos_last = blockpos;
510 block_checked_in_modified = false;
514 catch(InvalidPositionException &e)
519 // Calculate relative position in block
520 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
521 // Get node straight from the block
522 MapNode n2 = block->getNode(relpos);
524 bool changed = false;
526 If the neighbor is brighter than the current node,
527 add to list (it will light up this node on its turn)
529 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
531 lighted_nodes.insert(n2pos, true);
532 //lighted_nodes.push_back(n2pos);
536 If the neighbor is dimmer than how much light this node
537 would spread on it, add to list
539 if(n2.getLight(bank, nodemgr) < newlight)
541 if(nodemgr->get(n2).light_propagates)
543 n2.setLight(bank, newlight, nodemgr);
544 block->setNode(relpos, n2);
545 lighted_nodes.insert(n2pos, true);
546 //lighted_nodes.push_back(n2pos);
551 // Add to modified_blocks
552 if(changed == true && block_checked_in_modified == false)
554 // If the block is not found in modified_blocks, add.
555 if(modified_blocks.find(blockpos) == NULL)
557 modified_blocks.insert(blockpos, block);
559 block_checked_in_modified = true;
562 catch(InvalidPositionException &e)
569 /*infostream<<"spreadLight(): Changed block "
570 <<blockchangecount<<" times"
571 <<" for "<<from_nodes.size()<<" nodes"
574 if(lighted_nodes.size() > 0)
575 spreadLight(bank, lighted_nodes, modified_blocks);
579 A single-node source variation of the above.
581 void Map::lightNeighbors(enum LightBank bank,
583 core::map<v3s16, MapBlock*> & modified_blocks)
585 core::map<v3s16, bool> from_nodes;
586 from_nodes.insert(pos, true);
587 spreadLight(bank, from_nodes, modified_blocks);
590 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
592 INodeDefManager *nodemgr = m_gamedef->ndef();
595 v3s16(0,0,1), // back
597 v3s16(1,0,0), // right
598 v3s16(0,0,-1), // front
599 v3s16(0,-1,0), // bottom
600 v3s16(-1,0,0), // left
603 u8 brightest_light = 0;
604 v3s16 brightest_pos(0,0,0);
605 bool found_something = false;
607 // Loop through 6 neighbors
608 for(u16 i=0; i<6; i++){
609 // Get the position of the neighbor node
610 v3s16 n2pos = p + dirs[i];
615 catch(InvalidPositionException &e)
619 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
620 brightest_light = n2.getLight(bank, nodemgr);
621 brightest_pos = n2pos;
622 found_something = true;
626 if(found_something == false)
627 throw InvalidPositionException();
629 return brightest_pos;
633 Propagates sunlight down from a node.
634 Starting point gets sunlight.
636 Returns the lowest y value of where the sunlight went.
638 Mud is turned into grass in where the sunlight stops.
640 s16 Map::propagateSunlight(v3s16 start,
641 core::map<v3s16, MapBlock*> & modified_blocks)
643 INodeDefManager *nodemgr = m_gamedef->ndef();
648 v3s16 pos(start.X, y, start.Z);
650 v3s16 blockpos = getNodeBlockPos(pos);
653 block = getBlockNoCreate(blockpos);
655 catch(InvalidPositionException &e)
660 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
661 MapNode n = block->getNode(relpos);
663 if(nodemgr->get(n).sunlight_propagates)
665 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
666 block->setNode(relpos, n);
668 modified_blocks.insert(blockpos, block);
672 // Sunlight goes no further
679 void Map::updateLighting(enum LightBank bank,
680 core::map<v3s16, MapBlock*> & a_blocks,
681 core::map<v3s16, MapBlock*> & modified_blocks)
683 INodeDefManager *nodemgr = m_gamedef->ndef();
685 /*m_dout<<DTIME<<"Map::updateLighting(): "
686 <<a_blocks.size()<<" blocks."<<std::endl;*/
688 //TimeTaker timer("updateLighting");
692 //u32 count_was = modified_blocks.size();
694 core::map<v3s16, MapBlock*> blocks_to_update;
696 core::map<v3s16, bool> light_sources;
698 core::map<v3s16, u8> unlight_from;
700 core::map<v3s16, MapBlock*>::Iterator i;
701 i = a_blocks.getIterator();
702 for(; i.atEnd() == false; i++)
704 MapBlock *block = i.getNode()->getValue();
708 // Don't bother with dummy blocks.
712 v3s16 pos = block->getPos();
713 modified_blocks.insert(pos, block);
715 blocks_to_update.insert(pos, block);
718 Clear all light from block
720 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
721 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
722 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
727 MapNode n = block->getNode(v3s16(x,y,z));
728 u8 oldlight = n.getLight(bank, nodemgr);
729 n.setLight(bank, 0, nodemgr);
730 block->setNode(v3s16(x,y,z), n);
732 // Collect borders for unlighting
733 if(x==0 || x == MAP_BLOCKSIZE-1
734 || y==0 || y == MAP_BLOCKSIZE-1
735 || z==0 || z == MAP_BLOCKSIZE-1)
737 v3s16 p_map = p + v3s16(
740 MAP_BLOCKSIZE*pos.Z);
741 unlight_from.insert(p_map, oldlight);
744 catch(InvalidPositionException &e)
747 This would happen when dealing with a
751 infostream<<"updateLighting(): InvalidPositionException"
756 if(bank == LIGHTBANK_DAY)
758 bool bottom_valid = block->propagateSunlight(light_sources);
760 // If bottom is valid, we're done.
764 else if(bank == LIGHTBANK_NIGHT)
766 // For night lighting, sunlight is not propagated
771 // Invalid lighting bank
775 /*infostream<<"Bottom for sunlight-propagated block ("
776 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
779 // Bottom sunlight is not valid; get the block and loop to it
783 block = getBlockNoCreate(pos);
785 catch(InvalidPositionException &e)
794 Enable this to disable proper lighting for speeding up map
795 generation for testing or whatever
798 //if(g_settings->get(""))
800 core::map<v3s16, MapBlock*>::Iterator i;
801 i = blocks_to_update.getIterator();
802 for(; i.atEnd() == false; i++)
804 MapBlock *block = i.getNode()->getValue();
805 v3s16 p = block->getPos();
806 block->setLightingExpired(false);
814 TimeTaker timer("unspreadLight");
815 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
820 u32 diff = modified_blocks.size() - count_was;
821 count_was = modified_blocks.size();
822 infostream<<"unspreadLight modified "<<diff<<std::endl;
826 TimeTaker timer("spreadLight");
827 spreadLight(bank, light_sources, modified_blocks);
832 u32 diff = modified_blocks.size() - count_was;
833 count_was = modified_blocks.size();
834 infostream<<"spreadLight modified "<<diff<<std::endl;
839 //MapVoxelManipulator vmanip(this);
841 // Make a manual voxel manipulator and load all the blocks
842 // that touch the requested blocks
843 ManualMapVoxelManipulator vmanip(this);
844 core::map<v3s16, MapBlock*>::Iterator i;
845 i = blocks_to_update.getIterator();
846 for(; i.atEnd() == false; i++)
848 MapBlock *block = i.getNode()->getValue();
849 v3s16 p = block->getPos();
851 // Add all surrounding blocks
852 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
855 Add all surrounding blocks that have up-to-date lighting
856 NOTE: This doesn't quite do the job (not everything
857 appropriate is lighted)
859 /*for(s16 z=-1; z<=1; z++)
860 for(s16 y=-1; y<=1; y++)
861 for(s16 x=-1; x<=1; x++)
863 v3s16 p2 = p + v3s16(x,y,z);
864 MapBlock *block = getBlockNoCreateNoEx(p2);
869 if(block->getLightingExpired())
871 vmanip.initialEmerge(p2, p2);
874 // Lighting of block will be updated completely
875 block->setLightingExpired(false);
879 //TimeTaker timer("unSpreadLight");
880 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
883 //TimeTaker timer("spreadLight");
884 vmanip.spreadLight(bank, light_sources, nodemgr);
887 //TimeTaker timer("blitBack");
888 vmanip.blitBack(modified_blocks);
890 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
894 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
897 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
898 core::map<v3s16, MapBlock*> & modified_blocks)
900 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
901 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
904 Update information about whether day and night light differ
906 for(core::map<v3s16, MapBlock*>::Iterator
907 i = modified_blocks.getIterator();
908 i.atEnd() == false; i++)
910 MapBlock *block = i.getNode()->getValue();
911 block->updateDayNightDiff();
917 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
918 core::map<v3s16, MapBlock*> &modified_blocks)
920 INodeDefManager *nodemgr = m_gamedef->ndef();
923 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
924 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
927 From this node to nodes underneath:
928 If lighting is sunlight (1.0), unlight neighbours and
933 v3s16 toppos = p + v3s16(0,1,0);
934 v3s16 bottompos = p + v3s16(0,-1,0);
936 bool node_under_sunlight = true;
937 core::map<v3s16, bool> light_sources;
940 If there is a node at top and it doesn't have sunlight,
941 there has not been any sunlight going down.
943 Otherwise there probably is.
946 MapNode topnode = getNode(toppos);
948 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
949 node_under_sunlight = false;
951 catch(InvalidPositionException &e)
956 Remove all light that has come out of this node
959 enum LightBank banks[] =
964 for(s32 i=0; i<2; i++)
966 enum LightBank bank = banks[i];
968 u8 lightwas = getNode(p).getLight(bank, nodemgr);
970 // Add the block of the added node to modified_blocks
971 v3s16 blockpos = getNodeBlockPos(p);
972 MapBlock * block = getBlockNoCreate(blockpos);
973 assert(block != NULL);
974 modified_blocks.insert(blockpos, block);
976 assert(isValidPosition(p));
978 // Unlight neighbours of node.
979 // This means setting light of all consequent dimmer nodes
981 // This also collects the nodes at the border which will spread
982 // light again into this.
983 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
985 n.setLight(bank, 0, nodemgr);
989 If node lets sunlight through and is under sunlight, it has
992 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
994 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
998 Set the node on the map
1007 std::string metadata_name = nodemgr->get(n).metadata_name;
1008 if(metadata_name != ""){
1009 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1011 errorstream<<"Failed to create node metadata \""
1012 <<metadata_name<<"\""<<std::endl;
1014 setNodeMetadata(p, meta);
1019 If node is under sunlight and doesn't let sunlight through,
1020 take all sunlighted nodes under it and clear light from them
1021 and from where the light has been spread.
1022 TODO: This could be optimized by mass-unlighting instead
1025 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1029 //m_dout<<DTIME<<"y="<<y<<std::endl;
1030 v3s16 n2pos(p.X, y, p.Z);
1034 n2 = getNode(n2pos);
1036 catch(InvalidPositionException &e)
1041 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1043 unLightNeighbors(LIGHTBANK_DAY,
1044 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1045 light_sources, modified_blocks);
1046 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1054 for(s32 i=0; i<2; i++)
1056 enum LightBank bank = banks[i];
1059 Spread light from all nodes that might be capable of doing so
1061 spreadLight(bank, light_sources, modified_blocks);
1065 Update information about whether day and night light differ
1067 for(core::map<v3s16, MapBlock*>::Iterator
1068 i = modified_blocks.getIterator();
1069 i.atEnd() == false; i++)
1071 MapBlock *block = i.getNode()->getValue();
1072 block->updateDayNightDiff();
1076 Add neighboring liquid nodes and the node itself if it is
1077 liquid (=water node was added) to transform queue.
1080 v3s16(0,0,0), // self
1081 v3s16(0,0,1), // back
1082 v3s16(0,1,0), // top
1083 v3s16(1,0,0), // right
1084 v3s16(0,0,-1), // front
1085 v3s16(0,-1,0), // bottom
1086 v3s16(-1,0,0), // left
1088 for(u16 i=0; i<7; i++)
1093 v3s16 p2 = p + dirs[i];
1095 MapNode n2 = getNode(p2);
1096 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1098 m_transforming_liquid.push_back(p2);
1101 }catch(InvalidPositionException &e)
1109 void Map::removeNodeAndUpdate(v3s16 p,
1110 core::map<v3s16, MapBlock*> &modified_blocks)
1112 INodeDefManager *nodemgr = m_gamedef->ndef();
1114 /*PrintInfo(m_dout);
1115 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1116 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1118 bool node_under_sunlight = true;
1120 v3s16 toppos = p + v3s16(0,1,0);
1122 // Node will be replaced with this
1123 content_t replace_material = CONTENT_AIR;
1126 If there is a node at top and it doesn't have sunlight,
1127 there will be no sunlight going down.
1130 MapNode topnode = getNode(toppos);
1132 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1133 node_under_sunlight = false;
1135 catch(InvalidPositionException &e)
1139 core::map<v3s16, bool> light_sources;
1141 enum LightBank banks[] =
1146 for(s32 i=0; i<2; i++)
1148 enum LightBank bank = banks[i];
1151 Unlight neighbors (in case the node is a light source)
1153 unLightNeighbors(bank, p,
1154 getNode(p).getLight(bank, nodemgr),
1155 light_sources, modified_blocks);
1159 Remove node metadata
1162 removeNodeMetadata(p);
1166 This also clears the lighting.
1170 n.setContent(replace_material);
1173 for(s32 i=0; i<2; i++)
1175 enum LightBank bank = banks[i];
1178 Recalculate lighting
1180 spreadLight(bank, light_sources, modified_blocks);
1183 // Add the block of the removed node to modified_blocks
1184 v3s16 blockpos = getNodeBlockPos(p);
1185 MapBlock * block = getBlockNoCreate(blockpos);
1186 assert(block != NULL);
1187 modified_blocks.insert(blockpos, block);
1190 If the removed node was under sunlight, propagate the
1191 sunlight down from it and then light all neighbors
1192 of the propagated blocks.
1194 if(node_under_sunlight)
1196 s16 ybottom = propagateSunlight(p, modified_blocks);
1197 /*m_dout<<DTIME<<"Node was under sunlight. "
1198 "Propagating sunlight";
1199 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1201 for(; y >= ybottom; y--)
1203 v3s16 p2(p.X, y, p.Z);
1204 /*m_dout<<DTIME<<"lighting neighbors of node ("
1205 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1207 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1212 // Set the lighting of this node to 0
1213 // TODO: Is this needed? Lighting is cleared up there already.
1215 MapNode n = getNode(p);
1216 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1219 catch(InvalidPositionException &e)
1225 for(s32 i=0; i<2; i++)
1227 enum LightBank bank = banks[i];
1229 // Get the brightest neighbour node and propagate light from it
1230 v3s16 n2p = getBrightestNeighbour(bank, p);
1232 MapNode n2 = getNode(n2p);
1233 lightNeighbors(bank, n2p, modified_blocks);
1235 catch(InvalidPositionException &e)
1241 Update information about whether day and night light differ
1243 for(core::map<v3s16, MapBlock*>::Iterator
1244 i = modified_blocks.getIterator();
1245 i.atEnd() == false; i++)
1247 MapBlock *block = i.getNode()->getValue();
1248 block->updateDayNightDiff();
1252 Add neighboring liquid nodes and this node to transform queue.
1253 (it's vital for the node itself to get updated last.)
1256 v3s16(0,0,1), // back
1257 v3s16(0,1,0), // top
1258 v3s16(1,0,0), // right
1259 v3s16(0,0,-1), // front
1260 v3s16(0,-1,0), // bottom
1261 v3s16(-1,0,0), // left
1262 v3s16(0,0,0), // self
1264 for(u16 i=0; i<7; i++)
1269 v3s16 p2 = p + dirs[i];
1271 MapNode n2 = getNode(p2);
1272 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1274 m_transforming_liquid.push_back(p2);
1277 }catch(InvalidPositionException &e)
1283 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1286 event.type = MEET_ADDNODE;
1290 bool succeeded = true;
1292 core::map<v3s16, MapBlock*> modified_blocks;
1293 addNodeAndUpdate(p, n, modified_blocks);
1295 // Copy modified_blocks to event
1296 for(core::map<v3s16, MapBlock*>::Iterator
1297 i = modified_blocks.getIterator();
1298 i.atEnd()==false; i++)
1300 event.modified_blocks.insert(i.getNode()->getKey(), false);
1303 catch(InvalidPositionException &e){
1307 dispatchEvent(&event);
1312 bool Map::removeNodeWithEvent(v3s16 p)
1315 event.type = MEET_REMOVENODE;
1318 bool succeeded = true;
1320 core::map<v3s16, MapBlock*> modified_blocks;
1321 removeNodeAndUpdate(p, modified_blocks);
1323 // Copy modified_blocks to event
1324 for(core::map<v3s16, MapBlock*>::Iterator
1325 i = modified_blocks.getIterator();
1326 i.atEnd()==false; i++)
1328 event.modified_blocks.insert(i.getNode()->getKey(), false);
1331 catch(InvalidPositionException &e){
1335 dispatchEvent(&event);
1340 bool Map::dayNightDiffed(v3s16 blockpos)
1343 v3s16 p = blockpos + v3s16(0,0,0);
1344 MapBlock *b = getBlockNoCreate(p);
1345 if(b->dayNightDiffed())
1348 catch(InvalidPositionException &e){}
1351 v3s16 p = blockpos + v3s16(-1,0,0);
1352 MapBlock *b = getBlockNoCreate(p);
1353 if(b->dayNightDiffed())
1356 catch(InvalidPositionException &e){}
1358 v3s16 p = blockpos + v3s16(0,-1,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->dayNightDiffed())
1363 catch(InvalidPositionException &e){}
1365 v3s16 p = blockpos + v3s16(0,0,-1);
1366 MapBlock *b = getBlockNoCreate(p);
1367 if(b->dayNightDiffed())
1370 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(1,0,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->dayNightDiffed())
1378 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(0,1,0);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->dayNightDiffed())
1385 catch(InvalidPositionException &e){}
1387 v3s16 p = blockpos + v3s16(0,0,1);
1388 MapBlock *b = getBlockNoCreate(p);
1389 if(b->dayNightDiffed())
1392 catch(InvalidPositionException &e){}
1398 Updates usage timers
1400 void Map::timerUpdate(float dtime, float unload_timeout,
1401 core::list<v3s16> *unloaded_blocks)
1403 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1405 // Profile modified reasons
1406 Profiler modprofiler;
1408 core::list<v2s16> sector_deletion_queue;
1409 u32 deleted_blocks_count = 0;
1410 u32 saved_blocks_count = 0;
1411 u32 block_count_all = 0;
1413 core::map<v2s16, MapSector*>::Iterator si;
1416 si = m_sectors.getIterator();
1417 for(; si.atEnd() == false; si++)
1419 MapSector *sector = si.getNode()->getValue();
1421 bool all_blocks_deleted = true;
1423 core::list<MapBlock*> blocks;
1424 sector->getBlocks(blocks);
1426 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1427 i != blocks.end(); i++)
1429 MapBlock *block = (*i);
1431 block->incrementUsageTimer(dtime);
1433 if(block->getUsageTimer() > unload_timeout)
1435 v3s16 p = block->getPos();
1438 if(block->getModified() != MOD_STATE_CLEAN
1439 && save_before_unloading)
1441 modprofiler.add(block->getModifiedReason(), 1);
1443 saved_blocks_count++;
1446 // Delete from memory
1447 sector->deleteBlock(block);
1450 unloaded_blocks->push_back(p);
1452 deleted_blocks_count++;
1456 all_blocks_deleted = false;
1461 if(all_blocks_deleted)
1463 sector_deletion_queue.push_back(si.getNode()->getKey());
1468 // Finally delete the empty sectors
1469 deleteSectors(sector_deletion_queue);
1471 if(deleted_blocks_count != 0)
1473 PrintInfo(infostream); // ServerMap/ClientMap:
1474 infostream<<"Unloaded "<<deleted_blocks_count
1475 <<" blocks from memory";
1476 if(save_before_unloading)
1477 infostream<<", of which "<<saved_blocks_count<<" were written";
1478 infostream<<", "<<block_count_all<<" blocks in memory";
1479 infostream<<"."<<std::endl;
1480 if(saved_blocks_count != 0){
1481 PrintInfo(infostream); // ServerMap/ClientMap:
1482 infostream<<"Blocks modified by: "<<std::endl;
1483 modprofiler.print(infostream);
1488 void Map::deleteSectors(core::list<v2s16> &list)
1490 core::list<v2s16>::Iterator j;
1491 for(j=list.begin(); j!=list.end(); j++)
1493 MapSector *sector = m_sectors[*j];
1494 // If sector is in sector cache, remove it from there
1495 if(m_sector_cache == sector)
1496 m_sector_cache = NULL;
1497 // Remove from map and delete
1498 m_sectors.remove(*j);
1504 void Map::unloadUnusedData(float timeout,
1505 core::list<v3s16> *deleted_blocks)
1507 core::list<v2s16> sector_deletion_queue;
1508 u32 deleted_blocks_count = 0;
1509 u32 saved_blocks_count = 0;
1511 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1512 for(; si.atEnd() == false; si++)
1514 MapSector *sector = si.getNode()->getValue();
1516 bool all_blocks_deleted = true;
1518 core::list<MapBlock*> blocks;
1519 sector->getBlocks(blocks);
1520 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1521 i != blocks.end(); i++)
1523 MapBlock *block = (*i);
1525 if(block->getUsageTimer() > timeout)
1528 if(block->getModified() != MOD_STATE_CLEAN)
1531 saved_blocks_count++;
1533 // Delete from memory
1534 sector->deleteBlock(block);
1535 deleted_blocks_count++;
1539 all_blocks_deleted = false;
1543 if(all_blocks_deleted)
1545 sector_deletion_queue.push_back(si.getNode()->getKey());
1549 deleteSectors(sector_deletion_queue);
1551 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1552 <<", of which "<<saved_blocks_count<<" were wr."
1555 //return sector_deletion_queue.getSize();
1556 //return deleted_blocks_count;
1560 void Map::PrintInfo(std::ostream &out)
1565 #define WATER_DROP_BOOST 4
1569 NEIGHBOR_SAME_LEVEL,
1572 struct NodeNeighbor {
1578 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1580 INodeDefManager *nodemgr = m_gamedef->ndef();
1582 DSTACK(__FUNCTION_NAME);
1583 //TimeTaker timer("transformLiquids()");
1586 u32 initial_size = m_transforming_liquid.size();
1588 /*if(initial_size != 0)
1589 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1591 // list of nodes that due to viscosity have not reached their max level height
1592 UniqueQueue<v3s16> must_reflow;
1594 // List of MapBlocks that will require a lighting update (due to lava)
1595 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1597 while(m_transforming_liquid.size() != 0)
1599 // This should be done here so that it is done when continue is used
1600 if(loopcount >= initial_size * 3)
1605 Get a queued transforming liquid node
1607 v3s16 p0 = m_transforming_liquid.pop_front();
1609 MapNode n0 = getNodeNoEx(p0);
1612 Collect information about current node
1614 s8 liquid_level = -1;
1615 u8 liquid_kind = CONTENT_IGNORE;
1616 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1617 switch (liquid_type) {
1619 liquid_level = LIQUID_LEVEL_SOURCE;
1620 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1622 case LIQUID_FLOWING:
1623 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1624 liquid_kind = n0.getContent();
1627 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1628 // continue with the next node.
1629 if (n0.getContent() != CONTENT_AIR)
1631 liquid_kind = CONTENT_AIR;
1636 Collect information about the environment
1638 const v3s16 *dirs = g_6dirs;
1639 NodeNeighbor sources[6]; // surrounding sources
1640 int num_sources = 0;
1641 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1643 NodeNeighbor airs[6]; // surrounding air
1645 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1646 int num_neutrals = 0;
1647 bool flowing_down = false;
1648 for (u16 i = 0; i < 6; i++) {
1649 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1652 nt = NEIGHBOR_UPPER;
1655 nt = NEIGHBOR_LOWER;
1658 v3s16 npos = p0 + dirs[i];
1659 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1660 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1662 if (nb.n.getContent() == CONTENT_AIR) {
1663 airs[num_airs++] = nb;
1664 // if the current node is a water source the neighbor
1665 // should be enqueded for transformation regardless of whether the
1666 // current node changes or not.
1667 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1668 m_transforming_liquid.push_back(npos);
1669 // if the current node happens to be a flowing node, it will start to flow down here.
1670 if (nb.t == NEIGHBOR_LOWER) {
1671 flowing_down = true;
1674 neutrals[num_neutrals++] = nb;
1678 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1679 if (liquid_kind == CONTENT_AIR)
1680 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1681 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1682 neutrals[num_neutrals++] = nb;
1684 // Do not count bottom source, it will screw things up
1686 sources[num_sources++] = nb;
1689 case LIQUID_FLOWING:
1690 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1691 if (liquid_kind == CONTENT_AIR)
1692 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1693 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1694 neutrals[num_neutrals++] = nb;
1696 flows[num_flows++] = nb;
1697 if (nb.t == NEIGHBOR_LOWER)
1698 flowing_down = true;
1705 decide on the type (and possibly level) of the current node
1707 content_t new_node_content;
1708 s8 new_node_level = -1;
1709 s8 max_node_level = -1;
1710 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1711 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1712 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1713 // it's perfectly safe to use liquid_kind here to determine the new node content.
1714 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1715 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1716 // liquid_kind is set properly, see above
1717 new_node_content = liquid_kind;
1718 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1720 // no surrounding sources, so get the maximum level that can flow into this node
1721 for (u16 i = 0; i < num_flows; i++) {
1722 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1723 switch (flows[i].t) {
1724 case NEIGHBOR_UPPER:
1725 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1726 max_node_level = LIQUID_LEVEL_MAX;
1727 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1728 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1729 } else if (nb_liquid_level > max_node_level)
1730 max_node_level = nb_liquid_level;
1732 case NEIGHBOR_LOWER:
1734 case NEIGHBOR_SAME_LEVEL:
1735 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1736 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1737 max_node_level = nb_liquid_level - 1;
1743 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1744 if (viscosity > 1 && max_node_level != liquid_level) {
1745 // amount to gain, limited by viscosity
1746 // must be at least 1 in absolute value
1747 s8 level_inc = max_node_level - liquid_level;
1748 if (level_inc < -viscosity || level_inc > viscosity)
1749 new_node_level = liquid_level + level_inc/viscosity;
1750 else if (level_inc < 0)
1751 new_node_level = liquid_level - 1;
1752 else if (level_inc > 0)
1753 new_node_level = liquid_level + 1;
1754 if (new_node_level != max_node_level)
1755 must_reflow.push_back(p0);
1757 new_node_level = max_node_level;
1759 if (new_node_level >= 0)
1760 new_node_content = liquid_kind;
1762 new_node_content = CONTENT_AIR;
1767 check if anything has changed. if not, just continue with the next node.
1769 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1770 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1771 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1777 update the current node
1779 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1780 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1781 // set level to last 3 bits, flowing down bit to 4th bit
1782 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1784 // set the liquid level and flow bit to 0
1785 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1787 n0.setContent(new_node_content);
1789 v3s16 blockpos = getNodeBlockPos(p0);
1790 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1792 modified_blocks.insert(blockpos, block);
1793 // If node emits light, MapBlock requires lighting update
1794 if(nodemgr->get(n0).light_source != 0)
1795 lighting_modified_blocks[block->getPos()] = block;
1799 enqueue neighbors for update if neccessary
1801 switch (nodemgr->get(n0.getContent()).liquid_type) {
1803 case LIQUID_FLOWING:
1804 // make sure source flows into all neighboring nodes
1805 for (u16 i = 0; i < num_flows; i++)
1806 if (flows[i].t != NEIGHBOR_UPPER)
1807 m_transforming_liquid.push_back(flows[i].p);
1808 for (u16 i = 0; i < num_airs; i++)
1809 if (airs[i].t != NEIGHBOR_UPPER)
1810 m_transforming_liquid.push_back(airs[i].p);
1813 // this flow has turned to air; neighboring flows might need to do the same
1814 for (u16 i = 0; i < num_flows; i++)
1815 m_transforming_liquid.push_back(flows[i].p);
1819 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1820 while (must_reflow.size() > 0)
1821 m_transforming_liquid.push_back(must_reflow.pop_front());
1822 updateLighting(lighting_modified_blocks, modified_blocks);
1825 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1827 v3s16 blockpos = getNodeBlockPos(p);
1828 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1829 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1831 infostream<<"Map::getNodeMetadata(): Need to emerge "
1832 <<PP(blockpos)<<std::endl;
1833 block = emergeBlock(blockpos, false);
1837 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1841 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1845 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1847 v3s16 blockpos = getNodeBlockPos(p);
1848 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1849 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1851 infostream<<"Map::setNodeMetadata(): Need to emerge "
1852 <<PP(blockpos)<<std::endl;
1853 block = emergeBlock(blockpos, false);
1857 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1861 block->m_node_metadata->set(p_rel, meta);
1864 void Map::removeNodeMetadata(v3s16 p)
1866 v3s16 blockpos = getNodeBlockPos(p);
1867 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1868 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1871 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1875 block->m_node_metadata->remove(p_rel);
1878 void Map::nodeMetadataStep(float dtime,
1879 core::map<v3s16, MapBlock*> &changed_blocks)
1883 Currently there is no way to ensure that all the necessary
1884 blocks are loaded when this is run. (They might get unloaded)
1885 NOTE: ^- Actually, that might not be so. In a quick test it
1886 reloaded a block with a furnace when I walked back to it from
1889 core::map<v2s16, MapSector*>::Iterator si;
1890 si = m_sectors.getIterator();
1891 for(; si.atEnd() == false; si++)
1893 MapSector *sector = si.getNode()->getValue();
1894 core::list< MapBlock * > sectorblocks;
1895 sector->getBlocks(sectorblocks);
1896 core::list< MapBlock * >::Iterator i;
1897 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1899 MapBlock *block = *i;
1900 bool changed = block->m_node_metadata->step(dtime);
1902 changed_blocks[block->getPos()] = block;
1911 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1912 Map(dout_server, gamedef),
1914 m_map_metadata_changed(true),
1916 m_database_read(NULL),
1917 m_database_write(NULL)
1919 verbosestream<<__FUNCTION_NAME<<std::endl;
1921 //m_chunksize = 8; // Takes a few seconds
1923 if (g_settings->get("fixed_map_seed").empty())
1925 m_seed = (((u64)(myrand()%0xffff)<<0)
1926 + ((u64)(myrand()%0xffff)<<16)
1927 + ((u64)(myrand()%0xffff)<<32)
1928 + ((u64)(myrand()%0xffff)<<48));
1932 m_seed = g_settings->getU64("fixed_map_seed");
1936 Experimental and debug stuff
1943 Try to load map; if not found, create a new one.
1946 m_savedir = savedir;
1947 m_map_saving_enabled = false;
1951 // If directory exists, check contents and load if possible
1952 if(fs::PathExists(m_savedir))
1954 // If directory is empty, it is safe to save into it.
1955 if(fs::GetDirListing(m_savedir).size() == 0)
1957 infostream<<"ServerMap: Empty save directory is valid."
1959 m_map_saving_enabled = true;
1964 // Load map metadata (seed, chunksize)
1967 catch(FileNotGoodException &e){
1968 infostream<<"WARNING: Could not load map metadata"
1969 //<<" Disabling chunk-based generator."
1974 infostream<<"ServerMap: Successfully loaded map "
1975 <<"metadata from "<<savedir
1976 <<", assuming valid save directory."
1977 <<" seed="<<m_seed<<"."
1980 m_map_saving_enabled = true;
1981 // Map loaded, not creating new one
1985 // If directory doesn't exist, it is safe to save to it
1987 m_map_saving_enabled = true;
1990 catch(std::exception &e)
1992 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
1993 <<", exception: "<<e.what()<<std::endl;
1994 infostream<<"Please remove the map or fix it."<<std::endl;
1995 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1998 infostream<<"Initializing new map."<<std::endl;
2000 // Create zero sector
2001 emergeSector(v2s16(0,0));
2003 // Initially write whole map
2004 save(MOD_STATE_CLEAN);
2007 ServerMap::~ServerMap()
2009 verbosestream<<__FUNCTION_NAME<<std::endl;
2013 if(m_map_saving_enabled)
2015 // Save only changed parts
2016 save(MOD_STATE_WRITE_AT_UNLOAD);
2017 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2021 infostream<<"ServerMap: Map not saved"<<std::endl;
2024 catch(std::exception &e)
2026 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2027 <<", exception: "<<e.what()<<std::endl;
2031 Close database if it was opened
2034 sqlite3_finalize(m_database_read);
2035 if(m_database_write)
2036 sqlite3_finalize(m_database_write);
2038 sqlite3_close(m_database);
2044 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2045 for(; i.atEnd() == false; i++)
2047 MapChunk *chunk = i.getNode()->getValue();
2053 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2055 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2056 if(enable_mapgen_debug_info)
2057 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2058 <<blockpos.Z<<")"<<std::endl;
2060 // Do nothing if not inside limits (+-1 because of neighbors)
2061 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2062 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2068 data->no_op = false;
2069 data->seed = m_seed;
2070 data->blockpos = blockpos;
2071 data->nodedef = m_gamedef->ndef();
2074 Create the whole area of this and the neighboring blocks
2077 //TimeTaker timer("initBlockMake() create area");
2079 for(s16 x=-1; x<=1; x++)
2080 for(s16 z=-1; z<=1; z++)
2082 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2083 // Sector metadata is loaded from disk if not already loaded.
2084 ServerMapSector *sector = createSector(sectorpos);
2087 for(s16 y=-1; y<=1; y++)
2089 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2090 //MapBlock *block = createBlock(p);
2091 // 1) get from memory, 2) load from disk
2092 MapBlock *block = emergeBlock(p, false);
2093 // 3) create a blank one
2096 block = createBlock(p);
2099 Block gets sunlight if this is true.
2101 Refer to the map generator heuristics.
2103 bool ug = mapgen::block_is_underground(data->seed, p);
2104 block->setIsUnderground(ug);
2107 // Lighting will not be valid after make_chunk is called
2108 block->setLightingExpired(true);
2109 // Lighting will be calculated
2110 //block->setLightingExpired(false);
2116 Now we have a big empty area.
2118 Make a ManualMapVoxelManipulator that contains this and the
2122 // The area that contains this block and it's neighbors
2123 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2124 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2126 data->vmanip = new ManualMapVoxelManipulator(this);
2127 //data->vmanip->setMap(this);
2131 //TimeTaker timer("initBlockMake() initialEmerge");
2132 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2135 // Data is ready now.
2138 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2139 core::map<v3s16, MapBlock*> &changed_blocks)
2141 v3s16 blockpos = data->blockpos;
2142 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2143 <<blockpos.Z<<")"<<std::endl;*/
2147 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2151 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2153 /*infostream<<"Resulting vmanip:"<<std::endl;
2154 data->vmanip.print(infostream);*/
2156 // Make sure affected blocks are loaded
2157 for(s16 x=-1; x<=1; x++)
2158 for(s16 z=-1; z<=1; z++)
2159 for(s16 y=-1; y<=1; y++)
2161 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2162 // Load from disk if not already in memory
2163 emergeBlock(p, false);
2167 Blit generated stuff to map
2168 NOTE: blitBackAll adds nearly everything to changed_blocks
2172 //TimeTaker timer("finishBlockMake() blitBackAll");
2173 data->vmanip->blitBackAll(&changed_blocks);
2176 if(enable_mapgen_debug_info)
2177 infostream<<"finishBlockMake: changed_blocks.size()="
2178 <<changed_blocks.size()<<std::endl;
2181 Copy transforming liquid information
2183 while(data->transforming_liquid.size() > 0)
2185 v3s16 p = data->transforming_liquid.pop_front();
2186 m_transforming_liquid.push_back(p);
2192 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2196 Set is_underground flag for lighting with sunlight.
2198 Refer to map generator heuristics.
2200 NOTE: This is done in initChunkMake
2202 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2206 Add sunlight to central block.
2207 This makes in-dark-spawning monsters to not flood the whole thing.
2208 Do not spread the light, though.
2210 /*core::map<v3s16, bool> light_sources;
2211 bool black_air_left = false;
2212 block->propagateSunlight(light_sources, true, &black_air_left);*/
2215 NOTE: Lighting and object adding shouldn't really be here, but
2216 lighting is a bit tricky to move properly to makeBlock.
2217 TODO: Do this the right way anyway, that is, move it to makeBlock.
2218 - There needs to be some way for makeBlock to report back if
2219 the lighting update is going further down because of the
2220 new block blocking light
2225 NOTE: This takes ~60ms, TODO: Investigate why
2228 TimeTaker t("finishBlockMake lighting update");
2230 core::map<v3s16, MapBlock*> lighting_update_blocks;
2233 lighting_update_blocks.insert(block->getPos(), block);
2238 v3s16 p = block->getPos()+v3s16(x,1,z);
2239 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2243 // All modified blocks
2244 // NOTE: Should this be done? If this is not done, then the lighting
2245 // of the others will be updated in a different place, one by one, i
2246 // think... or they might not? Well, at least they are left marked as
2247 // "lighting expired"; it seems that is not handled at all anywhere,
2248 // so enabling this will slow it down A LOT because otherwise it
2249 // would not do this at all. This causes the black trees.
2250 for(core::map<v3s16, MapBlock*>::Iterator
2251 i = changed_blocks.getIterator();
2252 i.atEnd() == false; i++)
2254 lighting_update_blocks.insert(i.getNode()->getKey(),
2255 i.getNode()->getValue());
2257 /*// Also force-add all the upmost blocks for proper sunlight
2258 for(s16 x=-1; x<=1; x++)
2259 for(s16 z=-1; z<=1; z++)
2261 v3s16 p = block->getPos()+v3s16(x,1,z);
2262 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2265 updateLighting(lighting_update_blocks, changed_blocks);
2268 Set lighting to non-expired state in all of them.
2269 This is cheating, but it is not fast enough if all of them
2270 would actually be updated.
2272 for(s16 x=-1; x<=1; x++)
2273 for(s16 y=-1; y<=1; y++)
2274 for(s16 z=-1; z<=1; z++)
2276 v3s16 p = block->getPos()+v3s16(x,y,z);
2277 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2280 if(enable_mapgen_debug_info == false)
2281 t.stop(true); // Hide output
2285 Add random objects to block
2287 mapgen::add_random_objects(block);
2290 Go through changed blocks
2292 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2293 i.atEnd() == false; i++)
2295 MapBlock *block = i.getNode()->getValue();
2298 Update day/night difference cache of the MapBlocks
2300 block->updateDayNightDiff();
2302 Set block as modified
2304 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2305 "finishBlockMake updateDayNightDiff");
2309 Set central block as generated
2311 block->setGenerated(true);
2314 Save changed parts of map
2315 NOTE: Will be saved later.
2317 //save(MOD_STATE_WRITE_AT_UNLOAD);
2319 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2320 <<blockpos.Z<<")"<<std::endl;*/
2322 if(enable_mapgen_debug_info)
2325 Analyze resulting blocks
2327 for(s16 x=-1; x<=1; x++)
2328 for(s16 y=-1; y<=1; y++)
2329 for(s16 z=-1; z<=1; z++)
2331 v3s16 p = block->getPos()+v3s16(x,y,z);
2332 MapBlock *block = getBlockNoCreateNoEx(p);
2334 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2335 infostream<<"Generated "<<spos<<": "
2336 <<analyze_block(block)<<std::endl;
2344 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2346 DSTACKF("%s: p2d=(%d,%d)",
2351 Check if it exists already in memory
2353 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2358 Try to load it from disk (with blocks)
2360 //if(loadSectorFull(p2d) == true)
2363 Try to load metadata from disk
2366 if(loadSectorMeta(p2d) == true)
2368 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2371 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2372 throw InvalidPositionException("");
2378 Do not create over-limit
2380 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2381 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2382 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2383 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2384 throw InvalidPositionException("createSector(): pos. over limit");
2387 Generate blank sector
2390 sector = new ServerMapSector(this, p2d, m_gamedef);
2392 // Sector position on map in nodes
2393 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2398 m_sectors.insert(p2d, sector);
2404 This is a quick-hand function for calling makeBlock().
2406 MapBlock * ServerMap::generateBlock(
2408 core::map<v3s16, MapBlock*> &modified_blocks
2411 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2413 /*infostream<<"generateBlock(): "
2414 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2417 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2419 TimeTaker timer("generateBlock");
2421 //MapBlock *block = original_dummy;
2423 v2s16 p2d(p.X, p.Z);
2424 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2427 Do not generate over-limit
2429 if(blockpos_over_limit(p))
2431 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2432 throw InvalidPositionException("generateBlock(): pos. over limit");
2436 Create block make data
2438 mapgen::BlockMakeData data;
2439 initBlockMake(&data, p);
2445 TimeTaker t("mapgen::make_block()");
2446 mapgen::make_block(&data);
2448 if(enable_mapgen_debug_info == false)
2449 t.stop(true); // Hide output
2453 Blit data back on map, update lighting, add mobs and whatever this does
2455 finishBlockMake(&data, modified_blocks);
2460 MapBlock *block = getBlockNoCreateNoEx(p);
2468 bool erroneus_content = false;
2469 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2470 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2471 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2474 MapNode n = block->getNode(p);
2475 if(n.getContent() == CONTENT_IGNORE)
2477 infostream<<"CONTENT_IGNORE at "
2478 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2480 erroneus_content = true;
2484 if(erroneus_content)
2493 Generate a completely empty block
2497 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2498 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2500 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2503 n.setContent(CONTENT_AIR);
2504 block->setNode(v3s16(x0,y0,z0), n);
2510 if(enable_mapgen_debug_info == false)
2511 timer.stop(true); // Hide output
2516 MapBlock * ServerMap::createBlock(v3s16 p)
2518 DSTACKF("%s: p=(%d,%d,%d)",
2519 __FUNCTION_NAME, p.X, p.Y, p.Z);
2522 Do not create over-limit
2524 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2525 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2526 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2527 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2528 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2529 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2530 throw InvalidPositionException("createBlock(): pos. over limit");
2532 v2s16 p2d(p.X, p.Z);
2535 This will create or load a sector if not found in memory.
2536 If block exists on disk, it will be loaded.
2538 NOTE: On old save formats, this will be slow, as it generates
2539 lighting on blocks for them.
2541 ServerMapSector *sector;
2543 sector = (ServerMapSector*)createSector(p2d);
2544 assert(sector->getId() == MAPSECTOR_SERVER);
2546 catch(InvalidPositionException &e)
2548 infostream<<"createBlock: createSector() failed"<<std::endl;
2552 NOTE: This should not be done, or at least the exception
2553 should not be passed on as std::exception, because it
2554 won't be catched at all.
2556 /*catch(std::exception &e)
2558 infostream<<"createBlock: createSector() failed: "
2559 <<e.what()<<std::endl;
2564 Try to get a block from the sector
2567 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2570 if(block->isDummy())
2575 block = sector->createBlankBlock(block_y);
2579 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2581 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2583 p.X, p.Y, p.Z, allow_generate);
2586 MapBlock *block = getBlockNoCreateNoEx(p);
2587 if(block && block->isDummy() == false)
2592 MapBlock *block = loadBlock(p);
2599 core::map<v3s16, MapBlock*> modified_blocks;
2600 MapBlock *block = generateBlock(p, modified_blocks);
2604 event.type = MEET_OTHER;
2607 // Copy modified_blocks to event
2608 for(core::map<v3s16, MapBlock*>::Iterator
2609 i = modified_blocks.getIterator();
2610 i.atEnd()==false; i++)
2612 event.modified_blocks.insert(i.getNode()->getKey(), false);
2616 dispatchEvent(&event);
2625 s16 ServerMap::findGroundLevel(v2s16 p2d)
2629 Uh, just do something random...
2631 // Find existing map from top to down
2634 v3s16 p(p2d.X, max, p2d.Y);
2635 for(; p.Y>min; p.Y--)
2637 MapNode n = getNodeNoEx(p);
2638 if(n.getContent() != CONTENT_IGNORE)
2643 // If this node is not air, go to plan b
2644 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2646 // Search existing walkable and return it
2647 for(; p.Y>min; p.Y--)
2649 MapNode n = getNodeNoEx(p);
2650 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2659 Determine from map generator noise functions
2662 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2665 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2666 //return (s16)level;
2669 void ServerMap::createDatabase() {
2672 e = sqlite3_exec(m_database,
2673 "CREATE TABLE IF NOT EXISTS `blocks` ("
2674 "`pos` INT NOT NULL PRIMARY KEY,"
2677 , NULL, NULL, NULL);
2678 if(e == SQLITE_ABORT)
2679 throw FileNotGoodException("Could not create database structure");
2681 infostream<<"ServerMap: Database structure was created";
2684 void ServerMap::verifyDatabase() {
2689 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2690 bool needs_create = false;
2694 Open the database connection
2697 createDirs(m_savedir);
2699 if(!fs::PathExists(dbp))
2700 needs_create = true;
2702 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2703 if(d != SQLITE_OK) {
2704 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2705 throw FileNotGoodException("Cannot open database file");
2711 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2712 if(d != SQLITE_OK) {
2713 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2714 throw FileNotGoodException("Cannot prepare read statement");
2717 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2718 if(d != SQLITE_OK) {
2719 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2720 throw FileNotGoodException("Cannot prepare write statement");
2723 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2724 if(d != SQLITE_OK) {
2725 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2726 throw FileNotGoodException("Cannot prepare read statement");
2729 infostream<<"ServerMap: Database opened"<<std::endl;
2733 bool ServerMap::loadFromFolders() {
2734 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2739 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2740 return (sqlite3_int64)pos.Z*16777216 +
2741 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2744 void ServerMap::createDirs(std::string path)
2746 if(fs::CreateAllDirs(path) == false)
2748 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2749 <<"\""<<path<<"\""<<std::endl;
2750 throw BaseException("ServerMap failed to create directory");
2754 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2760 snprintf(cc, 9, "%.4x%.4x",
2761 (unsigned int)pos.X&0xffff,
2762 (unsigned int)pos.Y&0xffff);
2764 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2766 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2767 (unsigned int)pos.X&0xfff,
2768 (unsigned int)pos.Y&0xfff);
2770 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2776 v2s16 ServerMap::getSectorPos(std::string dirname)
2780 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2781 assert(spos != std::string::npos);
2782 if(dirname.size() - spos == 8)
2785 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2787 else if(dirname.size() - spos == 3)
2790 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2791 // Sign-extend the 12 bit values up to 16 bits...
2792 if(x&0x800) x|=0xF000;
2793 if(y&0x800) y|=0xF000;
2800 v2s16 pos((s16)x, (s16)y);
2804 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2806 v2s16 p2d = getSectorPos(sectordir);
2808 if(blockfile.size() != 4){
2809 throw InvalidFilenameException("Invalid block filename");
2812 int r = sscanf(blockfile.c_str(), "%4x", &y);
2814 throw InvalidFilenameException("Invalid block filename");
2815 return v3s16(p2d.X, y, p2d.Y);
2818 std::string ServerMap::getBlockFilename(v3s16 p)
2821 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2825 void ServerMap::save(ModifiedState save_level)
2827 DSTACK(__FUNCTION_NAME);
2828 if(m_map_saving_enabled == false)
2830 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2834 if(save_level == MOD_STATE_CLEAN)
2835 infostream<<"ServerMap: Saving whole map, this can take time."
2838 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2843 // Profile modified reasons
2844 Profiler modprofiler;
2846 u32 sector_meta_count = 0;
2847 u32 block_count = 0;
2848 u32 block_count_all = 0; // Number of blocks in memory
2850 // Don't do anything with sqlite unless something is really saved
2851 bool save_started = false;
2853 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2854 for(; i.atEnd() == false; i++)
2856 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2857 assert(sector->getId() == MAPSECTOR_SERVER);
2859 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2861 saveSectorMeta(sector);
2862 sector_meta_count++;
2864 core::list<MapBlock*> blocks;
2865 sector->getBlocks(blocks);
2866 core::list<MapBlock*>::Iterator j;
2868 for(j=blocks.begin(); j!=blocks.end(); j++)
2870 MapBlock *block = *j;
2874 if(block->getModified() >= save_level)
2879 save_started = true;
2882 modprofiler.add(block->getModifiedReason(), 1);
2887 /*infostream<<"ServerMap: Written block ("
2888 <<block->getPos().X<<","
2889 <<block->getPos().Y<<","
2890 <<block->getPos().Z<<")"
2899 Only print if something happened or saved whole map
2901 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2902 || block_count != 0)
2904 infostream<<"ServerMap: Written: "
2905 <<sector_meta_count<<" sector metadata files, "
2906 <<block_count<<" block files"
2907 <<", "<<block_count_all<<" blocks in memory."
2909 PrintInfo(infostream); // ServerMap/ClientMap:
2910 infostream<<"Blocks modified by: "<<std::endl;
2911 modprofiler.print(infostream);
2915 static s32 unsignedToSigned(s32 i, s32 max_positive)
2917 if(i < max_positive)
2920 return i - 2*max_positive;
2923 // modulo of a negative number does not work consistently in C
2924 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2928 return mod - ((-i) % mod);
2931 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2933 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2935 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2937 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2938 return v3s16(x,y,z);
2941 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2943 if(loadFromFolders()){
2944 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2945 <<"all blocks that are stored in flat files"<<std::endl;
2951 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2953 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2954 v3s16 p = getIntegerAsBlock(block_i);
2955 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2961 void ServerMap::saveMapMeta()
2963 DSTACK(__FUNCTION_NAME);
2965 /*infostream<<"ServerMap::saveMapMeta(): "
2969 createDirs(m_savedir);
2971 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2972 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2973 if(os.good() == false)
2975 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2976 <<"could not open"<<fullpath<<std::endl;
2977 throw FileNotGoodException("Cannot open chunk metadata");
2981 params.setU64("seed", m_seed);
2983 params.writeLines(os);
2985 os<<"[end_of_params]\n";
2987 m_map_metadata_changed = false;
2990 void ServerMap::loadMapMeta()
2992 DSTACK(__FUNCTION_NAME);
2994 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2997 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2998 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2999 if(is.good() == false)
3001 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3002 <<"could not open"<<fullpath<<std::endl;
3003 throw FileNotGoodException("Cannot open map metadata");
3011 throw SerializationError
3012 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3014 std::getline(is, line);
3015 std::string trimmedline = trim(line);
3016 if(trimmedline == "[end_of_params]")
3018 params.parseConfigLine(line);
3021 m_seed = params.getU64("seed");
3023 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3026 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3028 DSTACK(__FUNCTION_NAME);
3029 // Format used for writing
3030 u8 version = SER_FMT_VER_HIGHEST;
3032 v2s16 pos = sector->getPos();
3033 std::string dir = getSectorDir(pos);
3036 std::string fullpath = dir + DIR_DELIM + "meta";
3037 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3038 if(o.good() == false)
3039 throw FileNotGoodException("Cannot open sector metafile");
3041 sector->serialize(o, version);
3043 sector->differs_from_disk = false;
3046 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3048 DSTACK(__FUNCTION_NAME);
3050 v2s16 p2d = getSectorPos(sectordir);
3052 ServerMapSector *sector = NULL;
3054 std::string fullpath = sectordir + DIR_DELIM + "meta";
3055 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3056 if(is.good() == false)
3058 // If the directory exists anyway, it probably is in some old
3059 // format. Just go ahead and create the sector.
3060 if(fs::PathExists(sectordir))
3062 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3063 <<fullpath<<" doesn't exist but directory does."
3064 <<" Continuing with a sector with no metadata."
3066 sector = new ServerMapSector(this, p2d, m_gamedef);
3067 m_sectors.insert(p2d, sector);
3071 throw FileNotGoodException("Cannot open sector metafile");
3076 sector = ServerMapSector::deSerialize
3077 (is, this, p2d, m_sectors, m_gamedef);
3079 saveSectorMeta(sector);
3082 sector->differs_from_disk = false;
3087 bool ServerMap::loadSectorMeta(v2s16 p2d)
3089 DSTACK(__FUNCTION_NAME);
3091 MapSector *sector = NULL;
3093 // The directory layout we're going to load from.
3094 // 1 - original sectors/xxxxzzzz/
3095 // 2 - new sectors2/xxx/zzz/
3096 // If we load from anything but the latest structure, we will
3097 // immediately save to the new one, and remove the old.
3099 std::string sectordir1 = getSectorDir(p2d, 1);
3100 std::string sectordir;
3101 if(fs::PathExists(sectordir1))
3103 sectordir = sectordir1;
3108 sectordir = getSectorDir(p2d, 2);
3112 sector = loadSectorMeta(sectordir, loadlayout != 2);
3114 catch(InvalidFilenameException &e)
3118 catch(FileNotGoodException &e)
3122 catch(std::exception &e)
3131 bool ServerMap::loadSectorFull(v2s16 p2d)
3133 DSTACK(__FUNCTION_NAME);
3135 MapSector *sector = NULL;
3137 // The directory layout we're going to load from.
3138 // 1 - original sectors/xxxxzzzz/
3139 // 2 - new sectors2/xxx/zzz/
3140 // If we load from anything but the latest structure, we will
3141 // immediately save to the new one, and remove the old.
3143 std::string sectordir1 = getSectorDir(p2d, 1);
3144 std::string sectordir;
3145 if(fs::PathExists(sectordir1))
3147 sectordir = sectordir1;
3152 sectordir = getSectorDir(p2d, 2);
3156 sector = loadSectorMeta(sectordir, loadlayout != 2);
3158 catch(InvalidFilenameException &e)
3162 catch(FileNotGoodException &e)
3166 catch(std::exception &e)
3174 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3176 std::vector<fs::DirListNode>::iterator i2;
3177 for(i2=list2.begin(); i2!=list2.end(); i2++)
3183 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3185 catch(InvalidFilenameException &e)
3187 // This catches unknown crap in directory
3193 infostream<<"Sector converted to new layout - deleting "<<
3194 sectordir1<<std::endl;
3195 fs::RecursiveDelete(sectordir1);
3202 void ServerMap::beginSave() {
3204 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3205 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3208 void ServerMap::endSave() {
3210 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3211 infostream<<"WARNING: endSave() failed, map might not have saved.";
3214 void ServerMap::saveBlock(MapBlock *block)
3216 DSTACK(__FUNCTION_NAME);
3218 Dummy blocks are not written
3220 if(block->isDummy())
3222 /*v3s16 p = block->getPos();
3223 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3224 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3228 // Format used for writing
3229 u8 version = SER_FMT_VER_HIGHEST;
3231 v3s16 p3d = block->getPos();
3235 v2s16 p2d(p3d.X, p3d.Z);
3236 std::string sectordir = getSectorDir(p2d);
3238 createDirs(sectordir);
3240 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3241 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3242 if(o.good() == false)
3243 throw FileNotGoodException("Cannot open block data");
3246 [0] u8 serialization version
3252 std::ostringstream o(std::ios_base::binary);
3254 o.write((char*)&version, 1);
3257 block->serialize(o, version, true);
3259 // Write block to database
3261 std::string tmp = o.str();
3262 const char *bytes = tmp.c_str();
3264 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3265 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3266 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3267 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3268 int written = sqlite3_step(m_database_write);
3269 if(written != SQLITE_DONE)
3270 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3271 <<sqlite3_errmsg(m_database)<<std::endl;
3272 // Make ready for later reuse
3273 sqlite3_reset(m_database_write);
3275 // We just wrote it to the disk so clear modified flag
3276 block->resetModified();
3279 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3281 DSTACK(__FUNCTION_NAME);
3283 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3286 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3287 if(is.good() == false)
3288 throw FileNotGoodException("Cannot open block file");
3290 v3s16 p3d = getBlockPos(sectordir, blockfile);
3291 v2s16 p2d(p3d.X, p3d.Z);
3293 assert(sector->getPos() == p2d);
3295 u8 version = SER_FMT_VER_INVALID;
3296 is.read((char*)&version, 1);
3299 throw SerializationError("ServerMap::loadBlock(): Failed"
3300 " to read MapBlock version");
3302 /*u32 block_size = MapBlock::serializedLength(version);
3303 SharedBuffer<u8> data(block_size);
3304 is.read((char*)*data, block_size);*/
3306 // This will always return a sector because we're the server
3307 //MapSector *sector = emergeSector(p2d);
3309 MapBlock *block = NULL;
3310 bool created_new = false;
3311 block = sector->getBlockNoCreateNoEx(p3d.Y);
3314 block = sector->createBlankBlockNoInsert(p3d.Y);
3319 block->deSerialize(is, version, true);
3321 // If it's a new block, insert it to the map
3323 sector->insertBlock(block);
3326 Save blocks loaded in old format in new format
3329 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3333 // Should be in database now, so delete the old file
3334 fs::RecursiveDelete(fullpath);
3337 // We just loaded it from the disk, so it's up-to-date.
3338 block->resetModified();
3341 catch(SerializationError &e)
3343 infostream<<"WARNING: Invalid block data on disk "
3344 <<"fullpath="<<fullpath
3345 <<" (SerializationError). "
3346 <<"what()="<<e.what()
3348 //" Ignoring. A new one will be generated.
3351 // TODO: Backup file; name is in fullpath.
3355 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3357 DSTACK(__FUNCTION_NAME);
3360 std::istringstream is(*blob, std::ios_base::binary);
3362 u8 version = SER_FMT_VER_INVALID;
3363 is.read((char*)&version, 1);
3366 throw SerializationError("ServerMap::loadBlock(): Failed"
3367 " to read MapBlock version");
3369 /*u32 block_size = MapBlock::serializedLength(version);
3370 SharedBuffer<u8> data(block_size);
3371 is.read((char*)*data, block_size);*/
3373 // This will always return a sector because we're the server
3374 //MapSector *sector = emergeSector(p2d);
3376 MapBlock *block = NULL;
3377 bool created_new = false;
3378 block = sector->getBlockNoCreateNoEx(p3d.Y);
3381 block = sector->createBlankBlockNoInsert(p3d.Y);
3386 block->deSerialize(is, version, true);
3388 // If it's a new block, insert it to the map
3390 sector->insertBlock(block);
3393 Save blocks loaded in old format in new format
3396 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3397 // Only save if asked to; no need to update version
3401 // We just loaded it from, so it's up-to-date.
3402 block->resetModified();
3405 catch(SerializationError &e)
3407 infostream<<"WARNING: Invalid block data in database "
3408 <<" (SerializationError). "
3409 <<"what()="<<e.what()
3411 //" Ignoring. A new one will be generated.
3414 // TODO: Copy to a backup database.
3418 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3420 DSTACK(__FUNCTION_NAME);
3422 v2s16 p2d(blockpos.X, blockpos.Z);
3424 if(!loadFromFolders()) {
3427 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3428 infostream<<"WARNING: Could not bind block position for load: "
3429 <<sqlite3_errmsg(m_database)<<std::endl;
3430 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3432 Make sure sector is loaded
3434 MapSector *sector = createSector(p2d);
3439 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3440 size_t len = sqlite3_column_bytes(m_database_read, 0);
3442 std::string datastr(data, len);
3444 loadBlock(&datastr, blockpos, sector, false);
3446 sqlite3_step(m_database_read);
3447 // We should never get more than 1 row, so ok to reset
3448 sqlite3_reset(m_database_read);
3450 return getBlockNoCreateNoEx(blockpos);
3452 sqlite3_reset(m_database_read);
3454 // Not found in database, try the files
3457 // The directory layout we're going to load from.
3458 // 1 - original sectors/xxxxzzzz/
3459 // 2 - new sectors2/xxx/zzz/
3460 // If we load from anything but the latest structure, we will
3461 // immediately save to the new one, and remove the old.
3463 std::string sectordir1 = getSectorDir(p2d, 1);
3464 std::string sectordir;
3465 if(fs::PathExists(sectordir1))
3467 sectordir = sectordir1;
3472 sectordir = getSectorDir(p2d, 2);
3476 Make sure sector is loaded
3478 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3482 sector = loadSectorMeta(sectordir, loadlayout != 2);
3484 catch(InvalidFilenameException &e)
3488 catch(FileNotGoodException &e)
3492 catch(std::exception &e)
3499 Make sure file exists
3502 std::string blockfilename = getBlockFilename(blockpos);
3503 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3507 Load block and save it to the database
3509 loadBlock(sectordir, blockfilename, sector, true);
3510 return getBlockNoCreateNoEx(blockpos);
3513 void ServerMap::PrintInfo(std::ostream &out)
3524 ClientMap::ClientMap(
3527 MapDrawControl &control,
3528 scene::ISceneNode* parent,
3529 scene::ISceneManager* mgr,
3532 Map(dout_client, gamedef),
3533 scene::ISceneNode(parent, mgr, id),
3536 m_camera_position(0,0,0),
3537 m_camera_direction(0,0,1),
3540 m_camera_mutex.Init();
3541 assert(m_camera_mutex.IsInitialized());
3543 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3544 BS*1000000,BS*1000000,BS*1000000);
3547 ClientMap::~ClientMap()
3549 /*JMutexAutoLock lock(mesh_mutex);
3558 MapSector * ClientMap::emergeSector(v2s16 p2d)
3560 DSTACK(__FUNCTION_NAME);
3561 // Check that it doesn't exist already
3563 return getSectorNoGenerate(p2d);
3565 catch(InvalidPositionException &e)
3570 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3573 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3574 m_sectors.insert(p2d, sector);
3581 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3583 DSTACK(__FUNCTION_NAME);
3584 ClientMapSector *sector = NULL;
3586 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3588 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3592 sector = (ClientMapSector*)n->getValue();
3593 assert(sector->getId() == MAPSECTOR_CLIENT);
3597 sector = new ClientMapSector(this, p2d);
3599 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3600 m_sectors.insert(p2d, sector);
3604 sector->deSerialize(is);
3608 void ClientMap::OnRegisterSceneNode()
3612 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3613 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3616 ISceneNode::OnRegisterSceneNode();
3619 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3620 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3622 float d0 = (float)BS * p0.getDistanceFrom(p1);
3624 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3626 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3628 for(float s=start_off; s<d0+end_off; s+=step){
3629 v3f pf = p0f + uf * s;
3630 v3s16 p = floatToInt(pf, BS);
3631 MapNode n = map->getNodeNoEx(p);
3632 bool is_transparent = false;
3633 const ContentFeatures &f = nodemgr->get(n);
3634 if(f.solidness == 0)
3635 is_transparent = (f.visual_solidness != 2);
3637 is_transparent = (f.solidness != 2);
3638 if(!is_transparent){
3640 if(count >= needed_count)
3648 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3650 INodeDefManager *nodemgr = m_gamedef->ndef();
3652 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3653 DSTACK(__FUNCTION_NAME);
3655 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3658 if(pass == scene::ESNRP_SOLID)
3659 prefix = "CM: solid: ";
3661 prefix = "CM: transparent: ";
3664 This is called two times per frame, reset on the non-transparent one
3666 if(pass == scene::ESNRP_SOLID)
3668 m_last_drawn_sectors.clear();
3672 Get time for measuring timeout.
3674 Measuring time is very useful for long delays when the
3675 machine is swapping a lot.
3677 int time1 = time(0);
3679 //u32 daynight_ratio = m_client->getDayNightRatio();
3681 m_camera_mutex.Lock();
3682 v3f camera_position = m_camera_position;
3683 v3f camera_direction = m_camera_direction;
3684 f32 camera_fov = m_camera_fov;
3685 m_camera_mutex.Unlock();
3688 Get all blocks and draw all visible ones
3691 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3693 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3695 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3696 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3698 // Take a fair amount as we will be dropping more out later
3699 // Umm... these additions are a bit strange but they are needed.
3701 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3702 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3703 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3705 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3706 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3707 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3709 u32 vertex_count = 0;
3710 u32 meshbuffer_count = 0;
3712 // For limiting number of mesh updates per frame
3713 u32 mesh_update_count = 0;
3715 // Number of blocks in rendering range
3716 u32 blocks_in_range = 0;
3717 // Number of blocks occlusion culled
3718 u32 blocks_occlusion_culled = 0;
3719 // Number of blocks in rendering range but don't have a mesh
3720 u32 blocks_in_range_without_mesh = 0;
3721 // Blocks that had mesh that would have been drawn according to
3722 // rendering range (if max blocks limit didn't kick in)
3723 u32 blocks_would_have_drawn = 0;
3724 // Blocks that were drawn and had a mesh
3725 u32 blocks_drawn = 0;
3726 // Blocks which had a corresponding meshbuffer for this pass
3727 u32 blocks_had_pass_meshbuf = 0;
3728 // Blocks from which stuff was actually drawn
3729 u32 blocks_without_stuff = 0;
3732 Collect a set of blocks for drawing
3735 core::map<v3s16, MapBlock*> drawset;
3738 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3740 for(core::map<v2s16, MapSector*>::Iterator
3741 si = m_sectors.getIterator();
3742 si.atEnd() == false; si++)
3744 MapSector *sector = si.getNode()->getValue();
3745 v2s16 sp = sector->getPos();
3747 if(m_control.range_all == false)
3749 if(sp.X < p_blocks_min.X
3750 || sp.X > p_blocks_max.X
3751 || sp.Y < p_blocks_min.Z
3752 || sp.Y > p_blocks_max.Z)
3756 core::list< MapBlock * > sectorblocks;
3757 sector->getBlocks(sectorblocks);
3760 Loop through blocks in sector
3763 u32 sector_blocks_drawn = 0;
3765 core::list< MapBlock * >::Iterator i;
3766 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3768 MapBlock *block = *i;
3771 Compare block position to camera position, skip
3772 if not seen on display
3775 float range = 100000 * BS;
3776 if(m_control.range_all == false)
3777 range = m_control.wanted_range * BS;
3780 if(isBlockInSight(block->getPos(), camera_position,
3781 camera_direction, camera_fov,
3782 range, &d) == false)
3787 // This is ugly (spherical distance limit?)
3788 /*if(m_control.range_all == false &&
3789 d - 0.5*BS*MAP_BLOCKSIZE > range)
3796 Update expired mesh (used for day/night change)
3798 It doesn't work exactly like it should now with the
3799 tasked mesh update but whatever.
3802 bool mesh_expired = false;
3805 JMutexAutoLock lock(block->mesh_mutex);
3807 mesh_expired = block->getMeshExpired();
3809 // Mesh has not been expired and there is no mesh:
3810 // block has no content
3811 if(block->mesh == NULL && mesh_expired == false){
3812 blocks_in_range_without_mesh++;
3817 f32 faraway = BS*50;
3818 //f32 faraway = m_control.wanted_range * BS;
3821 This has to be done with the mesh_mutex unlocked
3823 // Pretty random but this should work somewhat nicely
3824 if(mesh_expired && (
3825 (mesh_update_count < 3
3826 && (d < faraway || mesh_update_count < 2)
3829 (m_control.range_all && mesh_update_count < 20)
3832 /*if(mesh_expired && mesh_update_count < 6
3833 && (d < faraway || mesh_update_count < 3))*/
3835 mesh_update_count++;
3837 // Mesh has been expired: generate new mesh
3838 //block->updateMesh(daynight_ratio);
3839 m_client->addUpdateMeshTask(block->getPos());
3841 mesh_expired = false;
3849 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3850 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3852 float stepfac = 1.1;
3853 float startoff = BS*1;
3854 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3855 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3856 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3857 u32 needed_count = 1;
3859 isOccluded(this, spn, cpn + v3s16(0,0,0),
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) &&
3875 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3876 step, stepfac, startoff, endoff, needed_count, nodemgr)
3879 blocks_occlusion_culled++;
3883 // This block is in range. Reset usage timer.
3884 block->resetUsageTimer();
3887 Ignore if mesh doesn't exist
3890 JMutexAutoLock lock(block->mesh_mutex);
3892 scene::SMesh *mesh = block->mesh;
3895 blocks_in_range_without_mesh++;
3900 // Limit block count in case of a sudden increase
3901 blocks_would_have_drawn++;
3902 if(blocks_drawn >= m_control.wanted_max_blocks
3903 && m_control.range_all == false
3904 && d > m_control.wanted_min_range * BS)
3908 drawset[block->getPos()] = block;
3910 sector_blocks_drawn++;
3913 } // foreach sectorblocks
3915 if(sector_blocks_drawn != 0)
3916 m_last_drawn_sectors[sp] = true;
3921 Draw the selected MapBlocks
3925 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3927 int timecheck_counter = 0;
3928 for(core::map<v3s16, MapBlock*>::Iterator
3929 i = drawset.getIterator();
3930 i.atEnd() == false; i++)
3933 timecheck_counter++;
3934 if(timecheck_counter > 50)
3936 timecheck_counter = 0;
3937 int time2 = time(0);
3938 if(time2 > time1 + 4)
3940 infostream<<"ClientMap::renderMap(): "
3941 "Rendering takes ages, returning."
3948 MapBlock *block = i.getNode()->getValue();
3951 Draw the faces of the block
3954 JMutexAutoLock lock(block->mesh_mutex);
3956 scene::SMesh *mesh = block->mesh;
3959 u32 c = mesh->getMeshBufferCount();
3960 bool stuff_actually_drawn = false;
3961 for(u32 i=0; i<c; i++)
3963 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3964 const video::SMaterial& material = buf->getMaterial();
3965 video::IMaterialRenderer* rnd =
3966 driver->getMaterialRenderer(material.MaterialType);
3967 bool transparent = (rnd && rnd->isTransparent());
3968 // Render transparent on transparent pass and likewise.
3969 if(transparent == is_transparent_pass)
3971 if(buf->getVertexCount() == 0)
3972 errorstream<<"Block ["<<analyze_block(block)
3973 <<"] contains an empty meshbuf"<<std::endl;
3975 This *shouldn't* hurt too much because Irrlicht
3976 doesn't change opengl textures if the old
3977 material has the same texture.
3979 driver->setMaterial(buf->getMaterial());
3980 driver->drawMeshBuffer(buf);
3981 vertex_count += buf->getVertexCount();
3983 stuff_actually_drawn = true;
3986 if(stuff_actually_drawn)
3987 blocks_had_pass_meshbuf++;
3989 blocks_without_stuff++;
3994 // Log only on solid pass because values are the same
3995 if(pass == scene::ESNRP_SOLID){
3996 g_profiler->avg("CM: blocks in range", blocks_in_range);
3997 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3998 if(blocks_in_range != 0)
3999 g_profiler->avg("CM: blocks in range without mesh (frac)",
4000 (float)blocks_in_range_without_mesh/blocks_in_range);
4001 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4004 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4005 if(blocks_had_pass_meshbuf != 0)
4006 g_profiler->avg(prefix+"meshbuffers per block",
4007 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4008 if(blocks_drawn != 0)
4009 g_profiler->avg(prefix+"empty blocks (frac)",
4010 (float)blocks_without_stuff / blocks_drawn);
4012 m_control.blocks_drawn = blocks_drawn;
4013 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4015 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4016 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4019 void ClientMap::renderPostFx()
4021 INodeDefManager *nodemgr = m_gamedef->ndef();
4023 // Sadly ISceneManager has no "post effects" render pass, in that case we
4024 // could just register for that and handle it in renderMap().
4026 m_camera_mutex.Lock();
4027 v3f camera_position = m_camera_position;
4028 m_camera_mutex.Unlock();
4030 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4032 // - If the player is in a solid node, make everything black.
4033 // - If the player is in liquid, draw a semi-transparent overlay.
4034 const ContentFeatures& features = nodemgr->get(n);
4035 video::SColor post_effect_color = features.post_effect_color;
4036 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4038 post_effect_color = video::SColor(255, 0, 0, 0);
4040 if (post_effect_color.getAlpha() != 0)
4042 // Draw a full-screen rectangle
4043 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4044 v2u32 ss = driver->getScreenSize();
4045 core::rect<s32> rect(0,0, ss.X, ss.Y);
4046 driver->draw2DRectangle(post_effect_color, rect);
4050 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4051 core::map<v3s16, MapBlock*> *affected_blocks)
4053 bool changed = false;
4055 Add it to all blocks touching it
4058 v3s16(0,0,0), // this
4059 v3s16(0,0,1), // back
4060 v3s16(0,1,0), // top
4061 v3s16(1,0,0), // right
4062 v3s16(0,0,-1), // front
4063 v3s16(0,-1,0), // bottom
4064 v3s16(-1,0,0), // left
4066 for(u16 i=0; i<7; i++)
4068 v3s16 p2 = p + dirs[i];
4069 // Block position of neighbor (or requested) node
4070 v3s16 blockpos = getNodeBlockPos(p2);
4071 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4072 if(blockref == NULL)
4074 // Relative position of requested node
4075 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4076 if(blockref->setTempMod(relpos, mod))
4081 if(changed && affected_blocks!=NULL)
4083 for(u16 i=0; i<7; i++)
4085 v3s16 p2 = p + dirs[i];
4086 // Block position of neighbor (or requested) node
4087 v3s16 blockpos = getNodeBlockPos(p2);
4088 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4089 if(blockref == NULL)
4091 affected_blocks->insert(blockpos, blockref);
4097 bool ClientMap::clearTempMod(v3s16 p,
4098 core::map<v3s16, MapBlock*> *affected_blocks)
4100 bool changed = false;
4102 v3s16(0,0,0), // this
4103 v3s16(0,0,1), // back
4104 v3s16(0,1,0), // top
4105 v3s16(1,0,0), // right
4106 v3s16(0,0,-1), // front
4107 v3s16(0,-1,0), // bottom
4108 v3s16(-1,0,0), // left
4110 for(u16 i=0; i<7; i++)
4112 v3s16 p2 = p + dirs[i];
4113 // Block position of neighbor (or requested) node
4114 v3s16 blockpos = getNodeBlockPos(p2);
4115 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4116 if(blockref == NULL)
4118 // Relative position of requested node
4119 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4120 if(blockref->clearTempMod(relpos))
4125 if(changed && affected_blocks!=NULL)
4127 for(u16 i=0; i<7; i++)
4129 v3s16 p2 = p + dirs[i];
4130 // Block position of neighbor (or requested) node
4131 v3s16 blockpos = getNodeBlockPos(p2);
4132 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4133 if(blockref == NULL)
4135 affected_blocks->insert(blockpos, blockref);
4141 void ClientMap::expireMeshes(bool only_daynight_diffed)
4143 TimeTaker timer("expireMeshes()");
4145 core::map<v2s16, MapSector*>::Iterator si;
4146 si = m_sectors.getIterator();
4147 for(; si.atEnd() == false; si++)
4149 MapSector *sector = si.getNode()->getValue();
4151 core::list< MapBlock * > sectorblocks;
4152 sector->getBlocks(sectorblocks);
4154 core::list< MapBlock * >::Iterator i;
4155 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4157 MapBlock *block = *i;
4159 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4165 JMutexAutoLock lock(block->mesh_mutex);
4166 if(block->mesh != NULL)
4168 /*block->mesh->drop();
4169 block->mesh = NULL;*/
4170 block->setMeshExpired(true);
4177 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4179 assert(mapType() == MAPTYPE_CLIENT);
4182 v3s16 p = blockpos + v3s16(0,0,0);
4183 MapBlock *b = getBlockNoCreate(p);
4184 b->updateMesh(daynight_ratio);
4185 //b->setMeshExpired(true);
4187 catch(InvalidPositionException &e){}
4190 v3s16 p = blockpos + v3s16(-1,0,0);
4191 MapBlock *b = getBlockNoCreate(p);
4192 b->updateMesh(daynight_ratio);
4193 //b->setMeshExpired(true);
4195 catch(InvalidPositionException &e){}
4197 v3s16 p = blockpos + v3s16(0,-1,0);
4198 MapBlock *b = getBlockNoCreate(p);
4199 b->updateMesh(daynight_ratio);
4200 //b->setMeshExpired(true);
4202 catch(InvalidPositionException &e){}
4204 v3s16 p = blockpos + v3s16(0,0,-1);
4205 MapBlock *b = getBlockNoCreate(p);
4206 b->updateMesh(daynight_ratio);
4207 //b->setMeshExpired(true);
4209 catch(InvalidPositionException &e){}
4214 Update mesh of block in which the node is, and if the node is at the
4215 leading edge, update the appropriate leading blocks too.
4217 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4225 v3s16 blockposes[4];
4226 for(u32 i=0; i<4; i++)
4228 v3s16 np = nodepos + dirs[i];
4229 blockposes[i] = getNodeBlockPos(np);
4230 // Don't update mesh of block if it has been done already
4231 bool already_updated = false;
4232 for(u32 j=0; j<i; j++)
4234 if(blockposes[j] == blockposes[i])
4236 already_updated = true;
4243 MapBlock *b = getBlockNoCreate(blockposes[i]);
4244 b->updateMesh(daynight_ratio);
4249 void ClientMap::PrintInfo(std::ostream &out)
4260 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4265 MapVoxelManipulator::~MapVoxelManipulator()
4267 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4271 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4273 TimeTaker timer1("emerge", &emerge_time);
4275 // Units of these are MapBlocks
4276 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4277 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4279 VoxelArea block_area_nodes
4280 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4282 addArea(block_area_nodes);
4284 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4285 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4286 for(s32 x=p_min.X; x<=p_max.X; x++)
4289 core::map<v3s16, bool>::Node *n;
4290 n = m_loaded_blocks.find(p);
4294 bool block_data_inexistent = false;
4297 TimeTaker timer1("emerge load", &emerge_load_time);
4299 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4300 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4302 a.print(infostream);
4303 infostream<<std::endl;*/
4305 MapBlock *block = m_map->getBlockNoCreate(p);
4306 if(block->isDummy())
4307 block_data_inexistent = true;
4309 block->copyTo(*this);
4311 catch(InvalidPositionException &e)
4313 block_data_inexistent = true;
4316 if(block_data_inexistent)
4318 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4319 // Fill with VOXELFLAG_INEXISTENT
4320 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4321 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4323 s32 i = m_area.index(a.MinEdge.X,y,z);
4324 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4328 m_loaded_blocks.insert(p, !block_data_inexistent);
4331 //infostream<<"emerge done"<<std::endl;
4335 SUGG: Add an option to only update eg. water and air nodes.
4336 This will make it interfere less with important stuff if
4339 void MapVoxelManipulator::blitBack
4340 (core::map<v3s16, MapBlock*> & modified_blocks)
4342 if(m_area.getExtent() == v3s16(0,0,0))
4345 //TimeTaker timer1("blitBack");
4347 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4348 <<m_loaded_blocks.size()<<std::endl;*/
4351 Initialize block cache
4353 v3s16 blockpos_last;
4354 MapBlock *block = NULL;
4355 bool block_checked_in_modified = false;
4357 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4358 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4359 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4363 u8 f = m_flags[m_area.index(p)];
4364 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4367 MapNode &n = m_data[m_area.index(p)];
4369 v3s16 blockpos = getNodeBlockPos(p);
4374 if(block == NULL || blockpos != blockpos_last){
4375 block = m_map->getBlockNoCreate(blockpos);
4376 blockpos_last = blockpos;
4377 block_checked_in_modified = false;
4380 // Calculate relative position in block
4381 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4383 // Don't continue if nothing has changed here
4384 if(block->getNode(relpos) == n)
4387 //m_map->setNode(m_area.MinEdge + p, n);
4388 block->setNode(relpos, n);
4391 Make sure block is in modified_blocks
4393 if(block_checked_in_modified == false)
4395 modified_blocks[blockpos] = block;
4396 block_checked_in_modified = true;
4399 catch(InvalidPositionException &e)
4405 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4406 MapVoxelManipulator(map),
4407 m_create_area(false)
4411 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4415 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4417 // Just create the area so that it can be pointed to
4418 VoxelManipulator::emerge(a, caller_id);
4421 void ManualMapVoxelManipulator::initialEmerge(
4422 v3s16 blockpos_min, v3s16 blockpos_max)
4424 TimeTaker timer1("initialEmerge", &emerge_time);
4426 // Units of these are MapBlocks
4427 v3s16 p_min = blockpos_min;
4428 v3s16 p_max = blockpos_max;
4430 VoxelArea block_area_nodes
4431 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4433 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4436 infostream<<"initialEmerge: area: ";
4437 block_area_nodes.print(infostream);
4438 infostream<<" ("<<size_MB<<"MB)";
4439 infostream<<std::endl;
4442 addArea(block_area_nodes);
4444 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4445 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4446 for(s32 x=p_min.X; x<=p_max.X; x++)
4449 core::map<v3s16, bool>::Node *n;
4450 n = m_loaded_blocks.find(p);
4454 bool block_data_inexistent = false;
4457 TimeTaker timer1("emerge load", &emerge_load_time);
4459 MapBlock *block = m_map->getBlockNoCreate(p);
4460 if(block->isDummy())
4461 block_data_inexistent = true;
4463 block->copyTo(*this);
4465 catch(InvalidPositionException &e)
4467 block_data_inexistent = true;
4470 if(block_data_inexistent)
4473 Mark area inexistent
4475 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4476 // Fill with VOXELFLAG_INEXISTENT
4477 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4478 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4480 s32 i = m_area.index(a.MinEdge.X,y,z);
4481 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4485 m_loaded_blocks.insert(p, !block_data_inexistent);
4489 void ManualMapVoxelManipulator::blitBackAll(
4490 core::map<v3s16, MapBlock*> * modified_blocks)
4492 if(m_area.getExtent() == v3s16(0,0,0))
4496 Copy data of all blocks
4498 for(core::map<v3s16, bool>::Iterator
4499 i = m_loaded_blocks.getIterator();
4500 i.atEnd() == false; i++)
4502 v3s16 p = i.getNode()->getKey();
4503 bool existed = i.getNode()->getValue();
4504 if(existed == false)
4506 // The Great Bug was found using this
4507 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4508 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4512 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4515 infostream<<"WARNING: "<<__FUNCTION_NAME
4516 <<": got NULL block "
4517 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4522 block->copyFrom(*this);
4525 modified_blocks->insert(p, block);