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;
1017 If node is under sunlight and doesn't let sunlight through,
1018 take all sunlighted nodes under it and clear light from them
1019 and from where the light has been spread.
1020 TODO: This could be optimized by mass-unlighting instead
1023 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1027 //m_dout<<DTIME<<"y="<<y<<std::endl;
1028 v3s16 n2pos(p.X, y, p.Z);
1032 n2 = getNode(n2pos);
1034 catch(InvalidPositionException &e)
1039 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1041 unLightNeighbors(LIGHTBANK_DAY,
1042 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1043 light_sources, modified_blocks);
1044 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1052 for(s32 i=0; i<2; i++)
1054 enum LightBank bank = banks[i];
1057 Spread light from all nodes that might be capable of doing so
1059 spreadLight(bank, light_sources, modified_blocks);
1063 Update information about whether day and night light differ
1065 for(core::map<v3s16, MapBlock*>::Iterator
1066 i = modified_blocks.getIterator();
1067 i.atEnd() == false; i++)
1069 MapBlock *block = i.getNode()->getValue();
1070 block->updateDayNightDiff();
1074 Add neighboring liquid nodes and the node itself if it is
1075 liquid (=water node was added) to transform queue.
1078 v3s16(0,0,0), // self
1079 v3s16(0,0,1), // back
1080 v3s16(0,1,0), // top
1081 v3s16(1,0,0), // right
1082 v3s16(0,0,-1), // front
1083 v3s16(0,-1,0), // bottom
1084 v3s16(-1,0,0), // left
1086 for(u16 i=0; i<7; i++)
1091 v3s16 p2 = p + dirs[i];
1093 MapNode n2 = getNode(p2);
1094 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1096 m_transforming_liquid.push_back(p2);
1099 }catch(InvalidPositionException &e)
1107 void Map::removeNodeAndUpdate(v3s16 p,
1108 core::map<v3s16, MapBlock*> &modified_blocks)
1110 INodeDefManager *nodemgr = m_gamedef->ndef();
1112 /*PrintInfo(m_dout);
1113 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1114 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1116 bool node_under_sunlight = true;
1118 v3s16 toppos = p + v3s16(0,1,0);
1120 // Node will be replaced with this
1121 content_t replace_material = CONTENT_AIR;
1124 If there is a node at top and it doesn't have sunlight,
1125 there will be no sunlight going down.
1128 MapNode topnode = getNode(toppos);
1130 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1131 node_under_sunlight = false;
1133 catch(InvalidPositionException &e)
1137 core::map<v3s16, bool> light_sources;
1139 enum LightBank banks[] =
1144 for(s32 i=0; i<2; i++)
1146 enum LightBank bank = banks[i];
1149 Unlight neighbors (in case the node is a light source)
1151 unLightNeighbors(bank, p,
1152 getNode(p).getLight(bank, nodemgr),
1153 light_sources, modified_blocks);
1157 Remove node metadata
1160 removeNodeMetadata(p);
1164 This also clears the lighting.
1168 n.setContent(replace_material);
1171 for(s32 i=0; i<2; i++)
1173 enum LightBank bank = banks[i];
1176 Recalculate lighting
1178 spreadLight(bank, light_sources, modified_blocks);
1181 // Add the block of the removed node to modified_blocks
1182 v3s16 blockpos = getNodeBlockPos(p);
1183 MapBlock * block = getBlockNoCreate(blockpos);
1184 assert(block != NULL);
1185 modified_blocks.insert(blockpos, block);
1188 If the removed node was under sunlight, propagate the
1189 sunlight down from it and then light all neighbors
1190 of the propagated blocks.
1192 if(node_under_sunlight)
1194 s16 ybottom = propagateSunlight(p, modified_blocks);
1195 /*m_dout<<DTIME<<"Node was under sunlight. "
1196 "Propagating sunlight";
1197 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1199 for(; y >= ybottom; y--)
1201 v3s16 p2(p.X, y, p.Z);
1202 /*m_dout<<DTIME<<"lighting neighbors of node ("
1203 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1205 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1210 // Set the lighting of this node to 0
1211 // TODO: Is this needed? Lighting is cleared up there already.
1213 MapNode n = getNode(p);
1214 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1217 catch(InvalidPositionException &e)
1223 for(s32 i=0; i<2; i++)
1225 enum LightBank bank = banks[i];
1227 // Get the brightest neighbour node and propagate light from it
1228 v3s16 n2p = getBrightestNeighbour(bank, p);
1230 MapNode n2 = getNode(n2p);
1231 lightNeighbors(bank, n2p, modified_blocks);
1233 catch(InvalidPositionException &e)
1239 Update information about whether day and night light differ
1241 for(core::map<v3s16, MapBlock*>::Iterator
1242 i = modified_blocks.getIterator();
1243 i.atEnd() == false; i++)
1245 MapBlock *block = i.getNode()->getValue();
1246 block->updateDayNightDiff();
1250 Add neighboring liquid nodes and this node to transform queue.
1251 (it's vital for the node itself to get updated last.)
1254 v3s16(0,0,1), // back
1255 v3s16(0,1,0), // top
1256 v3s16(1,0,0), // right
1257 v3s16(0,0,-1), // front
1258 v3s16(0,-1,0), // bottom
1259 v3s16(-1,0,0), // left
1260 v3s16(0,0,0), // self
1262 for(u16 i=0; i<7; i++)
1267 v3s16 p2 = p + dirs[i];
1269 MapNode n2 = getNode(p2);
1270 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1272 m_transforming_liquid.push_back(p2);
1275 }catch(InvalidPositionException &e)
1281 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1284 event.type = MEET_ADDNODE;
1288 bool succeeded = true;
1290 core::map<v3s16, MapBlock*> modified_blocks;
1291 addNodeAndUpdate(p, n, modified_blocks);
1293 // Copy modified_blocks to event
1294 for(core::map<v3s16, MapBlock*>::Iterator
1295 i = modified_blocks.getIterator();
1296 i.atEnd()==false; i++)
1298 event.modified_blocks.insert(i.getNode()->getKey(), false);
1301 catch(InvalidPositionException &e){
1305 dispatchEvent(&event);
1310 bool Map::removeNodeWithEvent(v3s16 p)
1313 event.type = MEET_REMOVENODE;
1316 bool succeeded = true;
1318 core::map<v3s16, MapBlock*> modified_blocks;
1319 removeNodeAndUpdate(p, modified_blocks);
1321 // Copy modified_blocks to event
1322 for(core::map<v3s16, MapBlock*>::Iterator
1323 i = modified_blocks.getIterator();
1324 i.atEnd()==false; i++)
1326 event.modified_blocks.insert(i.getNode()->getKey(), false);
1329 catch(InvalidPositionException &e){
1333 dispatchEvent(&event);
1338 bool Map::dayNightDiffed(v3s16 blockpos)
1341 v3s16 p = blockpos + v3s16(0,0,0);
1342 MapBlock *b = getBlockNoCreate(p);
1343 if(b->dayNightDiffed())
1346 catch(InvalidPositionException &e){}
1349 v3s16 p = blockpos + v3s16(-1,0,0);
1350 MapBlock *b = getBlockNoCreate(p);
1351 if(b->dayNightDiffed())
1354 catch(InvalidPositionException &e){}
1356 v3s16 p = blockpos + v3s16(0,-1,0);
1357 MapBlock *b = getBlockNoCreate(p);
1358 if(b->dayNightDiffed())
1361 catch(InvalidPositionException &e){}
1363 v3s16 p = blockpos + v3s16(0,0,-1);
1364 MapBlock *b = getBlockNoCreate(p);
1365 if(b->dayNightDiffed())
1368 catch(InvalidPositionException &e){}
1371 v3s16 p = blockpos + v3s16(1,0,0);
1372 MapBlock *b = getBlockNoCreate(p);
1373 if(b->dayNightDiffed())
1376 catch(InvalidPositionException &e){}
1378 v3s16 p = blockpos + v3s16(0,1,0);
1379 MapBlock *b = getBlockNoCreate(p);
1380 if(b->dayNightDiffed())
1383 catch(InvalidPositionException &e){}
1385 v3s16 p = blockpos + v3s16(0,0,1);
1386 MapBlock *b = getBlockNoCreate(p);
1387 if(b->dayNightDiffed())
1390 catch(InvalidPositionException &e){}
1396 Updates usage timers
1398 void Map::timerUpdate(float dtime, float unload_timeout,
1399 core::list<v3s16> *unloaded_blocks)
1401 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1403 // Profile modified reasons
1404 Profiler modprofiler;
1406 core::list<v2s16> sector_deletion_queue;
1407 u32 deleted_blocks_count = 0;
1408 u32 saved_blocks_count = 0;
1409 u32 block_count_all = 0;
1411 core::map<v2s16, MapSector*>::Iterator si;
1414 si = m_sectors.getIterator();
1415 for(; si.atEnd() == false; si++)
1417 MapSector *sector = si.getNode()->getValue();
1419 bool all_blocks_deleted = true;
1421 core::list<MapBlock*> blocks;
1422 sector->getBlocks(blocks);
1424 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1425 i != blocks.end(); i++)
1427 MapBlock *block = (*i);
1429 block->incrementUsageTimer(dtime);
1431 if(block->getUsageTimer() > unload_timeout)
1433 v3s16 p = block->getPos();
1436 if(block->getModified() != MOD_STATE_CLEAN
1437 && save_before_unloading)
1439 modprofiler.add(block->getModifiedReason(), 1);
1441 saved_blocks_count++;
1444 // Delete from memory
1445 sector->deleteBlock(block);
1448 unloaded_blocks->push_back(p);
1450 deleted_blocks_count++;
1454 all_blocks_deleted = false;
1459 if(all_blocks_deleted)
1461 sector_deletion_queue.push_back(si.getNode()->getKey());
1466 // Finally delete the empty sectors
1467 deleteSectors(sector_deletion_queue);
1469 if(deleted_blocks_count != 0)
1471 PrintInfo(infostream); // ServerMap/ClientMap:
1472 infostream<<"Unloaded "<<deleted_blocks_count
1473 <<" blocks from memory";
1474 if(save_before_unloading)
1475 infostream<<", of which "<<saved_blocks_count<<" were written";
1476 infostream<<", "<<block_count_all<<" blocks in memory";
1477 infostream<<"."<<std::endl;
1478 if(saved_blocks_count != 0){
1479 PrintInfo(infostream); // ServerMap/ClientMap:
1480 infostream<<"Blocks modified by: "<<std::endl;
1481 modprofiler.print(infostream);
1486 void Map::deleteSectors(core::list<v2s16> &list)
1488 core::list<v2s16>::Iterator j;
1489 for(j=list.begin(); j!=list.end(); j++)
1491 MapSector *sector = m_sectors[*j];
1492 // If sector is in sector cache, remove it from there
1493 if(m_sector_cache == sector)
1494 m_sector_cache = NULL;
1495 // Remove from map and delete
1496 m_sectors.remove(*j);
1502 void Map::unloadUnusedData(float timeout,
1503 core::list<v3s16> *deleted_blocks)
1505 core::list<v2s16> sector_deletion_queue;
1506 u32 deleted_blocks_count = 0;
1507 u32 saved_blocks_count = 0;
1509 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1510 for(; si.atEnd() == false; si++)
1512 MapSector *sector = si.getNode()->getValue();
1514 bool all_blocks_deleted = true;
1516 core::list<MapBlock*> blocks;
1517 sector->getBlocks(blocks);
1518 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1519 i != blocks.end(); i++)
1521 MapBlock *block = (*i);
1523 if(block->getUsageTimer() > timeout)
1526 if(block->getModified() != MOD_STATE_CLEAN)
1529 saved_blocks_count++;
1531 // Delete from memory
1532 sector->deleteBlock(block);
1533 deleted_blocks_count++;
1537 all_blocks_deleted = false;
1541 if(all_blocks_deleted)
1543 sector_deletion_queue.push_back(si.getNode()->getKey());
1547 deleteSectors(sector_deletion_queue);
1549 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1550 <<", of which "<<saved_blocks_count<<" were wr."
1553 //return sector_deletion_queue.getSize();
1554 //return deleted_blocks_count;
1558 void Map::PrintInfo(std::ostream &out)
1563 #define WATER_DROP_BOOST 4
1567 NEIGHBOR_SAME_LEVEL,
1570 struct NodeNeighbor {
1576 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1578 INodeDefManager *nodemgr = m_gamedef->ndef();
1580 DSTACK(__FUNCTION_NAME);
1581 //TimeTaker timer("transformLiquids()");
1584 u32 initial_size = m_transforming_liquid.size();
1586 /*if(initial_size != 0)
1587 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1589 // list of nodes that due to viscosity have not reached their max level height
1590 UniqueQueue<v3s16> must_reflow;
1592 // List of MapBlocks that will require a lighting update (due to lava)
1593 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1595 while(m_transforming_liquid.size() != 0)
1597 // This should be done here so that it is done when continue is used
1598 if(loopcount >= initial_size * 3)
1603 Get a queued transforming liquid node
1605 v3s16 p0 = m_transforming_liquid.pop_front();
1607 MapNode n0 = getNodeNoEx(p0);
1610 Collect information about current node
1612 s8 liquid_level = -1;
1613 u8 liquid_kind = CONTENT_IGNORE;
1614 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1615 switch (liquid_type) {
1617 liquid_level = LIQUID_LEVEL_SOURCE;
1618 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1620 case LIQUID_FLOWING:
1621 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1622 liquid_kind = n0.getContent();
1625 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1626 // continue with the next node.
1627 if (n0.getContent() != CONTENT_AIR)
1629 liquid_kind = CONTENT_AIR;
1634 Collect information about the environment
1636 const v3s16 *dirs = g_6dirs;
1637 NodeNeighbor sources[6]; // surrounding sources
1638 int num_sources = 0;
1639 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1641 NodeNeighbor airs[6]; // surrounding air
1643 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1644 int num_neutrals = 0;
1645 bool flowing_down = false;
1646 for (u16 i = 0; i < 6; i++) {
1647 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1650 nt = NEIGHBOR_UPPER;
1653 nt = NEIGHBOR_LOWER;
1656 v3s16 npos = p0 + dirs[i];
1657 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1658 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1660 if (nb.n.getContent() == CONTENT_AIR) {
1661 airs[num_airs++] = nb;
1662 // if the current node is a water source the neighbor
1663 // should be enqueded for transformation regardless of whether the
1664 // current node changes or not.
1665 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1666 m_transforming_liquid.push_back(npos);
1667 // if the current node happens to be a flowing node, it will start to flow down here.
1668 if (nb.t == NEIGHBOR_LOWER) {
1669 flowing_down = true;
1672 neutrals[num_neutrals++] = nb;
1676 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1677 if (liquid_kind == CONTENT_AIR)
1678 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1679 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1680 neutrals[num_neutrals++] = nb;
1682 // Do not count bottom source, it will screw things up
1684 sources[num_sources++] = nb;
1687 case LIQUID_FLOWING:
1688 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1689 if (liquid_kind == CONTENT_AIR)
1690 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1691 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1692 neutrals[num_neutrals++] = nb;
1694 flows[num_flows++] = nb;
1695 if (nb.t == NEIGHBOR_LOWER)
1696 flowing_down = true;
1703 decide on the type (and possibly level) of the current node
1705 content_t new_node_content;
1706 s8 new_node_level = -1;
1707 s8 max_node_level = -1;
1708 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1709 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1710 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1711 // it's perfectly safe to use liquid_kind here to determine the new node content.
1712 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1713 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1714 // liquid_kind is set properly, see above
1715 new_node_content = liquid_kind;
1716 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1718 // no surrounding sources, so get the maximum level that can flow into this node
1719 for (u16 i = 0; i < num_flows; i++) {
1720 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1721 switch (flows[i].t) {
1722 case NEIGHBOR_UPPER:
1723 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1724 max_node_level = LIQUID_LEVEL_MAX;
1725 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1726 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1727 } else if (nb_liquid_level > max_node_level)
1728 max_node_level = nb_liquid_level;
1730 case NEIGHBOR_LOWER:
1732 case NEIGHBOR_SAME_LEVEL:
1733 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1734 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1735 max_node_level = nb_liquid_level - 1;
1741 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1742 if (viscosity > 1 && max_node_level != liquid_level) {
1743 // amount to gain, limited by viscosity
1744 // must be at least 1 in absolute value
1745 s8 level_inc = max_node_level - liquid_level;
1746 if (level_inc < -viscosity || level_inc > viscosity)
1747 new_node_level = liquid_level + level_inc/viscosity;
1748 else if (level_inc < 0)
1749 new_node_level = liquid_level - 1;
1750 else if (level_inc > 0)
1751 new_node_level = liquid_level + 1;
1752 if (new_node_level != max_node_level)
1753 must_reflow.push_back(p0);
1755 new_node_level = max_node_level;
1757 if (new_node_level >= 0)
1758 new_node_content = liquid_kind;
1760 new_node_content = CONTENT_AIR;
1765 check if anything has changed. if not, just continue with the next node.
1767 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1768 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1769 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1775 update the current node
1777 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1778 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1779 // set level to last 3 bits, flowing down bit to 4th bit
1780 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1782 // set the liquid level and flow bit to 0
1783 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1785 n0.setContent(new_node_content);
1787 v3s16 blockpos = getNodeBlockPos(p0);
1788 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1790 modified_blocks.insert(blockpos, block);
1791 // If node emits light, MapBlock requires lighting update
1792 if(nodemgr->get(n0).light_source != 0)
1793 lighting_modified_blocks[block->getPos()] = block;
1797 enqueue neighbors for update if neccessary
1799 switch (nodemgr->get(n0.getContent()).liquid_type) {
1801 case LIQUID_FLOWING:
1802 // make sure source flows into all neighboring nodes
1803 for (u16 i = 0; i < num_flows; i++)
1804 if (flows[i].t != NEIGHBOR_UPPER)
1805 m_transforming_liquid.push_back(flows[i].p);
1806 for (u16 i = 0; i < num_airs; i++)
1807 if (airs[i].t != NEIGHBOR_UPPER)
1808 m_transforming_liquid.push_back(airs[i].p);
1811 // this flow has turned to air; neighboring flows might need to do the same
1812 for (u16 i = 0; i < num_flows; i++)
1813 m_transforming_liquid.push_back(flows[i].p);
1817 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1818 while (must_reflow.size() > 0)
1819 m_transforming_liquid.push_back(must_reflow.pop_front());
1820 updateLighting(lighting_modified_blocks, modified_blocks);
1823 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1825 v3s16 blockpos = getNodeBlockPos(p);
1826 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1827 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1829 infostream<<"Map::getNodeMetadata(): Need to emerge "
1830 <<PP(blockpos)<<std::endl;
1831 block = emergeBlock(blockpos, false);
1835 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1839 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1843 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1845 v3s16 blockpos = getNodeBlockPos(p);
1846 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1847 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1849 infostream<<"Map::setNodeMetadata(): Need to emerge "
1850 <<PP(blockpos)<<std::endl;
1851 block = emergeBlock(blockpos, false);
1855 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1859 block->m_node_metadata->set(p_rel, meta);
1862 void Map::removeNodeMetadata(v3s16 p)
1864 v3s16 blockpos = getNodeBlockPos(p);
1865 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1866 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1869 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1873 block->m_node_metadata->remove(p_rel);
1876 void Map::nodeMetadataStep(float dtime,
1877 core::map<v3s16, MapBlock*> &changed_blocks)
1881 Currently there is no way to ensure that all the necessary
1882 blocks are loaded when this is run. (They might get unloaded)
1883 NOTE: ^- Actually, that might not be so. In a quick test it
1884 reloaded a block with a furnace when I walked back to it from
1887 core::map<v2s16, MapSector*>::Iterator si;
1888 si = m_sectors.getIterator();
1889 for(; si.atEnd() == false; si++)
1891 MapSector *sector = si.getNode()->getValue();
1892 core::list< MapBlock * > sectorblocks;
1893 sector->getBlocks(sectorblocks);
1894 core::list< MapBlock * >::Iterator i;
1895 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1897 MapBlock *block = *i;
1898 bool changed = block->m_node_metadata->step(dtime);
1900 changed_blocks[block->getPos()] = block;
1909 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1910 Map(dout_server, gamedef),
1912 m_map_metadata_changed(true),
1914 m_database_read(NULL),
1915 m_database_write(NULL)
1917 infostream<<__FUNCTION_NAME<<std::endl;
1919 //m_chunksize = 8; // Takes a few seconds
1921 if (g_settings->get("fixed_map_seed").empty())
1923 m_seed = (((u64)(myrand()%0xffff)<<0)
1924 + ((u64)(myrand()%0xffff)<<16)
1925 + ((u64)(myrand()%0xffff)<<32)
1926 + ((u64)(myrand()%0xffff)<<48));
1930 m_seed = g_settings->getU64("fixed_map_seed");
1934 Experimental and debug stuff
1941 Try to load map; if not found, create a new one.
1944 m_savedir = savedir;
1945 m_map_saving_enabled = false;
1949 // If directory exists, check contents and load if possible
1950 if(fs::PathExists(m_savedir))
1952 // If directory is empty, it is safe to save into it.
1953 if(fs::GetDirListing(m_savedir).size() == 0)
1955 infostream<<"Server: Empty save directory is valid."
1957 m_map_saving_enabled = true;
1962 // Load map metadata (seed, chunksize)
1965 catch(FileNotGoodException &e){
1966 infostream<<"WARNING: Could not load map metadata"
1967 //<<" Disabling chunk-based generator."
1973 // Load chunk metadata
1976 catch(FileNotGoodException &e){
1977 infostream<<"WARNING: Could not load chunk metadata."
1978 <<" Disabling chunk-based generator."
1983 /*infostream<<"Server: Successfully loaded chunk "
1984 "metadata and sector (0,0) from "<<savedir<<
1985 ", assuming valid save directory."
1988 infostream<<"Server: Successfully loaded map "
1989 <<"and chunk metadata from "<<savedir
1990 <<", assuming valid save directory."
1993 m_map_saving_enabled = true;
1994 // Map loaded, not creating new one
1998 // If directory doesn't exist, it is safe to save to it
2000 m_map_saving_enabled = true;
2003 catch(std::exception &e)
2005 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2006 <<", exception: "<<e.what()<<std::endl;
2007 infostream<<"Please remove the map or fix it."<<std::endl;
2008 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2011 infostream<<"Initializing new map."<<std::endl;
2013 // Create zero sector
2014 emergeSector(v2s16(0,0));
2016 // Initially write whole map
2017 save(MOD_STATE_CLEAN);
2020 ServerMap::~ServerMap()
2022 infostream<<__FUNCTION_NAME<<std::endl;
2026 if(m_map_saving_enabled)
2028 // Save only changed parts
2029 save(MOD_STATE_WRITE_AT_UNLOAD);
2030 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2034 infostream<<"Server: map not saved"<<std::endl;
2037 catch(std::exception &e)
2039 infostream<<"Server: Failed to save map to "<<m_savedir
2040 <<", exception: "<<e.what()<<std::endl;
2044 Close database if it was opened
2047 sqlite3_finalize(m_database_read);
2048 if(m_database_write)
2049 sqlite3_finalize(m_database_write);
2051 sqlite3_close(m_database);
2057 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2058 for(; i.atEnd() == false; i++)
2060 MapChunk *chunk = i.getNode()->getValue();
2066 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2068 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2069 if(enable_mapgen_debug_info)
2070 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2071 <<blockpos.Z<<")"<<std::endl;
2073 // Do nothing if not inside limits (+-1 because of neighbors)
2074 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2075 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2081 data->no_op = false;
2082 data->seed = m_seed;
2083 data->blockpos = blockpos;
2084 data->nodedef = m_gamedef->ndef();
2087 Create the whole area of this and the neighboring blocks
2090 //TimeTaker timer("initBlockMake() create area");
2092 for(s16 x=-1; x<=1; x++)
2093 for(s16 z=-1; z<=1; z++)
2095 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2096 // Sector metadata is loaded from disk if not already loaded.
2097 ServerMapSector *sector = createSector(sectorpos);
2100 for(s16 y=-1; y<=1; y++)
2102 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2103 //MapBlock *block = createBlock(p);
2104 // 1) get from memory, 2) load from disk
2105 MapBlock *block = emergeBlock(p, false);
2106 // 3) create a blank one
2109 block = createBlock(p);
2112 Block gets sunlight if this is true.
2114 Refer to the map generator heuristics.
2116 bool ug = mapgen::block_is_underground(data->seed, p);
2117 block->setIsUnderground(ug);
2120 // Lighting will not be valid after make_chunk is called
2121 block->setLightingExpired(true);
2122 // Lighting will be calculated
2123 //block->setLightingExpired(false);
2129 Now we have a big empty area.
2131 Make a ManualMapVoxelManipulator that contains this and the
2135 // The area that contains this block and it's neighbors
2136 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2137 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2139 data->vmanip = new ManualMapVoxelManipulator(this);
2140 //data->vmanip->setMap(this);
2144 //TimeTaker timer("initBlockMake() initialEmerge");
2145 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2148 // Data is ready now.
2151 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2152 core::map<v3s16, MapBlock*> &changed_blocks)
2154 v3s16 blockpos = data->blockpos;
2155 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2156 <<blockpos.Z<<")"<<std::endl;*/
2160 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2164 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2166 /*infostream<<"Resulting vmanip:"<<std::endl;
2167 data->vmanip.print(infostream);*/
2169 // Make sure affected blocks are loaded
2170 for(s16 x=-1; x<=1; x++)
2171 for(s16 z=-1; z<=1; z++)
2172 for(s16 y=-1; y<=1; y++)
2174 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2175 // Load from disk if not already in memory
2176 emergeBlock(p, false);
2180 Blit generated stuff to map
2181 NOTE: blitBackAll adds nearly everything to changed_blocks
2185 //TimeTaker timer("finishBlockMake() blitBackAll");
2186 data->vmanip->blitBackAll(&changed_blocks);
2189 if(enable_mapgen_debug_info)
2190 infostream<<"finishBlockMake: changed_blocks.size()="
2191 <<changed_blocks.size()<<std::endl;
2194 Copy transforming liquid information
2196 while(data->transforming_liquid.size() > 0)
2198 v3s16 p = data->transforming_liquid.pop_front();
2199 m_transforming_liquid.push_back(p);
2205 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2209 Set is_underground flag for lighting with sunlight.
2211 Refer to map generator heuristics.
2213 NOTE: This is done in initChunkMake
2215 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2219 Add sunlight to central block.
2220 This makes in-dark-spawning monsters to not flood the whole thing.
2221 Do not spread the light, though.
2223 /*core::map<v3s16, bool> light_sources;
2224 bool black_air_left = false;
2225 block->propagateSunlight(light_sources, true, &black_air_left);*/
2228 NOTE: Lighting and object adding shouldn't really be here, but
2229 lighting is a bit tricky to move properly to makeBlock.
2230 TODO: Do this the right way anyway, that is, move it to makeBlock.
2231 - There needs to be some way for makeBlock to report back if
2232 the lighting update is going further down because of the
2233 new block blocking light
2238 NOTE: This takes ~60ms, TODO: Investigate why
2241 TimeTaker t("finishBlockMake lighting update");
2243 core::map<v3s16, MapBlock*> lighting_update_blocks;
2246 lighting_update_blocks.insert(block->getPos(), block);
2251 v3s16 p = block->getPos()+v3s16(x,1,z);
2252 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2256 // All modified blocks
2257 // NOTE: Should this be done? If this is not done, then the lighting
2258 // of the others will be updated in a different place, one by one, i
2259 // think... or they might not? Well, at least they are left marked as
2260 // "lighting expired"; it seems that is not handled at all anywhere,
2261 // so enabling this will slow it down A LOT because otherwise it
2262 // would not do this at all. This causes the black trees.
2263 for(core::map<v3s16, MapBlock*>::Iterator
2264 i = changed_blocks.getIterator();
2265 i.atEnd() == false; i++)
2267 lighting_update_blocks.insert(i.getNode()->getKey(),
2268 i.getNode()->getValue());
2270 /*// Also force-add all the upmost blocks for proper sunlight
2271 for(s16 x=-1; x<=1; x++)
2272 for(s16 z=-1; z<=1; z++)
2274 v3s16 p = block->getPos()+v3s16(x,1,z);
2275 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2278 updateLighting(lighting_update_blocks, changed_blocks);
2281 Set lighting to non-expired state in all of them.
2282 This is cheating, but it is not fast enough if all of them
2283 would actually be updated.
2285 for(s16 x=-1; x<=1; x++)
2286 for(s16 y=-1; y<=1; y++)
2287 for(s16 z=-1; z<=1; z++)
2289 v3s16 p = block->getPos()+v3s16(x,y,z);
2290 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2293 if(enable_mapgen_debug_info == false)
2294 t.stop(true); // Hide output
2298 Add random objects to block
2300 mapgen::add_random_objects(block);
2303 Go through changed blocks
2305 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2306 i.atEnd() == false; i++)
2308 MapBlock *block = i.getNode()->getValue();
2311 Update day/night difference cache of the MapBlocks
2313 block->updateDayNightDiff();
2315 Set block as modified
2317 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2318 "finishBlockMake updateDayNightDiff");
2322 Set central block as generated
2324 block->setGenerated(true);
2327 Save changed parts of map
2328 NOTE: Will be saved later.
2330 //save(MOD_STATE_WRITE_AT_UNLOAD);
2332 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2333 <<blockpos.Z<<")"<<std::endl;*/
2335 if(enable_mapgen_debug_info)
2338 Analyze resulting blocks
2340 for(s16 x=-1; x<=1; x++)
2341 for(s16 y=-1; y<=1; y++)
2342 for(s16 z=-1; z<=1; z++)
2344 v3s16 p = block->getPos()+v3s16(x,y,z);
2345 MapBlock *block = getBlockNoCreateNoEx(p);
2347 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2348 infostream<<"Generated "<<spos<<": "
2349 <<analyze_block(block)<<std::endl;
2357 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2359 DSTACKF("%s: p2d=(%d,%d)",
2364 Check if it exists already in memory
2366 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2371 Try to load it from disk (with blocks)
2373 //if(loadSectorFull(p2d) == true)
2376 Try to load metadata from disk
2379 if(loadSectorMeta(p2d) == true)
2381 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2384 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2385 throw InvalidPositionException("");
2391 Do not create over-limit
2393 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2394 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2395 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2396 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2397 throw InvalidPositionException("createSector(): pos. over limit");
2400 Generate blank sector
2403 sector = new ServerMapSector(this, p2d, m_gamedef);
2405 // Sector position on map in nodes
2406 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2411 m_sectors.insert(p2d, sector);
2417 This is a quick-hand function for calling makeBlock().
2419 MapBlock * ServerMap::generateBlock(
2421 core::map<v3s16, MapBlock*> &modified_blocks
2424 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2426 /*infostream<<"generateBlock(): "
2427 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2430 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2432 TimeTaker timer("generateBlock");
2434 //MapBlock *block = original_dummy;
2436 v2s16 p2d(p.X, p.Z);
2437 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2440 Do not generate over-limit
2442 if(blockpos_over_limit(p))
2444 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2445 throw InvalidPositionException("generateBlock(): pos. over limit");
2449 Create block make data
2451 mapgen::BlockMakeData data;
2452 initBlockMake(&data, p);
2458 TimeTaker t("mapgen::make_block()");
2459 mapgen::make_block(&data);
2461 if(enable_mapgen_debug_info == false)
2462 t.stop(true); // Hide output
2466 Blit data back on map, update lighting, add mobs and whatever this does
2468 finishBlockMake(&data, modified_blocks);
2473 MapBlock *block = getBlockNoCreateNoEx(p);
2481 bool erroneus_content = false;
2482 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2483 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2484 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2487 MapNode n = block->getNode(p);
2488 if(n.getContent() == CONTENT_IGNORE)
2490 infostream<<"CONTENT_IGNORE at "
2491 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2493 erroneus_content = true;
2497 if(erroneus_content)
2506 Generate a completely empty block
2510 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2511 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2513 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2516 n.setContent(CONTENT_AIR);
2517 block->setNode(v3s16(x0,y0,z0), n);
2523 if(enable_mapgen_debug_info == false)
2524 timer.stop(true); // Hide output
2529 MapBlock * ServerMap::createBlock(v3s16 p)
2531 DSTACKF("%s: p=(%d,%d,%d)",
2532 __FUNCTION_NAME, p.X, p.Y, p.Z);
2535 Do not create over-limit
2537 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2538 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2539 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2540 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2541 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2542 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2543 throw InvalidPositionException("createBlock(): pos. over limit");
2545 v2s16 p2d(p.X, p.Z);
2548 This will create or load a sector if not found in memory.
2549 If block exists on disk, it will be loaded.
2551 NOTE: On old save formats, this will be slow, as it generates
2552 lighting on blocks for them.
2554 ServerMapSector *sector;
2556 sector = (ServerMapSector*)createSector(p2d);
2557 assert(sector->getId() == MAPSECTOR_SERVER);
2559 catch(InvalidPositionException &e)
2561 infostream<<"createBlock: createSector() failed"<<std::endl;
2565 NOTE: This should not be done, or at least the exception
2566 should not be passed on as std::exception, because it
2567 won't be catched at all.
2569 /*catch(std::exception &e)
2571 infostream<<"createBlock: createSector() failed: "
2572 <<e.what()<<std::endl;
2577 Try to get a block from the sector
2580 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2583 if(block->isDummy())
2588 block = sector->createBlankBlock(block_y);
2592 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2594 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2596 p.X, p.Y, p.Z, allow_generate);
2599 MapBlock *block = getBlockNoCreateNoEx(p);
2600 if(block && block->isDummy() == false)
2605 MapBlock *block = loadBlock(p);
2612 core::map<v3s16, MapBlock*> modified_blocks;
2613 MapBlock *block = generateBlock(p, modified_blocks);
2617 event.type = MEET_OTHER;
2620 // Copy modified_blocks to event
2621 for(core::map<v3s16, MapBlock*>::Iterator
2622 i = modified_blocks.getIterator();
2623 i.atEnd()==false; i++)
2625 event.modified_blocks.insert(i.getNode()->getKey(), false);
2629 dispatchEvent(&event);
2638 s16 ServerMap::findGroundLevel(v2s16 p2d)
2642 Uh, just do something random...
2644 // Find existing map from top to down
2647 v3s16 p(p2d.X, max, p2d.Y);
2648 for(; p.Y>min; p.Y--)
2650 MapNode n = getNodeNoEx(p);
2651 if(n.getContent() != CONTENT_IGNORE)
2656 // If this node is not air, go to plan b
2657 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2659 // Search existing walkable and return it
2660 for(; p.Y>min; p.Y--)
2662 MapNode n = getNodeNoEx(p);
2663 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2672 Determine from map generator noise functions
2675 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2678 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2679 //return (s16)level;
2682 void ServerMap::createDatabase() {
2685 e = sqlite3_exec(m_database,
2686 "CREATE TABLE IF NOT EXISTS `blocks` ("
2687 "`pos` INT NOT NULL PRIMARY KEY,"
2690 , NULL, NULL, NULL);
2691 if(e == SQLITE_ABORT)
2692 throw FileNotGoodException("Could not create database structure");
2694 infostream<<"Server: Database structure was created";
2697 void ServerMap::verifyDatabase() {
2702 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2703 bool needs_create = false;
2707 Open the database connection
2710 createDirs(m_savedir);
2712 if(!fs::PathExists(dbp))
2713 needs_create = true;
2715 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2716 if(d != SQLITE_OK) {
2717 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2718 throw FileNotGoodException("Cannot open database file");
2724 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2725 if(d != SQLITE_OK) {
2726 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2727 throw FileNotGoodException("Cannot prepare read statement");
2730 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2731 if(d != SQLITE_OK) {
2732 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2733 throw FileNotGoodException("Cannot prepare write statement");
2736 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2737 if(d != SQLITE_OK) {
2738 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2739 throw FileNotGoodException("Cannot prepare read statement");
2742 infostream<<"Server: Database opened"<<std::endl;
2746 bool ServerMap::loadFromFolders() {
2747 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2752 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2753 return (sqlite3_int64)pos.Z*16777216 +
2754 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2757 void ServerMap::createDirs(std::string path)
2759 if(fs::CreateAllDirs(path) == false)
2761 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2762 <<"\""<<path<<"\""<<std::endl;
2763 throw BaseException("ServerMap failed to create directory");
2767 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2773 snprintf(cc, 9, "%.4x%.4x",
2774 (unsigned int)pos.X&0xffff,
2775 (unsigned int)pos.Y&0xffff);
2777 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2779 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2780 (unsigned int)pos.X&0xfff,
2781 (unsigned int)pos.Y&0xfff);
2783 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2789 v2s16 ServerMap::getSectorPos(std::string dirname)
2793 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2794 assert(spos != std::string::npos);
2795 if(dirname.size() - spos == 8)
2798 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2800 else if(dirname.size() - spos == 3)
2803 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2804 // Sign-extend the 12 bit values up to 16 bits...
2805 if(x&0x800) x|=0xF000;
2806 if(y&0x800) y|=0xF000;
2813 v2s16 pos((s16)x, (s16)y);
2817 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2819 v2s16 p2d = getSectorPos(sectordir);
2821 if(blockfile.size() != 4){
2822 throw InvalidFilenameException("Invalid block filename");
2825 int r = sscanf(blockfile.c_str(), "%4x", &y);
2827 throw InvalidFilenameException("Invalid block filename");
2828 return v3s16(p2d.X, y, p2d.Y);
2831 std::string ServerMap::getBlockFilename(v3s16 p)
2834 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2838 void ServerMap::save(ModifiedState save_level)
2840 DSTACK(__FUNCTION_NAME);
2841 if(m_map_saving_enabled == false)
2843 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2847 if(save_level == MOD_STATE_CLEAN)
2848 infostream<<"ServerMap: Saving whole map, this can take time."
2851 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2856 // Profile modified reasons
2857 Profiler modprofiler;
2859 u32 sector_meta_count = 0;
2860 u32 block_count = 0;
2861 u32 block_count_all = 0; // Number of blocks in memory
2863 // Don't do anything with sqlite unless something is really saved
2864 bool save_started = false;
2866 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2867 for(; i.atEnd() == false; i++)
2869 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2870 assert(sector->getId() == MAPSECTOR_SERVER);
2872 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2874 saveSectorMeta(sector);
2875 sector_meta_count++;
2877 core::list<MapBlock*> blocks;
2878 sector->getBlocks(blocks);
2879 core::list<MapBlock*>::Iterator j;
2881 for(j=blocks.begin(); j!=blocks.end(); j++)
2883 MapBlock *block = *j;
2887 if(block->getModified() >= save_level)
2892 save_started = true;
2895 modprofiler.add(block->getModifiedReason(), 1);
2900 /*infostream<<"ServerMap: Written block ("
2901 <<block->getPos().X<<","
2902 <<block->getPos().Y<<","
2903 <<block->getPos().Z<<")"
2912 Only print if something happened or saved whole map
2914 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2915 || block_count != 0)
2917 infostream<<"ServerMap: Written: "
2918 <<sector_meta_count<<" sector metadata files, "
2919 <<block_count<<" block files"
2920 <<", "<<block_count_all<<" blocks in memory."
2922 PrintInfo(infostream); // ServerMap/ClientMap:
2923 infostream<<"Blocks modified by: "<<std::endl;
2924 modprofiler.print(infostream);
2928 static s32 unsignedToSigned(s32 i, s32 max_positive)
2930 if(i < max_positive)
2933 return i - 2*max_positive;
2936 // modulo of a negative number does not work consistently in C
2937 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2941 return mod - ((-i) % mod);
2944 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2946 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2948 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2950 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2951 return v3s16(x,y,z);
2954 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2956 if(loadFromFolders()){
2957 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2958 <<"all blocks that are stored in flat files"<<std::endl;
2964 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2966 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2967 v3s16 p = getIntegerAsBlock(block_i);
2968 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2974 void ServerMap::saveMapMeta()
2976 DSTACK(__FUNCTION_NAME);
2978 infostream<<"ServerMap::saveMapMeta(): "
2982 createDirs(m_savedir);
2984 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2985 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2986 if(os.good() == false)
2988 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2989 <<"could not open"<<fullpath<<std::endl;
2990 throw FileNotGoodException("Cannot open chunk metadata");
2994 params.setU64("seed", m_seed);
2996 params.writeLines(os);
2998 os<<"[end_of_params]\n";
3000 m_map_metadata_changed = false;
3003 void ServerMap::loadMapMeta()
3005 DSTACK(__FUNCTION_NAME);
3007 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3010 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3011 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3012 if(is.good() == false)
3014 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3015 <<"could not open"<<fullpath<<std::endl;
3016 throw FileNotGoodException("Cannot open map metadata");
3024 throw SerializationError
3025 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3027 std::getline(is, line);
3028 std::string trimmedline = trim(line);
3029 if(trimmedline == "[end_of_params]")
3031 params.parseConfigLine(line);
3034 m_seed = params.getU64("seed");
3036 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3039 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3041 DSTACK(__FUNCTION_NAME);
3042 // Format used for writing
3043 u8 version = SER_FMT_VER_HIGHEST;
3045 v2s16 pos = sector->getPos();
3046 std::string dir = getSectorDir(pos);
3049 std::string fullpath = dir + DIR_DELIM + "meta";
3050 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3051 if(o.good() == false)
3052 throw FileNotGoodException("Cannot open sector metafile");
3054 sector->serialize(o, version);
3056 sector->differs_from_disk = false;
3059 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3061 DSTACK(__FUNCTION_NAME);
3063 v2s16 p2d = getSectorPos(sectordir);
3065 ServerMapSector *sector = NULL;
3067 std::string fullpath = sectordir + DIR_DELIM + "meta";
3068 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3069 if(is.good() == false)
3071 // If the directory exists anyway, it probably is in some old
3072 // format. Just go ahead and create the sector.
3073 if(fs::PathExists(sectordir))
3075 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3076 <<fullpath<<" doesn't exist but directory does."
3077 <<" Continuing with a sector with no metadata."
3079 sector = new ServerMapSector(this, p2d, m_gamedef);
3080 m_sectors.insert(p2d, sector);
3084 throw FileNotGoodException("Cannot open sector metafile");
3089 sector = ServerMapSector::deSerialize
3090 (is, this, p2d, m_sectors, m_gamedef);
3092 saveSectorMeta(sector);
3095 sector->differs_from_disk = false;
3100 bool ServerMap::loadSectorMeta(v2s16 p2d)
3102 DSTACK(__FUNCTION_NAME);
3104 MapSector *sector = NULL;
3106 // The directory layout we're going to load from.
3107 // 1 - original sectors/xxxxzzzz/
3108 // 2 - new sectors2/xxx/zzz/
3109 // If we load from anything but the latest structure, we will
3110 // immediately save to the new one, and remove the old.
3112 std::string sectordir1 = getSectorDir(p2d, 1);
3113 std::string sectordir;
3114 if(fs::PathExists(sectordir1))
3116 sectordir = sectordir1;
3121 sectordir = getSectorDir(p2d, 2);
3125 sector = loadSectorMeta(sectordir, loadlayout != 2);
3127 catch(InvalidFilenameException &e)
3131 catch(FileNotGoodException &e)
3135 catch(std::exception &e)
3144 bool ServerMap::loadSectorFull(v2s16 p2d)
3146 DSTACK(__FUNCTION_NAME);
3148 MapSector *sector = NULL;
3150 // The directory layout we're going to load from.
3151 // 1 - original sectors/xxxxzzzz/
3152 // 2 - new sectors2/xxx/zzz/
3153 // If we load from anything but the latest structure, we will
3154 // immediately save to the new one, and remove the old.
3156 std::string sectordir1 = getSectorDir(p2d, 1);
3157 std::string sectordir;
3158 if(fs::PathExists(sectordir1))
3160 sectordir = sectordir1;
3165 sectordir = getSectorDir(p2d, 2);
3169 sector = loadSectorMeta(sectordir, loadlayout != 2);
3171 catch(InvalidFilenameException &e)
3175 catch(FileNotGoodException &e)
3179 catch(std::exception &e)
3187 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3189 std::vector<fs::DirListNode>::iterator i2;
3190 for(i2=list2.begin(); i2!=list2.end(); i2++)
3196 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3198 catch(InvalidFilenameException &e)
3200 // This catches unknown crap in directory
3206 infostream<<"Sector converted to new layout - deleting "<<
3207 sectordir1<<std::endl;
3208 fs::RecursiveDelete(sectordir1);
3215 void ServerMap::beginSave() {
3217 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3218 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3221 void ServerMap::endSave() {
3223 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3224 infostream<<"WARNING: endSave() failed, map might not have saved.";
3227 void ServerMap::saveBlock(MapBlock *block)
3229 DSTACK(__FUNCTION_NAME);
3231 Dummy blocks are not written
3233 if(block->isDummy())
3235 /*v3s16 p = block->getPos();
3236 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3237 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3241 // Format used for writing
3242 u8 version = SER_FMT_VER_HIGHEST;
3244 v3s16 p3d = block->getPos();
3248 v2s16 p2d(p3d.X, p3d.Z);
3249 std::string sectordir = getSectorDir(p2d);
3251 createDirs(sectordir);
3253 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3254 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3255 if(o.good() == false)
3256 throw FileNotGoodException("Cannot open block data");
3259 [0] u8 serialization version
3265 std::ostringstream o(std::ios_base::binary);
3267 o.write((char*)&version, 1);
3270 block->serialize(o, version, true);
3272 // Write block to database
3274 std::string tmp = o.str();
3275 const char *bytes = tmp.c_str();
3277 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3278 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3279 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3280 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3281 int written = sqlite3_step(m_database_write);
3282 if(written != SQLITE_DONE)
3283 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3284 <<sqlite3_errmsg(m_database)<<std::endl;
3285 // Make ready for later reuse
3286 sqlite3_reset(m_database_write);
3288 // We just wrote it to the disk so clear modified flag
3289 block->resetModified();
3292 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3294 DSTACK(__FUNCTION_NAME);
3296 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3299 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3300 if(is.good() == false)
3301 throw FileNotGoodException("Cannot open block file");
3303 v3s16 p3d = getBlockPos(sectordir, blockfile);
3304 v2s16 p2d(p3d.X, p3d.Z);
3306 assert(sector->getPos() == p2d);
3308 u8 version = SER_FMT_VER_INVALID;
3309 is.read((char*)&version, 1);
3312 throw SerializationError("ServerMap::loadBlock(): Failed"
3313 " to read MapBlock version");
3315 /*u32 block_size = MapBlock::serializedLength(version);
3316 SharedBuffer<u8> data(block_size);
3317 is.read((char*)*data, block_size);*/
3319 // This will always return a sector because we're the server
3320 //MapSector *sector = emergeSector(p2d);
3322 MapBlock *block = NULL;
3323 bool created_new = false;
3324 block = sector->getBlockNoCreateNoEx(p3d.Y);
3327 block = sector->createBlankBlockNoInsert(p3d.Y);
3332 block->deSerialize(is, version, true);
3334 // If it's a new block, insert it to the map
3336 sector->insertBlock(block);
3339 Save blocks loaded in old format in new format
3342 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3346 // Should be in database now, so delete the old file
3347 fs::RecursiveDelete(fullpath);
3350 // We just loaded it from the disk, so it's up-to-date.
3351 block->resetModified();
3354 catch(SerializationError &e)
3356 infostream<<"WARNING: Invalid block data on disk "
3357 <<"fullpath="<<fullpath
3358 <<" (SerializationError). "
3359 <<"what()="<<e.what()
3361 //" Ignoring. A new one will be generated.
3364 // TODO: Backup file; name is in fullpath.
3368 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3370 DSTACK(__FUNCTION_NAME);
3373 std::istringstream is(*blob, std::ios_base::binary);
3375 u8 version = SER_FMT_VER_INVALID;
3376 is.read((char*)&version, 1);
3379 throw SerializationError("ServerMap::loadBlock(): Failed"
3380 " to read MapBlock version");
3382 /*u32 block_size = MapBlock::serializedLength(version);
3383 SharedBuffer<u8> data(block_size);
3384 is.read((char*)*data, block_size);*/
3386 // This will always return a sector because we're the server
3387 //MapSector *sector = emergeSector(p2d);
3389 MapBlock *block = NULL;
3390 bool created_new = false;
3391 block = sector->getBlockNoCreateNoEx(p3d.Y);
3394 block = sector->createBlankBlockNoInsert(p3d.Y);
3399 block->deSerialize(is, version, true);
3401 // If it's a new block, insert it to the map
3403 sector->insertBlock(block);
3406 Save blocks loaded in old format in new format
3409 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3410 // Only save if asked to; no need to update version
3414 // We just loaded it from, so it's up-to-date.
3415 block->resetModified();
3418 catch(SerializationError &e)
3420 infostream<<"WARNING: Invalid block data in database "
3421 <<" (SerializationError). "
3422 <<"what()="<<e.what()
3424 //" Ignoring. A new one will be generated.
3427 // TODO: Copy to a backup database.
3431 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3433 DSTACK(__FUNCTION_NAME);
3435 v2s16 p2d(blockpos.X, blockpos.Z);
3437 if(!loadFromFolders()) {
3440 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3441 infostream<<"WARNING: Could not bind block position for load: "
3442 <<sqlite3_errmsg(m_database)<<std::endl;
3443 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3445 Make sure sector is loaded
3447 MapSector *sector = createSector(p2d);
3452 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3453 size_t len = sqlite3_column_bytes(m_database_read, 0);
3455 std::string datastr(data, len);
3457 loadBlock(&datastr, blockpos, sector, false);
3459 sqlite3_step(m_database_read);
3460 // We should never get more than 1 row, so ok to reset
3461 sqlite3_reset(m_database_read);
3463 return getBlockNoCreateNoEx(blockpos);
3465 sqlite3_reset(m_database_read);
3467 // Not found in database, try the files
3470 // The directory layout we're going to load from.
3471 // 1 - original sectors/xxxxzzzz/
3472 // 2 - new sectors2/xxx/zzz/
3473 // If we load from anything but the latest structure, we will
3474 // immediately save to the new one, and remove the old.
3476 std::string sectordir1 = getSectorDir(p2d, 1);
3477 std::string sectordir;
3478 if(fs::PathExists(sectordir1))
3480 sectordir = sectordir1;
3485 sectordir = getSectorDir(p2d, 2);
3489 Make sure sector is loaded
3491 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3495 sector = loadSectorMeta(sectordir, loadlayout != 2);
3497 catch(InvalidFilenameException &e)
3501 catch(FileNotGoodException &e)
3505 catch(std::exception &e)
3512 Make sure file exists
3515 std::string blockfilename = getBlockFilename(blockpos);
3516 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3520 Load block and save it to the database
3522 loadBlock(sectordir, blockfilename, sector, true);
3523 return getBlockNoCreateNoEx(blockpos);
3526 void ServerMap::PrintInfo(std::ostream &out)
3537 ClientMap::ClientMap(
3540 MapDrawControl &control,
3541 scene::ISceneNode* parent,
3542 scene::ISceneManager* mgr,
3545 Map(dout_client, gamedef),
3546 scene::ISceneNode(parent, mgr, id),
3549 m_camera_position(0,0,0),
3550 m_camera_direction(0,0,1),
3553 m_camera_mutex.Init();
3554 assert(m_camera_mutex.IsInitialized());
3556 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3557 BS*1000000,BS*1000000,BS*1000000);
3560 ClientMap::~ClientMap()
3562 /*JMutexAutoLock lock(mesh_mutex);
3571 MapSector * ClientMap::emergeSector(v2s16 p2d)
3573 DSTACK(__FUNCTION_NAME);
3574 // Check that it doesn't exist already
3576 return getSectorNoGenerate(p2d);
3578 catch(InvalidPositionException &e)
3583 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3586 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3587 m_sectors.insert(p2d, sector);
3594 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3596 DSTACK(__FUNCTION_NAME);
3597 ClientMapSector *sector = NULL;
3599 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3601 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3605 sector = (ClientMapSector*)n->getValue();
3606 assert(sector->getId() == MAPSECTOR_CLIENT);
3610 sector = new ClientMapSector(this, p2d);
3612 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3613 m_sectors.insert(p2d, sector);
3617 sector->deSerialize(is);
3621 void ClientMap::OnRegisterSceneNode()
3625 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3626 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3629 ISceneNode::OnRegisterSceneNode();
3632 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3633 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3635 float d0 = (float)BS * p0.getDistanceFrom(p1);
3637 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3639 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3641 for(float s=start_off; s<d0+end_off; s+=step){
3642 v3f pf = p0f + uf * s;
3643 v3s16 p = floatToInt(pf, BS);
3644 MapNode n = map->getNodeNoEx(p);
3645 bool is_transparent = false;
3646 const ContentFeatures &f = nodemgr->get(n);
3647 if(f.solidness == 0)
3648 is_transparent = (f.visual_solidness != 2);
3650 is_transparent = (f.solidness != 2);
3651 if(!is_transparent){
3653 if(count >= needed_count)
3661 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3663 INodeDefManager *nodemgr = m_gamedef->ndef();
3665 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3666 DSTACK(__FUNCTION_NAME);
3668 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3671 if(pass == scene::ESNRP_SOLID)
3672 prefix = "CM: solid: ";
3674 prefix = "CM: transparent: ";
3677 This is called two times per frame, reset on the non-transparent one
3679 if(pass == scene::ESNRP_SOLID)
3681 m_last_drawn_sectors.clear();
3685 Get time for measuring timeout.
3687 Measuring time is very useful for long delays when the
3688 machine is swapping a lot.
3690 int time1 = time(0);
3692 //u32 daynight_ratio = m_client->getDayNightRatio();
3694 m_camera_mutex.Lock();
3695 v3f camera_position = m_camera_position;
3696 v3f camera_direction = m_camera_direction;
3697 f32 camera_fov = m_camera_fov;
3698 m_camera_mutex.Unlock();
3701 Get all blocks and draw all visible ones
3704 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3706 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3708 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3709 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3711 // Take a fair amount as we will be dropping more out later
3712 // Umm... these additions are a bit strange but they are needed.
3714 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3715 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3716 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3718 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3719 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3720 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3722 u32 vertex_count = 0;
3723 u32 meshbuffer_count = 0;
3725 // For limiting number of mesh updates per frame
3726 u32 mesh_update_count = 0;
3728 // Number of blocks in rendering range
3729 u32 blocks_in_range = 0;
3730 // Number of blocks occlusion culled
3731 u32 blocks_occlusion_culled = 0;
3732 // Number of blocks in rendering range but don't have a mesh
3733 u32 blocks_in_range_without_mesh = 0;
3734 // Blocks that had mesh that would have been drawn according to
3735 // rendering range (if max blocks limit didn't kick in)
3736 u32 blocks_would_have_drawn = 0;
3737 // Blocks that were drawn and had a mesh
3738 u32 blocks_drawn = 0;
3739 // Blocks which had a corresponding meshbuffer for this pass
3740 u32 blocks_had_pass_meshbuf = 0;
3741 // Blocks from which stuff was actually drawn
3742 u32 blocks_without_stuff = 0;
3745 Collect a set of blocks for drawing
3748 core::map<v3s16, MapBlock*> drawset;
3751 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3753 for(core::map<v2s16, MapSector*>::Iterator
3754 si = m_sectors.getIterator();
3755 si.atEnd() == false; si++)
3757 MapSector *sector = si.getNode()->getValue();
3758 v2s16 sp = sector->getPos();
3760 if(m_control.range_all == false)
3762 if(sp.X < p_blocks_min.X
3763 || sp.X > p_blocks_max.X
3764 || sp.Y < p_blocks_min.Z
3765 || sp.Y > p_blocks_max.Z)
3769 core::list< MapBlock * > sectorblocks;
3770 sector->getBlocks(sectorblocks);
3773 Loop through blocks in sector
3776 u32 sector_blocks_drawn = 0;
3778 core::list< MapBlock * >::Iterator i;
3779 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3781 MapBlock *block = *i;
3784 Compare block position to camera position, skip
3785 if not seen on display
3788 float range = 100000 * BS;
3789 if(m_control.range_all == false)
3790 range = m_control.wanted_range * BS;
3793 if(isBlockInSight(block->getPos(), camera_position,
3794 camera_direction, camera_fov,
3795 range, &d) == false)
3800 // This is ugly (spherical distance limit?)
3801 /*if(m_control.range_all == false &&
3802 d - 0.5*BS*MAP_BLOCKSIZE > range)
3809 Update expired mesh (used for day/night change)
3811 It doesn't work exactly like it should now with the
3812 tasked mesh update but whatever.
3815 bool mesh_expired = false;
3818 JMutexAutoLock lock(block->mesh_mutex);
3820 mesh_expired = block->getMeshExpired();
3822 // Mesh has not been expired and there is no mesh:
3823 // block has no content
3824 if(block->mesh == NULL && mesh_expired == false){
3825 blocks_in_range_without_mesh++;
3830 f32 faraway = BS*50;
3831 //f32 faraway = m_control.wanted_range * BS;
3834 This has to be done with the mesh_mutex unlocked
3836 // Pretty random but this should work somewhat nicely
3837 if(mesh_expired && (
3838 (mesh_update_count < 3
3839 && (d < faraway || mesh_update_count < 2)
3842 (m_control.range_all && mesh_update_count < 20)
3845 /*if(mesh_expired && mesh_update_count < 6
3846 && (d < faraway || mesh_update_count < 3))*/
3848 mesh_update_count++;
3850 // Mesh has been expired: generate new mesh
3851 //block->updateMesh(daynight_ratio);
3852 m_client->addUpdateMeshTask(block->getPos());
3854 mesh_expired = false;
3862 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3863 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3865 float stepfac = 1.1;
3866 float startoff = BS*1;
3867 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3868 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3869 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3870 u32 needed_count = 1;
3872 isOccluded(this, spn, cpn + v3s16(0,0,0),
3873 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3874 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3875 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3876 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3877 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3878 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3879 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3880 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3881 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3882 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3883 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3884 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3885 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3886 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3887 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3888 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3889 step, stepfac, startoff, endoff, needed_count, nodemgr)
3892 blocks_occlusion_culled++;
3896 // This block is in range. Reset usage timer.
3897 block->resetUsageTimer();
3900 Ignore if mesh doesn't exist
3903 JMutexAutoLock lock(block->mesh_mutex);
3905 scene::SMesh *mesh = block->mesh;
3908 blocks_in_range_without_mesh++;
3913 // Limit block count in case of a sudden increase
3914 blocks_would_have_drawn++;
3915 if(blocks_drawn >= m_control.wanted_max_blocks
3916 && m_control.range_all == false
3917 && d > m_control.wanted_min_range * BS)
3921 drawset[block->getPos()] = block;
3923 sector_blocks_drawn++;
3926 } // foreach sectorblocks
3928 if(sector_blocks_drawn != 0)
3929 m_last_drawn_sectors[sp] = true;
3934 Draw the selected MapBlocks
3938 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3940 int timecheck_counter = 0;
3941 for(core::map<v3s16, MapBlock*>::Iterator
3942 i = drawset.getIterator();
3943 i.atEnd() == false; i++)
3946 timecheck_counter++;
3947 if(timecheck_counter > 50)
3949 timecheck_counter = 0;
3950 int time2 = time(0);
3951 if(time2 > time1 + 4)
3953 infostream<<"ClientMap::renderMap(): "
3954 "Rendering takes ages, returning."
3961 MapBlock *block = i.getNode()->getValue();
3964 Draw the faces of the block
3967 JMutexAutoLock lock(block->mesh_mutex);
3969 scene::SMesh *mesh = block->mesh;
3972 u32 c = mesh->getMeshBufferCount();
3973 bool stuff_actually_drawn = false;
3974 for(u32 i=0; i<c; i++)
3976 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3977 const video::SMaterial& material = buf->getMaterial();
3978 video::IMaterialRenderer* rnd =
3979 driver->getMaterialRenderer(material.MaterialType);
3980 bool transparent = (rnd && rnd->isTransparent());
3981 // Render transparent on transparent pass and likewise.
3982 if(transparent == is_transparent_pass)
3984 if(buf->getVertexCount() == 0)
3985 errorstream<<"Block ["<<analyze_block(block)
3986 <<"] contains an empty meshbuf"<<std::endl;
3988 This *shouldn't* hurt too much because Irrlicht
3989 doesn't change opengl textures if the old
3990 material has the same texture.
3992 driver->setMaterial(buf->getMaterial());
3993 driver->drawMeshBuffer(buf);
3994 vertex_count += buf->getVertexCount();
3996 stuff_actually_drawn = true;
3999 if(stuff_actually_drawn)
4000 blocks_had_pass_meshbuf++;
4002 blocks_without_stuff++;
4007 // Log only on solid pass because values are the same
4008 if(pass == scene::ESNRP_SOLID){
4009 g_profiler->avg("CM: blocks in range", blocks_in_range);
4010 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
4011 if(blocks_in_range != 0)
4012 g_profiler->avg("CM: blocks in range without mesh (frac)",
4013 (float)blocks_in_range_without_mesh/blocks_in_range);
4014 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4017 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4018 if(blocks_had_pass_meshbuf != 0)
4019 g_profiler->avg(prefix+"meshbuffers per block",
4020 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4021 if(blocks_drawn != 0)
4022 g_profiler->avg(prefix+"empty blocks (frac)",
4023 (float)blocks_without_stuff / blocks_drawn);
4025 m_control.blocks_drawn = blocks_drawn;
4026 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4028 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4029 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4032 void ClientMap::renderPostFx()
4034 INodeDefManager *nodemgr = m_gamedef->ndef();
4036 // Sadly ISceneManager has no "post effects" render pass, in that case we
4037 // could just register for that and handle it in renderMap().
4039 m_camera_mutex.Lock();
4040 v3f camera_position = m_camera_position;
4041 m_camera_mutex.Unlock();
4043 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4045 // - If the player is in a solid node, make everything black.
4046 // - If the player is in liquid, draw a semi-transparent overlay.
4047 const ContentFeatures& features = nodemgr->get(n);
4048 video::SColor post_effect_color = features.post_effect_color;
4049 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4051 post_effect_color = video::SColor(255, 0, 0, 0);
4053 if (post_effect_color.getAlpha() != 0)
4055 // Draw a full-screen rectangle
4056 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4057 v2u32 ss = driver->getScreenSize();
4058 core::rect<s32> rect(0,0, ss.X, ss.Y);
4059 driver->draw2DRectangle(post_effect_color, rect);
4063 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4064 core::map<v3s16, MapBlock*> *affected_blocks)
4066 bool changed = false;
4068 Add it to all blocks touching it
4071 v3s16(0,0,0), // this
4072 v3s16(0,0,1), // back
4073 v3s16(0,1,0), // top
4074 v3s16(1,0,0), // right
4075 v3s16(0,0,-1), // front
4076 v3s16(0,-1,0), // bottom
4077 v3s16(-1,0,0), // left
4079 for(u16 i=0; i<7; i++)
4081 v3s16 p2 = p + dirs[i];
4082 // Block position of neighbor (or requested) node
4083 v3s16 blockpos = getNodeBlockPos(p2);
4084 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4085 if(blockref == NULL)
4087 // Relative position of requested node
4088 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4089 if(blockref->setTempMod(relpos, mod))
4094 if(changed && affected_blocks!=NULL)
4096 for(u16 i=0; i<7; i++)
4098 v3s16 p2 = p + dirs[i];
4099 // Block position of neighbor (or requested) node
4100 v3s16 blockpos = getNodeBlockPos(p2);
4101 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4102 if(blockref == NULL)
4104 affected_blocks->insert(blockpos, blockref);
4110 bool ClientMap::clearTempMod(v3s16 p,
4111 core::map<v3s16, MapBlock*> *affected_blocks)
4113 bool changed = false;
4115 v3s16(0,0,0), // this
4116 v3s16(0,0,1), // back
4117 v3s16(0,1,0), // top
4118 v3s16(1,0,0), // right
4119 v3s16(0,0,-1), // front
4120 v3s16(0,-1,0), // bottom
4121 v3s16(-1,0,0), // left
4123 for(u16 i=0; i<7; i++)
4125 v3s16 p2 = p + dirs[i];
4126 // Block position of neighbor (or requested) node
4127 v3s16 blockpos = getNodeBlockPos(p2);
4128 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4129 if(blockref == NULL)
4131 // Relative position of requested node
4132 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4133 if(blockref->clearTempMod(relpos))
4138 if(changed && affected_blocks!=NULL)
4140 for(u16 i=0; i<7; i++)
4142 v3s16 p2 = p + dirs[i];
4143 // Block position of neighbor (or requested) node
4144 v3s16 blockpos = getNodeBlockPos(p2);
4145 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4146 if(blockref == NULL)
4148 affected_blocks->insert(blockpos, blockref);
4154 void ClientMap::expireMeshes(bool only_daynight_diffed)
4156 TimeTaker timer("expireMeshes()");
4158 core::map<v2s16, MapSector*>::Iterator si;
4159 si = m_sectors.getIterator();
4160 for(; si.atEnd() == false; si++)
4162 MapSector *sector = si.getNode()->getValue();
4164 core::list< MapBlock * > sectorblocks;
4165 sector->getBlocks(sectorblocks);
4167 core::list< MapBlock * >::Iterator i;
4168 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4170 MapBlock *block = *i;
4172 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4178 JMutexAutoLock lock(block->mesh_mutex);
4179 if(block->mesh != NULL)
4181 /*block->mesh->drop();
4182 block->mesh = NULL;*/
4183 block->setMeshExpired(true);
4190 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4192 assert(mapType() == MAPTYPE_CLIENT);
4195 v3s16 p = blockpos + v3s16(0,0,0);
4196 MapBlock *b = getBlockNoCreate(p);
4197 b->updateMesh(daynight_ratio);
4198 //b->setMeshExpired(true);
4200 catch(InvalidPositionException &e){}
4203 v3s16 p = blockpos + v3s16(-1,0,0);
4204 MapBlock *b = getBlockNoCreate(p);
4205 b->updateMesh(daynight_ratio);
4206 //b->setMeshExpired(true);
4208 catch(InvalidPositionException &e){}
4210 v3s16 p = blockpos + v3s16(0,-1,0);
4211 MapBlock *b = getBlockNoCreate(p);
4212 b->updateMesh(daynight_ratio);
4213 //b->setMeshExpired(true);
4215 catch(InvalidPositionException &e){}
4217 v3s16 p = blockpos + v3s16(0,0,-1);
4218 MapBlock *b = getBlockNoCreate(p);
4219 b->updateMesh(daynight_ratio);
4220 //b->setMeshExpired(true);
4222 catch(InvalidPositionException &e){}
4227 Update mesh of block in which the node is, and if the node is at the
4228 leading edge, update the appropriate leading blocks too.
4230 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4238 v3s16 blockposes[4];
4239 for(u32 i=0; i<4; i++)
4241 v3s16 np = nodepos + dirs[i];
4242 blockposes[i] = getNodeBlockPos(np);
4243 // Don't update mesh of block if it has been done already
4244 bool already_updated = false;
4245 for(u32 j=0; j<i; j++)
4247 if(blockposes[j] == blockposes[i])
4249 already_updated = true;
4256 MapBlock *b = getBlockNoCreate(blockposes[i]);
4257 b->updateMesh(daynight_ratio);
4262 void ClientMap::PrintInfo(std::ostream &out)
4273 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4278 MapVoxelManipulator::~MapVoxelManipulator()
4280 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4284 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4286 TimeTaker timer1("emerge", &emerge_time);
4288 // Units of these are MapBlocks
4289 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4290 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4292 VoxelArea block_area_nodes
4293 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4295 addArea(block_area_nodes);
4297 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4298 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4299 for(s32 x=p_min.X; x<=p_max.X; x++)
4302 core::map<v3s16, bool>::Node *n;
4303 n = m_loaded_blocks.find(p);
4307 bool block_data_inexistent = false;
4310 TimeTaker timer1("emerge load", &emerge_load_time);
4312 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4313 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4315 a.print(infostream);
4316 infostream<<std::endl;*/
4318 MapBlock *block = m_map->getBlockNoCreate(p);
4319 if(block->isDummy())
4320 block_data_inexistent = true;
4322 block->copyTo(*this);
4324 catch(InvalidPositionException &e)
4326 block_data_inexistent = true;
4329 if(block_data_inexistent)
4331 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4332 // Fill with VOXELFLAG_INEXISTENT
4333 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4334 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4336 s32 i = m_area.index(a.MinEdge.X,y,z);
4337 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4341 m_loaded_blocks.insert(p, !block_data_inexistent);
4344 //infostream<<"emerge done"<<std::endl;
4348 SUGG: Add an option to only update eg. water and air nodes.
4349 This will make it interfere less with important stuff if
4352 void MapVoxelManipulator::blitBack
4353 (core::map<v3s16, MapBlock*> & modified_blocks)
4355 if(m_area.getExtent() == v3s16(0,0,0))
4358 //TimeTaker timer1("blitBack");
4360 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4361 <<m_loaded_blocks.size()<<std::endl;*/
4364 Initialize block cache
4366 v3s16 blockpos_last;
4367 MapBlock *block = NULL;
4368 bool block_checked_in_modified = false;
4370 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4371 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4372 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4376 u8 f = m_flags[m_area.index(p)];
4377 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4380 MapNode &n = m_data[m_area.index(p)];
4382 v3s16 blockpos = getNodeBlockPos(p);
4387 if(block == NULL || blockpos != blockpos_last){
4388 block = m_map->getBlockNoCreate(blockpos);
4389 blockpos_last = blockpos;
4390 block_checked_in_modified = false;
4393 // Calculate relative position in block
4394 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4396 // Don't continue if nothing has changed here
4397 if(block->getNode(relpos) == n)
4400 //m_map->setNode(m_area.MinEdge + p, n);
4401 block->setNode(relpos, n);
4404 Make sure block is in modified_blocks
4406 if(block_checked_in_modified == false)
4408 modified_blocks[blockpos] = block;
4409 block_checked_in_modified = true;
4412 catch(InvalidPositionException &e)
4418 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4419 MapVoxelManipulator(map),
4420 m_create_area(false)
4424 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4428 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4430 // Just create the area so that it can be pointed to
4431 VoxelManipulator::emerge(a, caller_id);
4434 void ManualMapVoxelManipulator::initialEmerge(
4435 v3s16 blockpos_min, v3s16 blockpos_max)
4437 TimeTaker timer1("initialEmerge", &emerge_time);
4439 // Units of these are MapBlocks
4440 v3s16 p_min = blockpos_min;
4441 v3s16 p_max = blockpos_max;
4443 VoxelArea block_area_nodes
4444 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4446 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4449 infostream<<"initialEmerge: area: ";
4450 block_area_nodes.print(infostream);
4451 infostream<<" ("<<size_MB<<"MB)";
4452 infostream<<std::endl;
4455 addArea(block_area_nodes);
4457 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4458 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4459 for(s32 x=p_min.X; x<=p_max.X; x++)
4462 core::map<v3s16, bool>::Node *n;
4463 n = m_loaded_blocks.find(p);
4467 bool block_data_inexistent = false;
4470 TimeTaker timer1("emerge load", &emerge_load_time);
4472 MapBlock *block = m_map->getBlockNoCreate(p);
4473 if(block->isDummy())
4474 block_data_inexistent = true;
4476 block->copyTo(*this);
4478 catch(InvalidPositionException &e)
4480 block_data_inexistent = true;
4483 if(block_data_inexistent)
4486 Mark area inexistent
4488 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4489 // Fill with VOXELFLAG_INEXISTENT
4490 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4491 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4493 s32 i = m_area.index(a.MinEdge.X,y,z);
4494 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4498 m_loaded_blocks.insert(p, !block_data_inexistent);
4502 void ManualMapVoxelManipulator::blitBackAll(
4503 core::map<v3s16, MapBlock*> * modified_blocks)
4505 if(m_area.getExtent() == v3s16(0,0,0))
4509 Copy data of all blocks
4511 for(core::map<v3s16, bool>::Iterator
4512 i = m_loaded_blocks.getIterator();
4513 i.atEnd() == false; i++)
4515 v3s16 p = i.getNode()->getKey();
4516 bool existed = i.getNode()->getValue();
4517 if(existed == false)
4519 // The Great Bug was found using this
4520 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4521 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4525 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4528 infostream<<"WARNING: "<<__FUNCTION_NAME
4529 <<": got NULL block "
4530 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4535 block->copyFrom(*this);
4538 modified_blocks->insert(p, block);