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, std::string &player_name)
920 INodeDefManager *nodemgr = m_gamedef->ndef();
923 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
924 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
927 From this node to nodes underneath:
928 If lighting is sunlight (1.0), unlight neighbours and
933 v3s16 toppos = p + v3s16(0,1,0);
934 v3s16 bottompos = p + v3s16(0,-1,0);
936 bool node_under_sunlight = true;
937 core::map<v3s16, bool> light_sources;
940 If there is a node at top and it doesn't have sunlight,
941 there has not been any sunlight going down.
943 Otherwise there probably is.
946 MapNode topnode = getNode(toppos);
948 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
949 node_under_sunlight = false;
951 catch(InvalidPositionException &e)
956 Remove all light that has come out of this node
959 enum LightBank banks[] =
964 for(s32 i=0; i<2; i++)
966 enum LightBank bank = banks[i];
968 u8 lightwas = getNode(p).getLight(bank, nodemgr);
970 // Add the block of the added node to modified_blocks
971 v3s16 blockpos = getNodeBlockPos(p);
972 MapBlock * block = getBlockNoCreate(blockpos);
973 assert(block != NULL);
974 modified_blocks.insert(blockpos, block);
976 assert(isValidPosition(p));
978 // Unlight neighbours of node.
979 // This means setting light of all consequent dimmer nodes
981 // This also collects the nodes at the border which will spread
982 // light again into this.
983 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
985 n.setLight(bank, 0, nodemgr);
989 If node lets sunlight through and is under sunlight, it has
992 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
994 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
998 Set the node on the map
1007 std::string metadata_name = nodemgr->get(n).metadata_name;
1008 if(metadata_name != ""){
1009 NodeMetadata *meta = NodeMetadata::create(metadata_name, m_gamedef);
1011 errorstream<<"Failed to create node metadata \""
1012 <<metadata_name<<"\""<<std::endl;
1014 meta->setOwner(player_name);
1015 setNodeMetadata(p, meta);
1020 If node is under sunlight and doesn't let sunlight through,
1021 take all sunlighted nodes under it and clear light from them
1022 and from where the light has been spread.
1023 TODO: This could be optimized by mass-unlighting instead
1026 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1030 //m_dout<<DTIME<<"y="<<y<<std::endl;
1031 v3s16 n2pos(p.X, y, p.Z);
1035 n2 = getNode(n2pos);
1037 catch(InvalidPositionException &e)
1042 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1044 unLightNeighbors(LIGHTBANK_DAY,
1045 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1046 light_sources, modified_blocks);
1047 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1055 for(s32 i=0; i<2; i++)
1057 enum LightBank bank = banks[i];
1060 Spread light from all nodes that might be capable of doing so
1062 spreadLight(bank, light_sources, modified_blocks);
1066 Update information about whether day and night light differ
1068 for(core::map<v3s16, MapBlock*>::Iterator
1069 i = modified_blocks.getIterator();
1070 i.atEnd() == false; i++)
1072 MapBlock *block = i.getNode()->getValue();
1073 block->updateDayNightDiff();
1077 Add neighboring liquid nodes and the node itself if it is
1078 liquid (=water node was added) to transform queue.
1081 v3s16(0,0,0), // self
1082 v3s16(0,0,1), // back
1083 v3s16(0,1,0), // top
1084 v3s16(1,0,0), // right
1085 v3s16(0,0,-1), // front
1086 v3s16(0,-1,0), // bottom
1087 v3s16(-1,0,0), // left
1089 for(u16 i=0; i<7; i++)
1094 v3s16 p2 = p + dirs[i];
1096 MapNode n2 = getNode(p2);
1097 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1099 m_transforming_liquid.push_back(p2);
1102 }catch(InvalidPositionException &e)
1110 void Map::removeNodeAndUpdate(v3s16 p,
1111 core::map<v3s16, MapBlock*> &modified_blocks)
1113 INodeDefManager *nodemgr = m_gamedef->ndef();
1115 /*PrintInfo(m_dout);
1116 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1117 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1119 bool node_under_sunlight = true;
1121 v3s16 toppos = p + v3s16(0,1,0);
1123 // Node will be replaced with this
1124 content_t replace_material = CONTENT_AIR;
1127 If there is a node at top and it doesn't have sunlight,
1128 there will be no sunlight going down.
1131 MapNode topnode = getNode(toppos);
1133 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1134 node_under_sunlight = false;
1136 catch(InvalidPositionException &e)
1140 core::map<v3s16, bool> light_sources;
1142 enum LightBank banks[] =
1147 for(s32 i=0; i<2; i++)
1149 enum LightBank bank = banks[i];
1152 Unlight neighbors (in case the node is a light source)
1154 unLightNeighbors(bank, p,
1155 getNode(p).getLight(bank, nodemgr),
1156 light_sources, modified_blocks);
1160 Remove node metadata
1163 removeNodeMetadata(p);
1167 This also clears the lighting.
1171 n.setContent(replace_material);
1174 for(s32 i=0; i<2; i++)
1176 enum LightBank bank = banks[i];
1179 Recalculate lighting
1181 spreadLight(bank, light_sources, modified_blocks);
1184 // Add the block of the removed node to modified_blocks
1185 v3s16 blockpos = getNodeBlockPos(p);
1186 MapBlock * block = getBlockNoCreate(blockpos);
1187 assert(block != NULL);
1188 modified_blocks.insert(blockpos, block);
1191 If the removed node was under sunlight, propagate the
1192 sunlight down from it and then light all neighbors
1193 of the propagated blocks.
1195 if(node_under_sunlight)
1197 s16 ybottom = propagateSunlight(p, modified_blocks);
1198 /*m_dout<<DTIME<<"Node was under sunlight. "
1199 "Propagating sunlight";
1200 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1202 for(; y >= ybottom; y--)
1204 v3s16 p2(p.X, y, p.Z);
1205 /*m_dout<<DTIME<<"lighting neighbors of node ("
1206 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1208 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1213 // Set the lighting of this node to 0
1214 // TODO: Is this needed? Lighting is cleared up there already.
1216 MapNode n = getNode(p);
1217 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1220 catch(InvalidPositionException &e)
1226 for(s32 i=0; i<2; i++)
1228 enum LightBank bank = banks[i];
1230 // Get the brightest neighbour node and propagate light from it
1231 v3s16 n2p = getBrightestNeighbour(bank, p);
1233 MapNode n2 = getNode(n2p);
1234 lightNeighbors(bank, n2p, modified_blocks);
1236 catch(InvalidPositionException &e)
1242 Update information about whether day and night light differ
1244 for(core::map<v3s16, MapBlock*>::Iterator
1245 i = modified_blocks.getIterator();
1246 i.atEnd() == false; i++)
1248 MapBlock *block = i.getNode()->getValue();
1249 block->updateDayNightDiff();
1253 Add neighboring liquid nodes and this node to transform queue.
1254 (it's vital for the node itself to get updated last.)
1257 v3s16(0,0,1), // back
1258 v3s16(0,1,0), // top
1259 v3s16(1,0,0), // right
1260 v3s16(0,0,-1), // front
1261 v3s16(0,-1,0), // bottom
1262 v3s16(-1,0,0), // left
1263 v3s16(0,0,0), // self
1265 for(u16 i=0; i<7; i++)
1270 v3s16 p2 = p + dirs[i];
1272 MapNode n2 = getNode(p2);
1273 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1275 m_transforming_liquid.push_back(p2);
1278 }catch(InvalidPositionException &e)
1284 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1287 event.type = MEET_ADDNODE;
1291 bool succeeded = true;
1293 core::map<v3s16, MapBlock*> modified_blocks;
1294 std::string st = std::string("");
1295 addNodeAndUpdate(p, n, modified_blocks, st);
1297 // Copy modified_blocks to event
1298 for(core::map<v3s16, MapBlock*>::Iterator
1299 i = modified_blocks.getIterator();
1300 i.atEnd()==false; i++)
1302 event.modified_blocks.insert(i.getNode()->getKey(), false);
1305 catch(InvalidPositionException &e){
1309 dispatchEvent(&event);
1314 bool Map::removeNodeWithEvent(v3s16 p)
1317 event.type = MEET_REMOVENODE;
1320 bool succeeded = true;
1322 core::map<v3s16, MapBlock*> modified_blocks;
1323 removeNodeAndUpdate(p, modified_blocks);
1325 // Copy modified_blocks to event
1326 for(core::map<v3s16, MapBlock*>::Iterator
1327 i = modified_blocks.getIterator();
1328 i.atEnd()==false; i++)
1330 event.modified_blocks.insert(i.getNode()->getKey(), false);
1333 catch(InvalidPositionException &e){
1337 dispatchEvent(&event);
1342 bool Map::dayNightDiffed(v3s16 blockpos)
1345 v3s16 p = blockpos + v3s16(0,0,0);
1346 MapBlock *b = getBlockNoCreate(p);
1347 if(b->dayNightDiffed())
1350 catch(InvalidPositionException &e){}
1353 v3s16 p = blockpos + v3s16(-1,0,0);
1354 MapBlock *b = getBlockNoCreate(p);
1355 if(b->dayNightDiffed())
1358 catch(InvalidPositionException &e){}
1360 v3s16 p = blockpos + v3s16(0,-1,0);
1361 MapBlock *b = getBlockNoCreate(p);
1362 if(b->dayNightDiffed())
1365 catch(InvalidPositionException &e){}
1367 v3s16 p = blockpos + v3s16(0,0,-1);
1368 MapBlock *b = getBlockNoCreate(p);
1369 if(b->dayNightDiffed())
1372 catch(InvalidPositionException &e){}
1375 v3s16 p = blockpos + v3s16(1,0,0);
1376 MapBlock *b = getBlockNoCreate(p);
1377 if(b->dayNightDiffed())
1380 catch(InvalidPositionException &e){}
1382 v3s16 p = blockpos + v3s16(0,1,0);
1383 MapBlock *b = getBlockNoCreate(p);
1384 if(b->dayNightDiffed())
1387 catch(InvalidPositionException &e){}
1389 v3s16 p = blockpos + v3s16(0,0,1);
1390 MapBlock *b = getBlockNoCreate(p);
1391 if(b->dayNightDiffed())
1394 catch(InvalidPositionException &e){}
1400 Updates usage timers
1402 void Map::timerUpdate(float dtime, float unload_timeout,
1403 core::list<v3s16> *unloaded_blocks)
1405 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1407 // Profile modified reasons
1408 Profiler modprofiler;
1410 core::list<v2s16> sector_deletion_queue;
1411 u32 deleted_blocks_count = 0;
1412 u32 saved_blocks_count = 0;
1413 u32 block_count_all = 0;
1415 core::map<v2s16, MapSector*>::Iterator si;
1418 si = m_sectors.getIterator();
1419 for(; si.atEnd() == false; si++)
1421 MapSector *sector = si.getNode()->getValue();
1423 bool all_blocks_deleted = true;
1425 core::list<MapBlock*> blocks;
1426 sector->getBlocks(blocks);
1428 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1429 i != blocks.end(); i++)
1431 MapBlock *block = (*i);
1433 block->incrementUsageTimer(dtime);
1435 if(block->getUsageTimer() > unload_timeout)
1437 v3s16 p = block->getPos();
1440 if(block->getModified() != MOD_STATE_CLEAN
1441 && save_before_unloading)
1443 modprofiler.add(block->getModifiedReason(), 1);
1445 saved_blocks_count++;
1448 // Delete from memory
1449 sector->deleteBlock(block);
1452 unloaded_blocks->push_back(p);
1454 deleted_blocks_count++;
1458 all_blocks_deleted = false;
1463 if(all_blocks_deleted)
1465 sector_deletion_queue.push_back(si.getNode()->getKey());
1470 // Finally delete the empty sectors
1471 deleteSectors(sector_deletion_queue);
1473 if(deleted_blocks_count != 0)
1475 PrintInfo(infostream); // ServerMap/ClientMap:
1476 infostream<<"Unloaded "<<deleted_blocks_count
1477 <<" blocks from memory";
1478 if(save_before_unloading)
1479 infostream<<", of which "<<saved_blocks_count<<" were written";
1480 infostream<<", "<<block_count_all<<" blocks in memory";
1481 infostream<<"."<<std::endl;
1482 if(saved_blocks_count != 0){
1483 PrintInfo(infostream); // ServerMap/ClientMap:
1484 infostream<<"Blocks modified by: "<<std::endl;
1485 modprofiler.print(infostream);
1490 void Map::deleteSectors(core::list<v2s16> &list)
1492 core::list<v2s16>::Iterator j;
1493 for(j=list.begin(); j!=list.end(); j++)
1495 MapSector *sector = m_sectors[*j];
1496 // If sector is in sector cache, remove it from there
1497 if(m_sector_cache == sector)
1498 m_sector_cache = NULL;
1499 // Remove from map and delete
1500 m_sectors.remove(*j);
1506 void Map::unloadUnusedData(float timeout,
1507 core::list<v3s16> *deleted_blocks)
1509 core::list<v2s16> sector_deletion_queue;
1510 u32 deleted_blocks_count = 0;
1511 u32 saved_blocks_count = 0;
1513 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1514 for(; si.atEnd() == false; si++)
1516 MapSector *sector = si.getNode()->getValue();
1518 bool all_blocks_deleted = true;
1520 core::list<MapBlock*> blocks;
1521 sector->getBlocks(blocks);
1522 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1523 i != blocks.end(); i++)
1525 MapBlock *block = (*i);
1527 if(block->getUsageTimer() > timeout)
1530 if(block->getModified() != MOD_STATE_CLEAN)
1533 saved_blocks_count++;
1535 // Delete from memory
1536 sector->deleteBlock(block);
1537 deleted_blocks_count++;
1541 all_blocks_deleted = false;
1545 if(all_blocks_deleted)
1547 sector_deletion_queue.push_back(si.getNode()->getKey());
1551 deleteSectors(sector_deletion_queue);
1553 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1554 <<", of which "<<saved_blocks_count<<" were wr."
1557 //return sector_deletion_queue.getSize();
1558 //return deleted_blocks_count;
1562 void Map::PrintInfo(std::ostream &out)
1567 #define WATER_DROP_BOOST 4
1571 NEIGHBOR_SAME_LEVEL,
1574 struct NodeNeighbor {
1580 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1582 INodeDefManager *nodemgr = m_gamedef->ndef();
1584 DSTACK(__FUNCTION_NAME);
1585 //TimeTaker timer("transformLiquids()");
1588 u32 initial_size = m_transforming_liquid.size();
1590 /*if(initial_size != 0)
1591 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1593 // list of nodes that due to viscosity have not reached their max level height
1594 UniqueQueue<v3s16> must_reflow;
1596 // List of MapBlocks that will require a lighting update (due to lava)
1597 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1599 while(m_transforming_liquid.size() != 0)
1601 // This should be done here so that it is done when continue is used
1602 if(loopcount >= initial_size * 3)
1607 Get a queued transforming liquid node
1609 v3s16 p0 = m_transforming_liquid.pop_front();
1611 MapNode n0 = getNodeNoEx(p0);
1614 Collect information about current node
1616 s8 liquid_level = -1;
1617 u8 liquid_kind = CONTENT_IGNORE;
1618 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1619 switch (liquid_type) {
1621 liquid_level = LIQUID_LEVEL_SOURCE;
1622 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1624 case LIQUID_FLOWING:
1625 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1626 liquid_kind = n0.getContent();
1629 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1630 // continue with the next node.
1631 if (n0.getContent() != CONTENT_AIR)
1633 liquid_kind = CONTENT_AIR;
1638 Collect information about the environment
1640 const v3s16 *dirs = g_6dirs;
1641 NodeNeighbor sources[6]; // surrounding sources
1642 int num_sources = 0;
1643 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1645 NodeNeighbor airs[6]; // surrounding air
1647 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1648 int num_neutrals = 0;
1649 bool flowing_down = false;
1650 for (u16 i = 0; i < 6; i++) {
1651 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1654 nt = NEIGHBOR_UPPER;
1657 nt = NEIGHBOR_LOWER;
1660 v3s16 npos = p0 + dirs[i];
1661 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1662 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1664 if (nb.n.getContent() == CONTENT_AIR) {
1665 airs[num_airs++] = nb;
1666 // if the current node is a water source the neighbor
1667 // should be enqueded for transformation regardless of whether the
1668 // current node changes or not.
1669 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1670 m_transforming_liquid.push_back(npos);
1671 // if the current node happens to be a flowing node, it will start to flow down here.
1672 if (nb.t == NEIGHBOR_LOWER) {
1673 flowing_down = true;
1676 neutrals[num_neutrals++] = nb;
1680 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1681 if (liquid_kind == CONTENT_AIR)
1682 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1683 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1684 neutrals[num_neutrals++] = nb;
1686 // Do not count bottom source, it will screw things up
1688 sources[num_sources++] = nb;
1691 case LIQUID_FLOWING:
1692 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1693 if (liquid_kind == CONTENT_AIR)
1694 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1695 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1696 neutrals[num_neutrals++] = nb;
1698 flows[num_flows++] = nb;
1699 if (nb.t == NEIGHBOR_LOWER)
1700 flowing_down = true;
1707 decide on the type (and possibly level) of the current node
1709 content_t new_node_content;
1710 s8 new_node_level = -1;
1711 s8 max_node_level = -1;
1712 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1713 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1714 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1715 // it's perfectly safe to use liquid_kind here to determine the new node content.
1716 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1717 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1718 // liquid_kind is set properly, see above
1719 new_node_content = liquid_kind;
1720 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1722 // no surrounding sources, so get the maximum level that can flow into this node
1723 for (u16 i = 0; i < num_flows; i++) {
1724 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1725 switch (flows[i].t) {
1726 case NEIGHBOR_UPPER:
1727 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1728 max_node_level = LIQUID_LEVEL_MAX;
1729 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1730 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1731 } else if (nb_liquid_level > max_node_level)
1732 max_node_level = nb_liquid_level;
1734 case NEIGHBOR_LOWER:
1736 case NEIGHBOR_SAME_LEVEL:
1737 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1738 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1739 max_node_level = nb_liquid_level - 1;
1745 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1746 if (viscosity > 1 && max_node_level != liquid_level) {
1747 // amount to gain, limited by viscosity
1748 // must be at least 1 in absolute value
1749 s8 level_inc = max_node_level - liquid_level;
1750 if (level_inc < -viscosity || level_inc > viscosity)
1751 new_node_level = liquid_level + level_inc/viscosity;
1752 else if (level_inc < 0)
1753 new_node_level = liquid_level - 1;
1754 else if (level_inc > 0)
1755 new_node_level = liquid_level + 1;
1756 if (new_node_level != max_node_level)
1757 must_reflow.push_back(p0);
1759 new_node_level = max_node_level;
1761 if (new_node_level >= 0)
1762 new_node_content = liquid_kind;
1764 new_node_content = CONTENT_AIR;
1769 check if anything has changed. if not, just continue with the next node.
1771 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1772 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1773 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1779 update the current node
1781 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1782 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1783 // set level to last 3 bits, flowing down bit to 4th bit
1784 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1786 // set the liquid level and flow bit to 0
1787 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1789 n0.setContent(new_node_content);
1791 v3s16 blockpos = getNodeBlockPos(p0);
1792 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1794 modified_blocks.insert(blockpos, block);
1795 // If node emits light, MapBlock requires lighting update
1796 if(nodemgr->get(n0).light_source != 0)
1797 lighting_modified_blocks[block->getPos()] = block;
1801 enqueue neighbors for update if neccessary
1803 switch (nodemgr->get(n0.getContent()).liquid_type) {
1805 case LIQUID_FLOWING:
1806 // make sure source flows into all neighboring nodes
1807 for (u16 i = 0; i < num_flows; i++)
1808 if (flows[i].t != NEIGHBOR_UPPER)
1809 m_transforming_liquid.push_back(flows[i].p);
1810 for (u16 i = 0; i < num_airs; i++)
1811 if (airs[i].t != NEIGHBOR_UPPER)
1812 m_transforming_liquid.push_back(airs[i].p);
1815 // this flow has turned to air; neighboring flows might need to do the same
1816 for (u16 i = 0; i < num_flows; i++)
1817 m_transforming_liquid.push_back(flows[i].p);
1821 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1822 while (must_reflow.size() > 0)
1823 m_transforming_liquid.push_back(must_reflow.pop_front());
1824 updateLighting(lighting_modified_blocks, modified_blocks);
1827 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1829 v3s16 blockpos = getNodeBlockPos(p);
1830 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1831 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1833 infostream<<"Map::getNodeMetadata(): Need to emerge "
1834 <<PP(blockpos)<<std::endl;
1835 block = emergeBlock(blockpos, false);
1839 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1843 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1847 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1849 v3s16 blockpos = getNodeBlockPos(p);
1850 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1851 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1853 infostream<<"Map::setNodeMetadata(): Need to emerge "
1854 <<PP(blockpos)<<std::endl;
1855 block = emergeBlock(blockpos, false);
1859 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1863 block->m_node_metadata->set(p_rel, meta);
1866 void Map::removeNodeMetadata(v3s16 p)
1868 v3s16 blockpos = getNodeBlockPos(p);
1869 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1870 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1873 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1877 block->m_node_metadata->remove(p_rel);
1880 void Map::nodeMetadataStep(float dtime,
1881 core::map<v3s16, MapBlock*> &changed_blocks)
1885 Currently there is no way to ensure that all the necessary
1886 blocks are loaded when this is run. (They might get unloaded)
1887 NOTE: ^- Actually, that might not be so. In a quick test it
1888 reloaded a block with a furnace when I walked back to it from
1891 core::map<v2s16, MapSector*>::Iterator si;
1892 si = m_sectors.getIterator();
1893 for(; si.atEnd() == false; si++)
1895 MapSector *sector = si.getNode()->getValue();
1896 core::list< MapBlock * > sectorblocks;
1897 sector->getBlocks(sectorblocks);
1898 core::list< MapBlock * >::Iterator i;
1899 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1901 MapBlock *block = *i;
1902 bool changed = block->m_node_metadata->step(dtime);
1904 changed_blocks[block->getPos()] = block;
1913 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1914 Map(dout_server, gamedef),
1916 m_map_metadata_changed(true),
1918 m_database_read(NULL),
1919 m_database_write(NULL)
1921 infostream<<__FUNCTION_NAME<<std::endl;
1923 //m_chunksize = 8; // Takes a few seconds
1925 if (g_settings->get("fixed_map_seed").empty())
1927 m_seed = (((u64)(myrand()%0xffff)<<0)
1928 + ((u64)(myrand()%0xffff)<<16)
1929 + ((u64)(myrand()%0xffff)<<32)
1930 + ((u64)(myrand()%0xffff)<<48));
1934 m_seed = g_settings->getU64("fixed_map_seed");
1938 Experimental and debug stuff
1945 Try to load map; if not found, create a new one.
1948 m_savedir = savedir;
1949 m_map_saving_enabled = false;
1953 // If directory exists, check contents and load if possible
1954 if(fs::PathExists(m_savedir))
1956 // If directory is empty, it is safe to save into it.
1957 if(fs::GetDirListing(m_savedir).size() == 0)
1959 infostream<<"Server: Empty save directory is valid."
1961 m_map_saving_enabled = true;
1966 // Load map metadata (seed, chunksize)
1969 catch(FileNotGoodException &e){
1970 infostream<<"WARNING: Could not load map metadata"
1971 //<<" Disabling chunk-based generator."
1977 // Load chunk metadata
1980 catch(FileNotGoodException &e){
1981 infostream<<"WARNING: Could not load chunk metadata."
1982 <<" Disabling chunk-based generator."
1987 /*infostream<<"Server: Successfully loaded chunk "
1988 "metadata and sector (0,0) from "<<savedir<<
1989 ", assuming valid save directory."
1992 infostream<<"Server: Successfully loaded map "
1993 <<"and chunk metadata from "<<savedir
1994 <<", assuming valid save directory."
1997 m_map_saving_enabled = true;
1998 // Map loaded, not creating new one
2002 // If directory doesn't exist, it is safe to save to it
2004 m_map_saving_enabled = true;
2007 catch(std::exception &e)
2009 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2010 <<", exception: "<<e.what()<<std::endl;
2011 infostream<<"Please remove the map or fix it."<<std::endl;
2012 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2015 infostream<<"Initializing new map."<<std::endl;
2017 // Create zero sector
2018 emergeSector(v2s16(0,0));
2020 // Initially write whole map
2021 save(MOD_STATE_CLEAN);
2024 ServerMap::~ServerMap()
2026 infostream<<__FUNCTION_NAME<<std::endl;
2030 if(m_map_saving_enabled)
2032 // Save only changed parts
2033 save(MOD_STATE_WRITE_AT_UNLOAD);
2034 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2038 infostream<<"Server: map not saved"<<std::endl;
2041 catch(std::exception &e)
2043 infostream<<"Server: Failed to save map to "<<m_savedir
2044 <<", exception: "<<e.what()<<std::endl;
2048 Close database if it was opened
2051 sqlite3_finalize(m_database_read);
2052 if(m_database_write)
2053 sqlite3_finalize(m_database_write);
2055 sqlite3_close(m_database);
2061 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2062 for(; i.atEnd() == false; i++)
2064 MapChunk *chunk = i.getNode()->getValue();
2070 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2072 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2073 if(enable_mapgen_debug_info)
2074 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2075 <<blockpos.Z<<")"<<std::endl;
2077 // Do nothing if not inside limits (+-1 because of neighbors)
2078 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2079 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2085 data->no_op = false;
2086 data->seed = m_seed;
2087 data->blockpos = blockpos;
2088 data->nodedef = m_gamedef->ndef();
2091 Create the whole area of this and the neighboring blocks
2094 //TimeTaker timer("initBlockMake() create area");
2096 for(s16 x=-1; x<=1; x++)
2097 for(s16 z=-1; z<=1; z++)
2099 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2100 // Sector metadata is loaded from disk if not already loaded.
2101 ServerMapSector *sector = createSector(sectorpos);
2104 for(s16 y=-1; y<=1; y++)
2106 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2107 //MapBlock *block = createBlock(p);
2108 // 1) get from memory, 2) load from disk
2109 MapBlock *block = emergeBlock(p, false);
2110 // 3) create a blank one
2113 block = createBlock(p);
2116 Block gets sunlight if this is true.
2118 Refer to the map generator heuristics.
2120 bool ug = mapgen::block_is_underground(data->seed, p);
2121 block->setIsUnderground(ug);
2124 // Lighting will not be valid after make_chunk is called
2125 block->setLightingExpired(true);
2126 // Lighting will be calculated
2127 //block->setLightingExpired(false);
2133 Now we have a big empty area.
2135 Make a ManualMapVoxelManipulator that contains this and the
2139 // The area that contains this block and it's neighbors
2140 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2141 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2143 data->vmanip = new ManualMapVoxelManipulator(this);
2144 //data->vmanip->setMap(this);
2148 //TimeTaker timer("initBlockMake() initialEmerge");
2149 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2152 // Data is ready now.
2155 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2156 core::map<v3s16, MapBlock*> &changed_blocks)
2158 v3s16 blockpos = data->blockpos;
2159 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2160 <<blockpos.Z<<")"<<std::endl;*/
2164 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2168 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2170 /*infostream<<"Resulting vmanip:"<<std::endl;
2171 data->vmanip.print(infostream);*/
2173 // Make sure affected blocks are loaded
2174 for(s16 x=-1; x<=1; x++)
2175 for(s16 z=-1; z<=1; z++)
2176 for(s16 y=-1; y<=1; y++)
2178 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2179 // Load from disk if not already in memory
2180 emergeBlock(p, false);
2184 Blit generated stuff to map
2185 NOTE: blitBackAll adds nearly everything to changed_blocks
2189 //TimeTaker timer("finishBlockMake() blitBackAll");
2190 data->vmanip->blitBackAll(&changed_blocks);
2193 if(enable_mapgen_debug_info)
2194 infostream<<"finishBlockMake: changed_blocks.size()="
2195 <<changed_blocks.size()<<std::endl;
2198 Copy transforming liquid information
2200 while(data->transforming_liquid.size() > 0)
2202 v3s16 p = data->transforming_liquid.pop_front();
2203 m_transforming_liquid.push_back(p);
2209 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2213 Set is_underground flag for lighting with sunlight.
2215 Refer to map generator heuristics.
2217 NOTE: This is done in initChunkMake
2219 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2223 Add sunlight to central block.
2224 This makes in-dark-spawning monsters to not flood the whole thing.
2225 Do not spread the light, though.
2227 /*core::map<v3s16, bool> light_sources;
2228 bool black_air_left = false;
2229 block->propagateSunlight(light_sources, true, &black_air_left);*/
2232 NOTE: Lighting and object adding shouldn't really be here, but
2233 lighting is a bit tricky to move properly to makeBlock.
2234 TODO: Do this the right way anyway, that is, move it to makeBlock.
2235 - There needs to be some way for makeBlock to report back if
2236 the lighting update is going further down because of the
2237 new block blocking light
2242 NOTE: This takes ~60ms, TODO: Investigate why
2245 TimeTaker t("finishBlockMake lighting update");
2247 core::map<v3s16, MapBlock*> lighting_update_blocks;
2250 lighting_update_blocks.insert(block->getPos(), block);
2255 v3s16 p = block->getPos()+v3s16(x,1,z);
2256 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2260 // All modified blocks
2261 // NOTE: Should this be done? If this is not done, then the lighting
2262 // of the others will be updated in a different place, one by one, i
2263 // think... or they might not? Well, at least they are left marked as
2264 // "lighting expired"; it seems that is not handled at all anywhere,
2265 // so enabling this will slow it down A LOT because otherwise it
2266 // would not do this at all. This causes the black trees.
2267 for(core::map<v3s16, MapBlock*>::Iterator
2268 i = changed_blocks.getIterator();
2269 i.atEnd() == false; i++)
2271 lighting_update_blocks.insert(i.getNode()->getKey(),
2272 i.getNode()->getValue());
2274 /*// Also force-add all the upmost blocks for proper sunlight
2275 for(s16 x=-1; x<=1; x++)
2276 for(s16 z=-1; z<=1; z++)
2278 v3s16 p = block->getPos()+v3s16(x,1,z);
2279 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2282 updateLighting(lighting_update_blocks, changed_blocks);
2285 Set lighting to non-expired state in all of them.
2286 This is cheating, but it is not fast enough if all of them
2287 would actually be updated.
2289 for(s16 x=-1; x<=1; x++)
2290 for(s16 y=-1; y<=1; y++)
2291 for(s16 z=-1; z<=1; z++)
2293 v3s16 p = block->getPos()+v3s16(x,y,z);
2294 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2297 if(enable_mapgen_debug_info == false)
2298 t.stop(true); // Hide output
2302 Add random objects to block
2304 mapgen::add_random_objects(block);
2307 Go through changed blocks
2309 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2310 i.atEnd() == false; i++)
2312 MapBlock *block = i.getNode()->getValue();
2315 Update day/night difference cache of the MapBlocks
2317 block->updateDayNightDiff();
2319 Set block as modified
2321 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2322 "finishBlockMake updateDayNightDiff");
2326 Set central block as generated
2328 block->setGenerated(true);
2331 Save changed parts of map
2332 NOTE: Will be saved later.
2334 //save(MOD_STATE_WRITE_AT_UNLOAD);
2336 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2337 <<blockpos.Z<<")"<<std::endl;*/
2339 if(enable_mapgen_debug_info)
2342 Analyze resulting blocks
2344 for(s16 x=-1; x<=1; x++)
2345 for(s16 y=-1; y<=1; y++)
2346 for(s16 z=-1; z<=1; z++)
2348 v3s16 p = block->getPos()+v3s16(x,y,z);
2349 MapBlock *block = getBlockNoCreateNoEx(p);
2351 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2352 infostream<<"Generated "<<spos<<": "
2353 <<analyze_block(block)<<std::endl;
2361 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2363 DSTACKF("%s: p2d=(%d,%d)",
2368 Check if it exists already in memory
2370 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2375 Try to load it from disk (with blocks)
2377 //if(loadSectorFull(p2d) == true)
2380 Try to load metadata from disk
2383 if(loadSectorMeta(p2d) == true)
2385 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2388 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2389 throw InvalidPositionException("");
2395 Do not create over-limit
2397 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2398 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2399 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2400 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2401 throw InvalidPositionException("createSector(): pos. over limit");
2404 Generate blank sector
2407 sector = new ServerMapSector(this, p2d, m_gamedef);
2409 // Sector position on map in nodes
2410 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2415 m_sectors.insert(p2d, sector);
2421 This is a quick-hand function for calling makeBlock().
2423 MapBlock * ServerMap::generateBlock(
2425 core::map<v3s16, MapBlock*> &modified_blocks
2428 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2430 /*infostream<<"generateBlock(): "
2431 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2434 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2436 TimeTaker timer("generateBlock");
2438 //MapBlock *block = original_dummy;
2440 v2s16 p2d(p.X, p.Z);
2441 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2444 Do not generate over-limit
2446 if(blockpos_over_limit(p))
2448 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2449 throw InvalidPositionException("generateBlock(): pos. over limit");
2453 Create block make data
2455 mapgen::BlockMakeData data;
2456 initBlockMake(&data, p);
2462 TimeTaker t("mapgen::make_block()");
2463 mapgen::make_block(&data);
2465 if(enable_mapgen_debug_info == false)
2466 t.stop(true); // Hide output
2470 Blit data back on map, update lighting, add mobs and whatever this does
2472 finishBlockMake(&data, modified_blocks);
2477 MapBlock *block = getBlockNoCreateNoEx(p);
2485 bool erroneus_content = false;
2486 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2487 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2488 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2491 MapNode n = block->getNode(p);
2492 if(n.getContent() == CONTENT_IGNORE)
2494 infostream<<"CONTENT_IGNORE at "
2495 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2497 erroneus_content = true;
2501 if(erroneus_content)
2510 Generate a completely empty block
2514 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2515 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2517 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2520 n.setContent(CONTENT_AIR);
2521 block->setNode(v3s16(x0,y0,z0), n);
2527 if(enable_mapgen_debug_info == false)
2528 timer.stop(true); // Hide output
2533 MapBlock * ServerMap::createBlock(v3s16 p)
2535 DSTACKF("%s: p=(%d,%d,%d)",
2536 __FUNCTION_NAME, p.X, p.Y, p.Z);
2539 Do not create over-limit
2541 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2542 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2543 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2544 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2545 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2546 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2547 throw InvalidPositionException("createBlock(): pos. over limit");
2549 v2s16 p2d(p.X, p.Z);
2552 This will create or load a sector if not found in memory.
2553 If block exists on disk, it will be loaded.
2555 NOTE: On old save formats, this will be slow, as it generates
2556 lighting on blocks for them.
2558 ServerMapSector *sector;
2560 sector = (ServerMapSector*)createSector(p2d);
2561 assert(sector->getId() == MAPSECTOR_SERVER);
2563 catch(InvalidPositionException &e)
2565 infostream<<"createBlock: createSector() failed"<<std::endl;
2569 NOTE: This should not be done, or at least the exception
2570 should not be passed on as std::exception, because it
2571 won't be catched at all.
2573 /*catch(std::exception &e)
2575 infostream<<"createBlock: createSector() failed: "
2576 <<e.what()<<std::endl;
2581 Try to get a block from the sector
2584 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2587 if(block->isDummy())
2592 block = sector->createBlankBlock(block_y);
2596 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2598 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2600 p.X, p.Y, p.Z, allow_generate);
2603 MapBlock *block = getBlockNoCreateNoEx(p);
2604 if(block && block->isDummy() == false)
2609 MapBlock *block = loadBlock(p);
2616 core::map<v3s16, MapBlock*> modified_blocks;
2617 MapBlock *block = generateBlock(p, modified_blocks);
2621 event.type = MEET_OTHER;
2624 // Copy modified_blocks to event
2625 for(core::map<v3s16, MapBlock*>::Iterator
2626 i = modified_blocks.getIterator();
2627 i.atEnd()==false; i++)
2629 event.modified_blocks.insert(i.getNode()->getKey(), false);
2633 dispatchEvent(&event);
2642 s16 ServerMap::findGroundLevel(v2s16 p2d)
2646 Uh, just do something random...
2648 // Find existing map from top to down
2651 v3s16 p(p2d.X, max, p2d.Y);
2652 for(; p.Y>min; p.Y--)
2654 MapNode n = getNodeNoEx(p);
2655 if(n.getContent() != CONTENT_IGNORE)
2660 // If this node is not air, go to plan b
2661 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2663 // Search existing walkable and return it
2664 for(; p.Y>min; p.Y--)
2666 MapNode n = getNodeNoEx(p);
2667 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2676 Determine from map generator noise functions
2679 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2682 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2683 //return (s16)level;
2686 void ServerMap::createDatabase() {
2689 e = sqlite3_exec(m_database,
2690 "CREATE TABLE IF NOT EXISTS `blocks` ("
2691 "`pos` INT NOT NULL PRIMARY KEY,"
2694 , NULL, NULL, NULL);
2695 if(e == SQLITE_ABORT)
2696 throw FileNotGoodException("Could not create database structure");
2698 infostream<<"Server: Database structure was created";
2701 void ServerMap::verifyDatabase() {
2706 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2707 bool needs_create = false;
2711 Open the database connection
2714 createDirs(m_savedir);
2716 if(!fs::PathExists(dbp))
2717 needs_create = true;
2719 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2720 if(d != SQLITE_OK) {
2721 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2722 throw FileNotGoodException("Cannot open database file");
2728 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2729 if(d != SQLITE_OK) {
2730 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2731 throw FileNotGoodException("Cannot prepare read statement");
2734 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2735 if(d != SQLITE_OK) {
2736 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2737 throw FileNotGoodException("Cannot prepare write statement");
2740 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2741 if(d != SQLITE_OK) {
2742 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2743 throw FileNotGoodException("Cannot prepare read statement");
2746 infostream<<"Server: Database opened"<<std::endl;
2750 bool ServerMap::loadFromFolders() {
2751 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2756 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2757 return (sqlite3_int64)pos.Z*16777216 +
2758 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2761 void ServerMap::createDirs(std::string path)
2763 if(fs::CreateAllDirs(path) == false)
2765 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2766 <<"\""<<path<<"\""<<std::endl;
2767 throw BaseException("ServerMap failed to create directory");
2771 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2777 snprintf(cc, 9, "%.4x%.4x",
2778 (unsigned int)pos.X&0xffff,
2779 (unsigned int)pos.Y&0xffff);
2781 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2783 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2784 (unsigned int)pos.X&0xfff,
2785 (unsigned int)pos.Y&0xfff);
2787 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2793 v2s16 ServerMap::getSectorPos(std::string dirname)
2797 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2798 assert(spos != std::string::npos);
2799 if(dirname.size() - spos == 8)
2802 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2804 else if(dirname.size() - spos == 3)
2807 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2808 // Sign-extend the 12 bit values up to 16 bits...
2809 if(x&0x800) x|=0xF000;
2810 if(y&0x800) y|=0xF000;
2817 v2s16 pos((s16)x, (s16)y);
2821 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2823 v2s16 p2d = getSectorPos(sectordir);
2825 if(blockfile.size() != 4){
2826 throw InvalidFilenameException("Invalid block filename");
2829 int r = sscanf(blockfile.c_str(), "%4x", &y);
2831 throw InvalidFilenameException("Invalid block filename");
2832 return v3s16(p2d.X, y, p2d.Y);
2835 std::string ServerMap::getBlockFilename(v3s16 p)
2838 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2842 void ServerMap::save(ModifiedState save_level)
2844 DSTACK(__FUNCTION_NAME);
2845 if(m_map_saving_enabled == false)
2847 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2851 if(save_level == MOD_STATE_CLEAN)
2852 infostream<<"ServerMap: Saving whole map, this can take time."
2855 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2860 // Profile modified reasons
2861 Profiler modprofiler;
2863 u32 sector_meta_count = 0;
2864 u32 block_count = 0;
2865 u32 block_count_all = 0; // Number of blocks in memory
2867 // Don't do anything with sqlite unless something is really saved
2868 bool save_started = false;
2870 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2871 for(; i.atEnd() == false; i++)
2873 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2874 assert(sector->getId() == MAPSECTOR_SERVER);
2876 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2878 saveSectorMeta(sector);
2879 sector_meta_count++;
2881 core::list<MapBlock*> blocks;
2882 sector->getBlocks(blocks);
2883 core::list<MapBlock*>::Iterator j;
2885 for(j=blocks.begin(); j!=blocks.end(); j++)
2887 MapBlock *block = *j;
2891 if(block->getModified() >= save_level)
2896 save_started = true;
2899 modprofiler.add(block->getModifiedReason(), 1);
2904 /*infostream<<"ServerMap: Written block ("
2905 <<block->getPos().X<<","
2906 <<block->getPos().Y<<","
2907 <<block->getPos().Z<<")"
2916 Only print if something happened or saved whole map
2918 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2919 || block_count != 0)
2921 infostream<<"ServerMap: Written: "
2922 <<sector_meta_count<<" sector metadata files, "
2923 <<block_count<<" block files"
2924 <<", "<<block_count_all<<" blocks in memory."
2926 PrintInfo(infostream); // ServerMap/ClientMap:
2927 infostream<<"Blocks modified by: "<<std::endl;
2928 modprofiler.print(infostream);
2932 static s32 unsignedToSigned(s32 i, s32 max_positive)
2934 if(i < max_positive)
2937 return i - 2*max_positive;
2940 // modulo of a negative number does not work consistently in C
2941 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2945 return mod - ((-i) % mod);
2948 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2950 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2952 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2954 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2955 return v3s16(x,y,z);
2958 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2960 if(loadFromFolders()){
2961 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2962 <<"all blocks that are stored in flat files"<<std::endl;
2968 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2970 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2971 v3s16 p = getIntegerAsBlock(block_i);
2972 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2978 void ServerMap::saveMapMeta()
2980 DSTACK(__FUNCTION_NAME);
2982 infostream<<"ServerMap::saveMapMeta(): "
2986 createDirs(m_savedir);
2988 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2989 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2990 if(os.good() == false)
2992 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2993 <<"could not open"<<fullpath<<std::endl;
2994 throw FileNotGoodException("Cannot open chunk metadata");
2998 params.setU64("seed", m_seed);
3000 params.writeLines(os);
3002 os<<"[end_of_params]\n";
3004 m_map_metadata_changed = false;
3007 void ServerMap::loadMapMeta()
3009 DSTACK(__FUNCTION_NAME);
3011 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3014 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3015 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3016 if(is.good() == false)
3018 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3019 <<"could not open"<<fullpath<<std::endl;
3020 throw FileNotGoodException("Cannot open map metadata");
3028 throw SerializationError
3029 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3031 std::getline(is, line);
3032 std::string trimmedline = trim(line);
3033 if(trimmedline == "[end_of_params]")
3035 params.parseConfigLine(line);
3038 m_seed = params.getU64("seed");
3040 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3043 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3045 DSTACK(__FUNCTION_NAME);
3046 // Format used for writing
3047 u8 version = SER_FMT_VER_HIGHEST;
3049 v2s16 pos = sector->getPos();
3050 std::string dir = getSectorDir(pos);
3053 std::string fullpath = dir + DIR_DELIM + "meta";
3054 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3055 if(o.good() == false)
3056 throw FileNotGoodException("Cannot open sector metafile");
3058 sector->serialize(o, version);
3060 sector->differs_from_disk = false;
3063 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3065 DSTACK(__FUNCTION_NAME);
3067 v2s16 p2d = getSectorPos(sectordir);
3069 ServerMapSector *sector = NULL;
3071 std::string fullpath = sectordir + DIR_DELIM + "meta";
3072 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3073 if(is.good() == false)
3075 // If the directory exists anyway, it probably is in some old
3076 // format. Just go ahead and create the sector.
3077 if(fs::PathExists(sectordir))
3079 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3080 <<fullpath<<" doesn't exist but directory does."
3081 <<" Continuing with a sector with no metadata."
3083 sector = new ServerMapSector(this, p2d, m_gamedef);
3084 m_sectors.insert(p2d, sector);
3088 throw FileNotGoodException("Cannot open sector metafile");
3093 sector = ServerMapSector::deSerialize
3094 (is, this, p2d, m_sectors, m_gamedef);
3096 saveSectorMeta(sector);
3099 sector->differs_from_disk = false;
3104 bool ServerMap::loadSectorMeta(v2s16 p2d)
3106 DSTACK(__FUNCTION_NAME);
3108 MapSector *sector = NULL;
3110 // The directory layout we're going to load from.
3111 // 1 - original sectors/xxxxzzzz/
3112 // 2 - new sectors2/xxx/zzz/
3113 // If we load from anything but the latest structure, we will
3114 // immediately save to the new one, and remove the old.
3116 std::string sectordir1 = getSectorDir(p2d, 1);
3117 std::string sectordir;
3118 if(fs::PathExists(sectordir1))
3120 sectordir = sectordir1;
3125 sectordir = getSectorDir(p2d, 2);
3129 sector = loadSectorMeta(sectordir, loadlayout != 2);
3131 catch(InvalidFilenameException &e)
3135 catch(FileNotGoodException &e)
3139 catch(std::exception &e)
3148 bool ServerMap::loadSectorFull(v2s16 p2d)
3150 DSTACK(__FUNCTION_NAME);
3152 MapSector *sector = NULL;
3154 // The directory layout we're going to load from.
3155 // 1 - original sectors/xxxxzzzz/
3156 // 2 - new sectors2/xxx/zzz/
3157 // If we load from anything but the latest structure, we will
3158 // immediately save to the new one, and remove the old.
3160 std::string sectordir1 = getSectorDir(p2d, 1);
3161 std::string sectordir;
3162 if(fs::PathExists(sectordir1))
3164 sectordir = sectordir1;
3169 sectordir = getSectorDir(p2d, 2);
3173 sector = loadSectorMeta(sectordir, loadlayout != 2);
3175 catch(InvalidFilenameException &e)
3179 catch(FileNotGoodException &e)
3183 catch(std::exception &e)
3191 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3193 std::vector<fs::DirListNode>::iterator i2;
3194 for(i2=list2.begin(); i2!=list2.end(); i2++)
3200 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3202 catch(InvalidFilenameException &e)
3204 // This catches unknown crap in directory
3210 infostream<<"Sector converted to new layout - deleting "<<
3211 sectordir1<<std::endl;
3212 fs::RecursiveDelete(sectordir1);
3219 void ServerMap::beginSave() {
3221 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3222 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3225 void ServerMap::endSave() {
3227 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3228 infostream<<"WARNING: endSave() failed, map might not have saved.";
3231 void ServerMap::saveBlock(MapBlock *block)
3233 DSTACK(__FUNCTION_NAME);
3235 Dummy blocks are not written
3237 if(block->isDummy())
3239 /*v3s16 p = block->getPos();
3240 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3241 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3245 // Format used for writing
3246 u8 version = SER_FMT_VER_HIGHEST;
3248 v3s16 p3d = block->getPos();
3252 v2s16 p2d(p3d.X, p3d.Z);
3253 std::string sectordir = getSectorDir(p2d);
3255 createDirs(sectordir);
3257 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3258 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3259 if(o.good() == false)
3260 throw FileNotGoodException("Cannot open block data");
3263 [0] u8 serialization version
3269 std::ostringstream o(std::ios_base::binary);
3271 o.write((char*)&version, 1);
3274 block->serialize(o, version);
3276 // Write extra data stored on disk
3277 block->serializeDiskExtra(o, version);
3279 // Write block to database
3281 std::string tmp = o.str();
3282 const char *bytes = tmp.c_str();
3284 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3285 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3286 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3287 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3288 int written = sqlite3_step(m_database_write);
3289 if(written != SQLITE_DONE)
3290 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3291 <<sqlite3_errmsg(m_database)<<std::endl;
3292 // Make ready for later reuse
3293 sqlite3_reset(m_database_write);
3295 // We just wrote it to the disk so clear modified flag
3296 block->resetModified();
3299 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3301 DSTACK(__FUNCTION_NAME);
3303 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3306 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3307 if(is.good() == false)
3308 throw FileNotGoodException("Cannot open block file");
3310 v3s16 p3d = getBlockPos(sectordir, blockfile);
3311 v2s16 p2d(p3d.X, p3d.Z);
3313 assert(sector->getPos() == p2d);
3315 u8 version = SER_FMT_VER_INVALID;
3316 is.read((char*)&version, 1);
3319 throw SerializationError("ServerMap::loadBlock(): Failed"
3320 " to read MapBlock version");
3322 /*u32 block_size = MapBlock::serializedLength(version);
3323 SharedBuffer<u8> data(block_size);
3324 is.read((char*)*data, block_size);*/
3326 // This will always return a sector because we're the server
3327 //MapSector *sector = emergeSector(p2d);
3329 MapBlock *block = NULL;
3330 bool created_new = false;
3331 block = sector->getBlockNoCreateNoEx(p3d.Y);
3334 block = sector->createBlankBlockNoInsert(p3d.Y);
3339 block->deSerialize(is, version);
3341 // Read extra data stored on disk
3342 block->deSerializeDiskExtra(is, version);
3344 // If it's a new block, insert it to the map
3346 sector->insertBlock(block);
3349 Save blocks loaded in old format in new format
3352 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3356 // Should be in database now, so delete the old file
3357 fs::RecursiveDelete(fullpath);
3360 // We just loaded it from the disk, so it's up-to-date.
3361 block->resetModified();
3364 catch(SerializationError &e)
3366 infostream<<"WARNING: Invalid block data on disk "
3367 <<"fullpath="<<fullpath
3368 <<" (SerializationError). "
3369 <<"what()="<<e.what()
3371 //" Ignoring. A new one will be generated.
3374 // TODO: Backup file; name is in fullpath.
3378 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3380 DSTACK(__FUNCTION_NAME);
3383 std::istringstream is(*blob, std::ios_base::binary);
3385 u8 version = SER_FMT_VER_INVALID;
3386 is.read((char*)&version, 1);
3389 throw SerializationError("ServerMap::loadBlock(): Failed"
3390 " to read MapBlock version");
3392 /*u32 block_size = MapBlock::serializedLength(version);
3393 SharedBuffer<u8> data(block_size);
3394 is.read((char*)*data, block_size);*/
3396 // This will always return a sector because we're the server
3397 //MapSector *sector = emergeSector(p2d);
3399 MapBlock *block = NULL;
3400 bool created_new = false;
3401 block = sector->getBlockNoCreateNoEx(p3d.Y);
3404 block = sector->createBlankBlockNoInsert(p3d.Y);
3409 block->deSerialize(is, version);
3411 // Read extra data stored on disk
3412 block->deSerializeDiskExtra(is, version);
3414 // If it's a new block, insert it to the map
3416 sector->insertBlock(block);
3419 Save blocks loaded in old format in new format
3422 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3423 // Only save if asked to; no need to update version
3427 // We just loaded it from, so it's up-to-date.
3428 block->resetModified();
3431 catch(SerializationError &e)
3433 infostream<<"WARNING: Invalid block data in database "
3434 <<" (SerializationError). "
3435 <<"what()="<<e.what()
3437 //" Ignoring. A new one will be generated.
3440 // TODO: Copy to a backup database.
3444 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3446 DSTACK(__FUNCTION_NAME);
3448 v2s16 p2d(blockpos.X, blockpos.Z);
3450 if(!loadFromFolders()) {
3453 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3454 infostream<<"WARNING: Could not bind block position for load: "
3455 <<sqlite3_errmsg(m_database)<<std::endl;
3456 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3458 Make sure sector is loaded
3460 MapSector *sector = createSector(p2d);
3465 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3466 size_t len = sqlite3_column_bytes(m_database_read, 0);
3468 std::string datastr(data, len);
3470 loadBlock(&datastr, blockpos, sector, false);
3472 sqlite3_step(m_database_read);
3473 // We should never get more than 1 row, so ok to reset
3474 sqlite3_reset(m_database_read);
3476 return getBlockNoCreateNoEx(blockpos);
3478 sqlite3_reset(m_database_read);
3480 // Not found in database, try the files
3483 // The directory layout we're going to load from.
3484 // 1 - original sectors/xxxxzzzz/
3485 // 2 - new sectors2/xxx/zzz/
3486 // If we load from anything but the latest structure, we will
3487 // immediately save to the new one, and remove the old.
3489 std::string sectordir1 = getSectorDir(p2d, 1);
3490 std::string sectordir;
3491 if(fs::PathExists(sectordir1))
3493 sectordir = sectordir1;
3498 sectordir = getSectorDir(p2d, 2);
3502 Make sure sector is loaded
3504 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3508 sector = loadSectorMeta(sectordir, loadlayout != 2);
3510 catch(InvalidFilenameException &e)
3514 catch(FileNotGoodException &e)
3518 catch(std::exception &e)
3525 Make sure file exists
3528 std::string blockfilename = getBlockFilename(blockpos);
3529 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3533 Load block and save it to the database
3535 loadBlock(sectordir, blockfilename, sector, true);
3536 return getBlockNoCreateNoEx(blockpos);
3539 void ServerMap::PrintInfo(std::ostream &out)
3550 ClientMap::ClientMap(
3553 MapDrawControl &control,
3554 scene::ISceneNode* parent,
3555 scene::ISceneManager* mgr,
3558 Map(dout_client, gamedef),
3559 scene::ISceneNode(parent, mgr, id),
3562 m_camera_position(0,0,0),
3563 m_camera_direction(0,0,1),
3566 m_camera_mutex.Init();
3567 assert(m_camera_mutex.IsInitialized());
3569 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3570 BS*1000000,BS*1000000,BS*1000000);
3573 ClientMap::~ClientMap()
3575 /*JMutexAutoLock lock(mesh_mutex);
3584 MapSector * ClientMap::emergeSector(v2s16 p2d)
3586 DSTACK(__FUNCTION_NAME);
3587 // Check that it doesn't exist already
3589 return getSectorNoGenerate(p2d);
3591 catch(InvalidPositionException &e)
3596 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3599 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3600 m_sectors.insert(p2d, sector);
3607 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3609 DSTACK(__FUNCTION_NAME);
3610 ClientMapSector *sector = NULL;
3612 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3614 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3618 sector = (ClientMapSector*)n->getValue();
3619 assert(sector->getId() == MAPSECTOR_CLIENT);
3623 sector = new ClientMapSector(this, p2d);
3625 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3626 m_sectors.insert(p2d, sector);
3630 sector->deSerialize(is);
3634 void ClientMap::OnRegisterSceneNode()
3638 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3639 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3642 ISceneNode::OnRegisterSceneNode();
3645 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3646 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3648 float d0 = (float)BS * p0.getDistanceFrom(p1);
3650 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3652 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3654 for(float s=start_off; s<d0+end_off; s+=step){
3655 v3f pf = p0f + uf * s;
3656 v3s16 p = floatToInt(pf, BS);
3657 MapNode n = map->getNodeNoEx(p);
3658 bool is_transparent = false;
3659 const ContentFeatures &f = nodemgr->get(n);
3660 if(f.solidness == 0)
3661 is_transparent = (f.visual_solidness != 2);
3663 is_transparent = (f.solidness != 2);
3664 if(!is_transparent){
3666 if(count >= needed_count)
3674 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3676 INodeDefManager *nodemgr = m_gamedef->ndef();
3678 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3679 DSTACK(__FUNCTION_NAME);
3681 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3684 if(pass == scene::ESNRP_SOLID)
3685 prefix = "CM: solid: ";
3687 prefix = "CM: transparent: ";
3690 This is called two times per frame, reset on the non-transparent one
3692 if(pass == scene::ESNRP_SOLID)
3694 m_last_drawn_sectors.clear();
3698 Get time for measuring timeout.
3700 Measuring time is very useful for long delays when the
3701 machine is swapping a lot.
3703 int time1 = time(0);
3705 //u32 daynight_ratio = m_client->getDayNightRatio();
3707 m_camera_mutex.Lock();
3708 v3f camera_position = m_camera_position;
3709 v3f camera_direction = m_camera_direction;
3710 f32 camera_fov = m_camera_fov;
3711 m_camera_mutex.Unlock();
3714 Get all blocks and draw all visible ones
3717 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3719 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3721 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3722 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3724 // Take a fair amount as we will be dropping more out later
3725 // Umm... these additions are a bit strange but they are needed.
3727 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3728 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3729 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3731 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3732 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3733 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3735 u32 vertex_count = 0;
3736 u32 meshbuffer_count = 0;
3738 // For limiting number of mesh updates per frame
3739 u32 mesh_update_count = 0;
3741 // Number of blocks in rendering range
3742 u32 blocks_in_range = 0;
3743 // Number of blocks occlusion culled
3744 u32 blocks_occlusion_culled = 0;
3745 // Number of blocks in rendering range but don't have a mesh
3746 u32 blocks_in_range_without_mesh = 0;
3747 // Blocks that had mesh that would have been drawn according to
3748 // rendering range (if max blocks limit didn't kick in)
3749 u32 blocks_would_have_drawn = 0;
3750 // Blocks that were drawn and had a mesh
3751 u32 blocks_drawn = 0;
3752 // Blocks which had a corresponding meshbuffer for this pass
3753 u32 blocks_had_pass_meshbuf = 0;
3754 // Blocks from which stuff was actually drawn
3755 u32 blocks_without_stuff = 0;
3758 Collect a set of blocks for drawing
3761 core::map<v3s16, MapBlock*> drawset;
3764 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3766 for(core::map<v2s16, MapSector*>::Iterator
3767 si = m_sectors.getIterator();
3768 si.atEnd() == false; si++)
3770 MapSector *sector = si.getNode()->getValue();
3771 v2s16 sp = sector->getPos();
3773 if(m_control.range_all == false)
3775 if(sp.X < p_blocks_min.X
3776 || sp.X > p_blocks_max.X
3777 || sp.Y < p_blocks_min.Z
3778 || sp.Y > p_blocks_max.Z)
3782 core::list< MapBlock * > sectorblocks;
3783 sector->getBlocks(sectorblocks);
3786 Loop through blocks in sector
3789 u32 sector_blocks_drawn = 0;
3791 core::list< MapBlock * >::Iterator i;
3792 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3794 MapBlock *block = *i;
3797 Compare block position to camera position, skip
3798 if not seen on display
3801 float range = 100000 * BS;
3802 if(m_control.range_all == false)
3803 range = m_control.wanted_range * BS;
3806 if(isBlockInSight(block->getPos(), camera_position,
3807 camera_direction, camera_fov,
3808 range, &d) == false)
3813 // This is ugly (spherical distance limit?)
3814 /*if(m_control.range_all == false &&
3815 d - 0.5*BS*MAP_BLOCKSIZE > range)
3822 Update expired mesh (used for day/night change)
3824 It doesn't work exactly like it should now with the
3825 tasked mesh update but whatever.
3828 bool mesh_expired = false;
3831 JMutexAutoLock lock(block->mesh_mutex);
3833 mesh_expired = block->getMeshExpired();
3835 // Mesh has not been expired and there is no mesh:
3836 // block has no content
3837 if(block->mesh == NULL && mesh_expired == false){
3838 blocks_in_range_without_mesh++;
3843 f32 faraway = BS*50;
3844 //f32 faraway = m_control.wanted_range * BS;
3847 This has to be done with the mesh_mutex unlocked
3849 // Pretty random but this should work somewhat nicely
3850 if(mesh_expired && (
3851 (mesh_update_count < 3
3852 && (d < faraway || mesh_update_count < 2)
3855 (m_control.range_all && mesh_update_count < 20)
3858 /*if(mesh_expired && mesh_update_count < 6
3859 && (d < faraway || mesh_update_count < 3))*/
3861 mesh_update_count++;
3863 // Mesh has been expired: generate new mesh
3864 //block->updateMesh(daynight_ratio);
3865 m_client->addUpdateMeshTask(block->getPos());
3867 mesh_expired = false;
3875 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3876 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3878 float stepfac = 1.1;
3879 float startoff = BS*1;
3880 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3881 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3882 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3883 u32 needed_count = 1;
3885 isOccluded(this, spn, cpn + v3s16(0,0,0),
3886 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3887 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3888 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3889 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3890 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3891 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3892 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3893 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3894 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3895 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3896 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3897 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3898 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3899 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3900 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3901 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3902 step, stepfac, startoff, endoff, needed_count, nodemgr)
3905 blocks_occlusion_culled++;
3909 // This block is in range. Reset usage timer.
3910 block->resetUsageTimer();
3913 Ignore if mesh doesn't exist
3916 JMutexAutoLock lock(block->mesh_mutex);
3918 scene::SMesh *mesh = block->mesh;
3921 blocks_in_range_without_mesh++;
3926 // Limit block count in case of a sudden increase
3927 blocks_would_have_drawn++;
3928 if(blocks_drawn >= m_control.wanted_max_blocks
3929 && m_control.range_all == false
3930 && d > m_control.wanted_min_range * BS)
3934 drawset[block->getPos()] = block;
3936 sector_blocks_drawn++;
3939 } // foreach sectorblocks
3941 if(sector_blocks_drawn != 0)
3942 m_last_drawn_sectors[sp] = true;
3947 Draw the selected MapBlocks
3951 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3953 int timecheck_counter = 0;
3954 for(core::map<v3s16, MapBlock*>::Iterator
3955 i = drawset.getIterator();
3956 i.atEnd() == false; i++)
3959 timecheck_counter++;
3960 if(timecheck_counter > 50)
3962 timecheck_counter = 0;
3963 int time2 = time(0);
3964 if(time2 > time1 + 4)
3966 infostream<<"ClientMap::renderMap(): "
3967 "Rendering takes ages, returning."
3974 MapBlock *block = i.getNode()->getValue();
3977 Draw the faces of the block
3980 JMutexAutoLock lock(block->mesh_mutex);
3982 scene::SMesh *mesh = block->mesh;
3985 u32 c = mesh->getMeshBufferCount();
3986 bool stuff_actually_drawn = false;
3987 for(u32 i=0; i<c; i++)
3989 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3990 const video::SMaterial& material = buf->getMaterial();
3991 video::IMaterialRenderer* rnd =
3992 driver->getMaterialRenderer(material.MaterialType);
3993 bool transparent = (rnd && rnd->isTransparent());
3994 // Render transparent on transparent pass and likewise.
3995 if(transparent == is_transparent_pass)
3997 if(buf->getVertexCount() == 0)
3998 errorstream<<"Block ["<<analyze_block(block)
3999 <<"] contains an empty meshbuf"<<std::endl;
4001 This *shouldn't* hurt too much because Irrlicht
4002 doesn't change opengl textures if the old
4003 material has the same texture.
4005 driver->setMaterial(buf->getMaterial());
4006 driver->drawMeshBuffer(buf);
4007 vertex_count += buf->getVertexCount();
4009 stuff_actually_drawn = true;
4012 if(stuff_actually_drawn)
4013 blocks_had_pass_meshbuf++;
4015 blocks_without_stuff++;
4020 // Log only on solid pass because values are the same
4021 if(pass == scene::ESNRP_SOLID){
4022 g_profiler->avg("CM: blocks in range", blocks_in_range);
4023 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
4024 if(blocks_in_range != 0)
4025 g_profiler->avg("CM: blocks in range without mesh (frac)",
4026 (float)blocks_in_range_without_mesh/blocks_in_range);
4027 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4030 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4031 if(blocks_had_pass_meshbuf != 0)
4032 g_profiler->avg(prefix+"meshbuffers per block",
4033 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4034 if(blocks_drawn != 0)
4035 g_profiler->avg(prefix+"empty blocks (frac)",
4036 (float)blocks_without_stuff / blocks_drawn);
4038 m_control.blocks_drawn = blocks_drawn;
4039 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4041 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4042 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4045 void ClientMap::renderPostFx()
4047 INodeDefManager *nodemgr = m_gamedef->ndef();
4049 // Sadly ISceneManager has no "post effects" render pass, in that case we
4050 // could just register for that and handle it in renderMap().
4052 m_camera_mutex.Lock();
4053 v3f camera_position = m_camera_position;
4054 m_camera_mutex.Unlock();
4056 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4058 // - If the player is in a solid node, make everything black.
4059 // - If the player is in liquid, draw a semi-transparent overlay.
4060 const ContentFeatures& features = nodemgr->get(n);
4061 video::SColor post_effect_color = features.post_effect_color;
4062 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4064 post_effect_color = video::SColor(255, 0, 0, 0);
4066 if (post_effect_color.getAlpha() != 0)
4068 // Draw a full-screen rectangle
4069 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4070 v2u32 ss = driver->getScreenSize();
4071 core::rect<s32> rect(0,0, ss.X, ss.Y);
4072 driver->draw2DRectangle(post_effect_color, rect);
4076 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4077 core::map<v3s16, MapBlock*> *affected_blocks)
4079 bool changed = false;
4081 Add it to all blocks touching it
4084 v3s16(0,0,0), // this
4085 v3s16(0,0,1), // back
4086 v3s16(0,1,0), // top
4087 v3s16(1,0,0), // right
4088 v3s16(0,0,-1), // front
4089 v3s16(0,-1,0), // bottom
4090 v3s16(-1,0,0), // left
4092 for(u16 i=0; i<7; i++)
4094 v3s16 p2 = p + dirs[i];
4095 // Block position of neighbor (or requested) node
4096 v3s16 blockpos = getNodeBlockPos(p2);
4097 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4098 if(blockref == NULL)
4100 // Relative position of requested node
4101 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4102 if(blockref->setTempMod(relpos, mod))
4107 if(changed && affected_blocks!=NULL)
4109 for(u16 i=0; i<7; i++)
4111 v3s16 p2 = p + dirs[i];
4112 // Block position of neighbor (or requested) node
4113 v3s16 blockpos = getNodeBlockPos(p2);
4114 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4115 if(blockref == NULL)
4117 affected_blocks->insert(blockpos, blockref);
4123 bool ClientMap::clearTempMod(v3s16 p,
4124 core::map<v3s16, MapBlock*> *affected_blocks)
4126 bool changed = false;
4128 v3s16(0,0,0), // this
4129 v3s16(0,0,1), // back
4130 v3s16(0,1,0), // top
4131 v3s16(1,0,0), // right
4132 v3s16(0,0,-1), // front
4133 v3s16(0,-1,0), // bottom
4134 v3s16(-1,0,0), // left
4136 for(u16 i=0; i<7; i++)
4138 v3s16 p2 = p + dirs[i];
4139 // Block position of neighbor (or requested) node
4140 v3s16 blockpos = getNodeBlockPos(p2);
4141 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4142 if(blockref == NULL)
4144 // Relative position of requested node
4145 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4146 if(blockref->clearTempMod(relpos))
4151 if(changed && affected_blocks!=NULL)
4153 for(u16 i=0; i<7; i++)
4155 v3s16 p2 = p + dirs[i];
4156 // Block position of neighbor (or requested) node
4157 v3s16 blockpos = getNodeBlockPos(p2);
4158 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4159 if(blockref == NULL)
4161 affected_blocks->insert(blockpos, blockref);
4167 void ClientMap::expireMeshes(bool only_daynight_diffed)
4169 TimeTaker timer("expireMeshes()");
4171 core::map<v2s16, MapSector*>::Iterator si;
4172 si = m_sectors.getIterator();
4173 for(; si.atEnd() == false; si++)
4175 MapSector *sector = si.getNode()->getValue();
4177 core::list< MapBlock * > sectorblocks;
4178 sector->getBlocks(sectorblocks);
4180 core::list< MapBlock * >::Iterator i;
4181 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4183 MapBlock *block = *i;
4185 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4191 JMutexAutoLock lock(block->mesh_mutex);
4192 if(block->mesh != NULL)
4194 /*block->mesh->drop();
4195 block->mesh = NULL;*/
4196 block->setMeshExpired(true);
4203 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4205 assert(mapType() == MAPTYPE_CLIENT);
4208 v3s16 p = blockpos + v3s16(0,0,0);
4209 MapBlock *b = getBlockNoCreate(p);
4210 b->updateMesh(daynight_ratio);
4211 //b->setMeshExpired(true);
4213 catch(InvalidPositionException &e){}
4216 v3s16 p = blockpos + v3s16(-1,0,0);
4217 MapBlock *b = getBlockNoCreate(p);
4218 b->updateMesh(daynight_ratio);
4219 //b->setMeshExpired(true);
4221 catch(InvalidPositionException &e){}
4223 v3s16 p = blockpos + v3s16(0,-1,0);
4224 MapBlock *b = getBlockNoCreate(p);
4225 b->updateMesh(daynight_ratio);
4226 //b->setMeshExpired(true);
4228 catch(InvalidPositionException &e){}
4230 v3s16 p = blockpos + v3s16(0,0,-1);
4231 MapBlock *b = getBlockNoCreate(p);
4232 b->updateMesh(daynight_ratio);
4233 //b->setMeshExpired(true);
4235 catch(InvalidPositionException &e){}
4240 Update mesh of block in which the node is, and if the node is at the
4241 leading edge, update the appropriate leading blocks too.
4243 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4251 v3s16 blockposes[4];
4252 for(u32 i=0; i<4; i++)
4254 v3s16 np = nodepos + dirs[i];
4255 blockposes[i] = getNodeBlockPos(np);
4256 // Don't update mesh of block if it has been done already
4257 bool already_updated = false;
4258 for(u32 j=0; j<i; j++)
4260 if(blockposes[j] == blockposes[i])
4262 already_updated = true;
4269 MapBlock *b = getBlockNoCreate(blockposes[i]);
4270 b->updateMesh(daynight_ratio);
4275 void ClientMap::PrintInfo(std::ostream &out)
4286 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4291 MapVoxelManipulator::~MapVoxelManipulator()
4293 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4297 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4299 TimeTaker timer1("emerge", &emerge_time);
4301 // Units of these are MapBlocks
4302 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4303 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4305 VoxelArea block_area_nodes
4306 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4308 addArea(block_area_nodes);
4310 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4311 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4312 for(s32 x=p_min.X; x<=p_max.X; x++)
4315 core::map<v3s16, bool>::Node *n;
4316 n = m_loaded_blocks.find(p);
4320 bool block_data_inexistent = false;
4323 TimeTaker timer1("emerge load", &emerge_load_time);
4325 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4326 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4328 a.print(infostream);
4329 infostream<<std::endl;*/
4331 MapBlock *block = m_map->getBlockNoCreate(p);
4332 if(block->isDummy())
4333 block_data_inexistent = true;
4335 block->copyTo(*this);
4337 catch(InvalidPositionException &e)
4339 block_data_inexistent = true;
4342 if(block_data_inexistent)
4344 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4345 // Fill with VOXELFLAG_INEXISTENT
4346 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4347 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4349 s32 i = m_area.index(a.MinEdge.X,y,z);
4350 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4354 m_loaded_blocks.insert(p, !block_data_inexistent);
4357 //infostream<<"emerge done"<<std::endl;
4361 SUGG: Add an option to only update eg. water and air nodes.
4362 This will make it interfere less with important stuff if
4365 void MapVoxelManipulator::blitBack
4366 (core::map<v3s16, MapBlock*> & modified_blocks)
4368 if(m_area.getExtent() == v3s16(0,0,0))
4371 //TimeTaker timer1("blitBack");
4373 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4374 <<m_loaded_blocks.size()<<std::endl;*/
4377 Initialize block cache
4379 v3s16 blockpos_last;
4380 MapBlock *block = NULL;
4381 bool block_checked_in_modified = false;
4383 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4384 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4385 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4389 u8 f = m_flags[m_area.index(p)];
4390 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4393 MapNode &n = m_data[m_area.index(p)];
4395 v3s16 blockpos = getNodeBlockPos(p);
4400 if(block == NULL || blockpos != blockpos_last){
4401 block = m_map->getBlockNoCreate(blockpos);
4402 blockpos_last = blockpos;
4403 block_checked_in_modified = false;
4406 // Calculate relative position in block
4407 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4409 // Don't continue if nothing has changed here
4410 if(block->getNode(relpos) == n)
4413 //m_map->setNode(m_area.MinEdge + p, n);
4414 block->setNode(relpos, n);
4417 Make sure block is in modified_blocks
4419 if(block_checked_in_modified == false)
4421 modified_blocks[blockpos] = block;
4422 block_checked_in_modified = true;
4425 catch(InvalidPositionException &e)
4431 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4432 MapVoxelManipulator(map),
4433 m_create_area(false)
4437 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4441 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4443 // Just create the area so that it can be pointed to
4444 VoxelManipulator::emerge(a, caller_id);
4447 void ManualMapVoxelManipulator::initialEmerge(
4448 v3s16 blockpos_min, v3s16 blockpos_max)
4450 TimeTaker timer1("initialEmerge", &emerge_time);
4452 // Units of these are MapBlocks
4453 v3s16 p_min = blockpos_min;
4454 v3s16 p_max = blockpos_max;
4456 VoxelArea block_area_nodes
4457 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4459 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4462 infostream<<"initialEmerge: area: ";
4463 block_area_nodes.print(infostream);
4464 infostream<<" ("<<size_MB<<"MB)";
4465 infostream<<std::endl;
4468 addArea(block_area_nodes);
4470 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4471 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4472 for(s32 x=p_min.X; x<=p_max.X; x++)
4475 core::map<v3s16, bool>::Node *n;
4476 n = m_loaded_blocks.find(p);
4480 bool block_data_inexistent = false;
4483 TimeTaker timer1("emerge load", &emerge_load_time);
4485 MapBlock *block = m_map->getBlockNoCreate(p);
4486 if(block->isDummy())
4487 block_data_inexistent = true;
4489 block->copyTo(*this);
4491 catch(InvalidPositionException &e)
4493 block_data_inexistent = true;
4496 if(block_data_inexistent)
4499 Mark area inexistent
4501 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4502 // Fill with VOXELFLAG_INEXISTENT
4503 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4504 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4506 s32 i = m_area.index(a.MinEdge.X,y,z);
4507 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4511 m_loaded_blocks.insert(p, !block_data_inexistent);
4515 void ManualMapVoxelManipulator::blitBackAll(
4516 core::map<v3s16, MapBlock*> * modified_blocks)
4518 if(m_area.getExtent() == v3s16(0,0,0))
4522 Copy data of all blocks
4524 for(core::map<v3s16, bool>::Iterator
4525 i = m_loaded_blocks.getIterator();
4526 i.atEnd() == false; i++)
4528 v3s16 p = i.getNode()->getKey();
4529 bool existed = i.getNode()->getValue();
4530 if(existed == false)
4532 // The Great Bug was found using this
4533 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4534 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4538 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4541 infostream<<"WARNING: "<<__FUNCTION_NAME
4542 <<": got NULL block "
4543 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4548 block->copyFrom(*this);
4551 modified_blocks->insert(p, block);