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(errorstream);
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 sources[num_sources++] = nb;
1689 case LIQUID_FLOWING:
1690 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1691 if (liquid_kind == CONTENT_AIR)
1692 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1693 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1694 neutrals[num_neutrals++] = nb;
1696 flows[num_flows++] = nb;
1697 if (nb.t == NEIGHBOR_LOWER)
1698 flowing_down = true;
1705 decide on the type (and possibly level) of the current node
1707 content_t new_node_content;
1708 s8 new_node_level = -1;
1709 s8 max_node_level = -1;
1710 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1711 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1712 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1713 // it's perfectly safe to use liquid_kind here to determine the new node content.
1714 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1715 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1716 // liquid_kind is set properly, see above
1717 new_node_content = liquid_kind;
1718 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1720 // no surrounding sources, so get the maximum level that can flow into this node
1721 for (u16 i = 0; i < num_flows; i++) {
1722 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1723 switch (flows[i].t) {
1724 case NEIGHBOR_UPPER:
1725 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1726 max_node_level = LIQUID_LEVEL_MAX;
1727 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1728 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1729 } else if (nb_liquid_level > max_node_level)
1730 max_node_level = nb_liquid_level;
1732 case NEIGHBOR_LOWER:
1734 case NEIGHBOR_SAME_LEVEL:
1735 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1736 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1737 max_node_level = nb_liquid_level - 1;
1743 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1744 if (viscosity > 1 && max_node_level != liquid_level) {
1745 // amount to gain, limited by viscosity
1746 // must be at least 1 in absolute value
1747 s8 level_inc = max_node_level - liquid_level;
1748 if (level_inc < -viscosity || level_inc > viscosity)
1749 new_node_level = liquid_level + level_inc/viscosity;
1750 else if (level_inc < 0)
1751 new_node_level = liquid_level - 1;
1752 else if (level_inc > 0)
1753 new_node_level = liquid_level + 1;
1754 if (new_node_level != max_node_level)
1755 must_reflow.push_back(p0);
1757 new_node_level = max_node_level;
1759 if (new_node_level >= 0)
1760 new_node_content = liquid_kind;
1762 new_node_content = CONTENT_AIR;
1767 check if anything has changed. if not, just continue with the next node.
1769 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1770 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1771 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1777 update the current node
1779 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1780 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1781 // set level to last 3 bits, flowing down bit to 4th bit
1782 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1784 // set the liquid level and flow bit to 0
1785 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1787 n0.setContent(new_node_content);
1789 v3s16 blockpos = getNodeBlockPos(p0);
1790 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1792 modified_blocks.insert(blockpos, block);
1793 // If node emits light, MapBlock requires lighting update
1794 if(nodemgr->get(n0).light_source != 0)
1795 lighting_modified_blocks[block->getPos()] = block;
1799 enqueue neighbors for update if neccessary
1801 switch (nodemgr->get(n0.getContent()).liquid_type) {
1803 case LIQUID_FLOWING:
1804 // make sure source flows into all neighboring nodes
1805 for (u16 i = 0; i < num_flows; i++)
1806 if (flows[i].t != NEIGHBOR_UPPER)
1807 m_transforming_liquid.push_back(flows[i].p);
1808 for (u16 i = 0; i < num_airs; i++)
1809 if (airs[i].t != NEIGHBOR_UPPER)
1810 m_transforming_liquid.push_back(airs[i].p);
1813 // this flow has turned to air; neighboring flows might need to do the same
1814 for (u16 i = 0; i < num_flows; i++)
1815 m_transforming_liquid.push_back(flows[i].p);
1819 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1820 while (must_reflow.size() > 0)
1821 m_transforming_liquid.push_back(must_reflow.pop_front());
1822 updateLighting(lighting_modified_blocks, modified_blocks);
1825 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1827 v3s16 blockpos = getNodeBlockPos(p);
1828 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1829 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1831 infostream<<"Map::getNodeMetadata(): Need to emerge "
1832 <<PP(blockpos)<<std::endl;
1833 block = emergeBlock(blockpos, false);
1837 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1841 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1845 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1847 v3s16 blockpos = getNodeBlockPos(p);
1848 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1849 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1851 infostream<<"Map::setNodeMetadata(): Need to emerge "
1852 <<PP(blockpos)<<std::endl;
1853 block = emergeBlock(blockpos, false);
1857 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1861 block->m_node_metadata->set(p_rel, meta);
1864 void Map::removeNodeMetadata(v3s16 p)
1866 v3s16 blockpos = getNodeBlockPos(p);
1867 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1868 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1871 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1875 block->m_node_metadata->remove(p_rel);
1878 void Map::nodeMetadataStep(float dtime,
1879 core::map<v3s16, MapBlock*> &changed_blocks)
1883 Currently there is no way to ensure that all the necessary
1884 blocks are loaded when this is run. (They might get unloaded)
1885 NOTE: ^- Actually, that might not be so. In a quick test it
1886 reloaded a block with a furnace when I walked back to it from
1889 core::map<v2s16, MapSector*>::Iterator si;
1890 si = m_sectors.getIterator();
1891 for(; si.atEnd() == false; si++)
1893 MapSector *sector = si.getNode()->getValue();
1894 core::list< MapBlock * > sectorblocks;
1895 sector->getBlocks(sectorblocks);
1896 core::list< MapBlock * >::Iterator i;
1897 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1899 MapBlock *block = *i;
1900 bool changed = block->m_node_metadata->step(dtime);
1902 changed_blocks[block->getPos()] = block;
1911 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1912 Map(dout_server, gamedef),
1914 m_map_metadata_changed(true),
1916 m_database_read(NULL),
1917 m_database_write(NULL)
1919 infostream<<__FUNCTION_NAME<<std::endl;
1921 //m_chunksize = 8; // Takes a few seconds
1923 if (g_settings->get("fixed_map_seed").empty())
1925 m_seed = (((u64)(myrand()%0xffff)<<0)
1926 + ((u64)(myrand()%0xffff)<<16)
1927 + ((u64)(myrand()%0xffff)<<32)
1928 + ((u64)(myrand()%0xffff)<<48));
1932 m_seed = g_settings->getU64("fixed_map_seed");
1936 Experimental and debug stuff
1943 Try to load map; if not found, create a new one.
1946 m_savedir = savedir;
1947 m_map_saving_enabled = false;
1951 // If directory exists, check contents and load if possible
1952 if(fs::PathExists(m_savedir))
1954 // If directory is empty, it is safe to save into it.
1955 if(fs::GetDirListing(m_savedir).size() == 0)
1957 infostream<<"Server: Empty save directory is valid."
1959 m_map_saving_enabled = true;
1964 // Load map metadata (seed, chunksize)
1967 catch(FileNotGoodException &e){
1968 infostream<<"WARNING: Could not load map metadata"
1969 //<<" Disabling chunk-based generator."
1975 // Load chunk metadata
1978 catch(FileNotGoodException &e){
1979 infostream<<"WARNING: Could not load chunk metadata."
1980 <<" Disabling chunk-based generator."
1985 /*infostream<<"Server: Successfully loaded chunk "
1986 "metadata and sector (0,0) from "<<savedir<<
1987 ", assuming valid save directory."
1990 infostream<<"Server: Successfully loaded map "
1991 <<"and chunk metadata from "<<savedir
1992 <<", assuming valid save directory."
1995 m_map_saving_enabled = true;
1996 // Map loaded, not creating new one
2000 // If directory doesn't exist, it is safe to save to it
2002 m_map_saving_enabled = true;
2005 catch(std::exception &e)
2007 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2008 <<", exception: "<<e.what()<<std::endl;
2009 infostream<<"Please remove the map or fix it."<<std::endl;
2010 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2013 infostream<<"Initializing new map."<<std::endl;
2015 // Create zero sector
2016 emergeSector(v2s16(0,0));
2018 // Initially write whole map
2019 save(MOD_STATE_CLEAN);
2022 ServerMap::~ServerMap()
2024 infostream<<__FUNCTION_NAME<<std::endl;
2028 if(m_map_saving_enabled)
2030 // Save only changed parts
2031 save(MOD_STATE_WRITE_AT_UNLOAD);
2032 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2036 infostream<<"Server: map not saved"<<std::endl;
2039 catch(std::exception &e)
2041 infostream<<"Server: Failed to save map to "<<m_savedir
2042 <<", exception: "<<e.what()<<std::endl;
2046 Close database if it was opened
2049 sqlite3_finalize(m_database_read);
2050 if(m_database_write)
2051 sqlite3_finalize(m_database_write);
2053 sqlite3_close(m_database);
2059 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2060 for(; i.atEnd() == false; i++)
2062 MapChunk *chunk = i.getNode()->getValue();
2068 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2070 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2071 if(enable_mapgen_debug_info)
2072 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2073 <<blockpos.Z<<")"<<std::endl;
2075 // Do nothing if not inside limits (+-1 because of neighbors)
2076 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2077 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2083 data->no_op = false;
2084 data->seed = m_seed;
2085 data->blockpos = blockpos;
2086 data->nodedef = m_gamedef->ndef();
2089 Create the whole area of this and the neighboring blocks
2092 //TimeTaker timer("initBlockMake() create area");
2094 for(s16 x=-1; x<=1; x++)
2095 for(s16 z=-1; z<=1; z++)
2097 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2098 // Sector metadata is loaded from disk if not already loaded.
2099 ServerMapSector *sector = createSector(sectorpos);
2102 for(s16 y=-1; y<=1; y++)
2104 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2105 //MapBlock *block = createBlock(p);
2106 // 1) get from memory, 2) load from disk
2107 MapBlock *block = emergeBlock(p, false);
2108 // 3) create a blank one
2111 block = createBlock(p);
2114 Block gets sunlight if this is true.
2116 Refer to the map generator heuristics.
2118 bool ug = mapgen::block_is_underground(data->seed, p);
2119 block->setIsUnderground(ug);
2122 // Lighting will not be valid after make_chunk is called
2123 block->setLightingExpired(true);
2124 // Lighting will be calculated
2125 //block->setLightingExpired(false);
2131 Now we have a big empty area.
2133 Make a ManualMapVoxelManipulator that contains this and the
2137 // The area that contains this block and it's neighbors
2138 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2139 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2141 data->vmanip = new ManualMapVoxelManipulator(this);
2142 //data->vmanip->setMap(this);
2146 //TimeTaker timer("initBlockMake() initialEmerge");
2147 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2150 // Data is ready now.
2153 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2154 core::map<v3s16, MapBlock*> &changed_blocks)
2156 v3s16 blockpos = data->blockpos;
2157 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2158 <<blockpos.Z<<")"<<std::endl;*/
2162 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2166 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2168 /*infostream<<"Resulting vmanip:"<<std::endl;
2169 data->vmanip.print(infostream);*/
2171 // Make sure affected blocks are loaded
2172 for(s16 x=-1; x<=1; x++)
2173 for(s16 z=-1; z<=1; z++)
2174 for(s16 y=-1; y<=1; y++)
2176 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2177 // Load from disk if not already in memory
2178 emergeBlock(p, false);
2182 Blit generated stuff to map
2183 NOTE: blitBackAll adds nearly everything to changed_blocks
2187 //TimeTaker timer("finishBlockMake() blitBackAll");
2188 data->vmanip->blitBackAll(&changed_blocks);
2191 if(enable_mapgen_debug_info)
2192 infostream<<"finishBlockMake: changed_blocks.size()="
2193 <<changed_blocks.size()<<std::endl;
2196 Copy transforming liquid information
2198 while(data->transforming_liquid.size() > 0)
2200 v3s16 p = data->transforming_liquid.pop_front();
2201 m_transforming_liquid.push_back(p);
2207 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2211 Set is_underground flag for lighting with sunlight.
2213 Refer to map generator heuristics.
2215 NOTE: This is done in initChunkMake
2217 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2221 Add sunlight to central block.
2222 This makes in-dark-spawning monsters to not flood the whole thing.
2223 Do not spread the light, though.
2225 /*core::map<v3s16, bool> light_sources;
2226 bool black_air_left = false;
2227 block->propagateSunlight(light_sources, true, &black_air_left);*/
2230 NOTE: Lighting and object adding shouldn't really be here, but
2231 lighting is a bit tricky to move properly to makeBlock.
2232 TODO: Do this the right way anyway, that is, move it to makeBlock.
2233 - There needs to be some way for makeBlock to report back if
2234 the lighting update is going further down because of the
2235 new block blocking light
2240 NOTE: This takes ~60ms, TODO: Investigate why
2243 TimeTaker t("finishBlockMake lighting update");
2245 core::map<v3s16, MapBlock*> lighting_update_blocks;
2248 lighting_update_blocks.insert(block->getPos(), block);
2253 v3s16 p = block->getPos()+v3s16(x,1,z);
2254 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2258 // All modified blocks
2259 // NOTE: Should this be done? If this is not done, then the lighting
2260 // of the others will be updated in a different place, one by one, i
2261 // think... or they might not? Well, at least they are left marked as
2262 // "lighting expired"; it seems that is not handled at all anywhere,
2263 // so enabling this will slow it down A LOT because otherwise it
2264 // would not do this at all. This causes the black trees.
2265 for(core::map<v3s16, MapBlock*>::Iterator
2266 i = changed_blocks.getIterator();
2267 i.atEnd() == false; i++)
2269 lighting_update_blocks.insert(i.getNode()->getKey(),
2270 i.getNode()->getValue());
2272 /*// Also force-add all the upmost blocks for proper sunlight
2273 for(s16 x=-1; x<=1; x++)
2274 for(s16 z=-1; z<=1; z++)
2276 v3s16 p = block->getPos()+v3s16(x,1,z);
2277 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2280 updateLighting(lighting_update_blocks, changed_blocks);
2283 Set lighting to non-expired state in all of them.
2284 This is cheating, but it is not fast enough if all of them
2285 would actually be updated.
2287 for(s16 x=-1; x<=1; x++)
2288 for(s16 y=-1; y<=1; y++)
2289 for(s16 z=-1; z<=1; z++)
2291 v3s16 p = block->getPos()+v3s16(x,y,z);
2292 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2295 if(enable_mapgen_debug_info == false)
2296 t.stop(true); // Hide output
2300 Add random objects to block
2302 mapgen::add_random_objects(block);
2305 Go through changed blocks
2307 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2308 i.atEnd() == false; i++)
2310 MapBlock *block = i.getNode()->getValue();
2313 Update day/night difference cache of the MapBlocks
2315 block->updateDayNightDiff();
2317 Set block as modified
2319 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2320 "finishBlockMake updateDayNightDiff");
2324 Set central block as generated
2326 block->setGenerated(true);
2329 Save changed parts of map
2330 NOTE: Will be saved later.
2332 //save(MOD_STATE_WRITE_AT_UNLOAD);
2334 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2335 <<blockpos.Z<<")"<<std::endl;*/
2337 if(enable_mapgen_debug_info)
2340 Analyze resulting blocks
2342 for(s16 x=-1; x<=1; x++)
2343 for(s16 y=-1; y<=1; y++)
2344 for(s16 z=-1; z<=1; z++)
2346 v3s16 p = block->getPos()+v3s16(x,y,z);
2347 MapBlock *block = getBlockNoCreateNoEx(p);
2349 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2350 infostream<<"Generated "<<spos<<": "
2351 <<analyze_block(block)<<std::endl;
2359 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2361 DSTACKF("%s: p2d=(%d,%d)",
2366 Check if it exists already in memory
2368 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2373 Try to load it from disk (with blocks)
2375 //if(loadSectorFull(p2d) == true)
2378 Try to load metadata from disk
2381 if(loadSectorMeta(p2d) == true)
2383 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2386 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2387 throw InvalidPositionException("");
2393 Do not create over-limit
2395 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2396 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2397 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2398 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2399 throw InvalidPositionException("createSector(): pos. over limit");
2402 Generate blank sector
2405 sector = new ServerMapSector(this, p2d, m_gamedef);
2407 // Sector position on map in nodes
2408 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2413 m_sectors.insert(p2d, sector);
2419 This is a quick-hand function for calling makeBlock().
2421 MapBlock * ServerMap::generateBlock(
2423 core::map<v3s16, MapBlock*> &modified_blocks
2426 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2428 /*infostream<<"generateBlock(): "
2429 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2432 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2434 TimeTaker timer("generateBlock");
2436 //MapBlock *block = original_dummy;
2438 v2s16 p2d(p.X, p.Z);
2439 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2442 Do not generate over-limit
2444 if(blockpos_over_limit(p))
2446 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2447 throw InvalidPositionException("generateBlock(): pos. over limit");
2451 Create block make data
2453 mapgen::BlockMakeData data;
2454 initBlockMake(&data, p);
2460 TimeTaker t("mapgen::make_block()");
2461 mapgen::make_block(&data);
2463 if(enable_mapgen_debug_info == false)
2464 t.stop(true); // Hide output
2468 Blit data back on map, update lighting, add mobs and whatever this does
2470 finishBlockMake(&data, modified_blocks);
2475 MapBlock *block = getBlockNoCreateNoEx(p);
2483 bool erroneus_content = false;
2484 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2485 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2486 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2489 MapNode n = block->getNode(p);
2490 if(n.getContent() == CONTENT_IGNORE)
2492 infostream<<"CONTENT_IGNORE at "
2493 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2495 erroneus_content = true;
2499 if(erroneus_content)
2508 Generate a completely empty block
2512 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2513 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2515 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2518 n.setContent(CONTENT_AIR);
2519 block->setNode(v3s16(x0,y0,z0), n);
2525 if(enable_mapgen_debug_info == false)
2526 timer.stop(true); // Hide output
2531 MapBlock * ServerMap::createBlock(v3s16 p)
2533 DSTACKF("%s: p=(%d,%d,%d)",
2534 __FUNCTION_NAME, p.X, p.Y, p.Z);
2537 Do not create over-limit
2539 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2540 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2541 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2542 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2543 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2544 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2545 throw InvalidPositionException("createBlock(): pos. over limit");
2547 v2s16 p2d(p.X, p.Z);
2550 This will create or load a sector if not found in memory.
2551 If block exists on disk, it will be loaded.
2553 NOTE: On old save formats, this will be slow, as it generates
2554 lighting on blocks for them.
2556 ServerMapSector *sector;
2558 sector = (ServerMapSector*)createSector(p2d);
2559 assert(sector->getId() == MAPSECTOR_SERVER);
2561 catch(InvalidPositionException &e)
2563 infostream<<"createBlock: createSector() failed"<<std::endl;
2567 NOTE: This should not be done, or at least the exception
2568 should not be passed on as std::exception, because it
2569 won't be catched at all.
2571 /*catch(std::exception &e)
2573 infostream<<"createBlock: createSector() failed: "
2574 <<e.what()<<std::endl;
2579 Try to get a block from the sector
2582 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2585 if(block->isDummy())
2590 block = sector->createBlankBlock(block_y);
2594 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2596 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2598 p.X, p.Y, p.Z, allow_generate);
2601 MapBlock *block = getBlockNoCreateNoEx(p);
2602 if(block && block->isDummy() == false)
2607 MapBlock *block = loadBlock(p);
2614 core::map<v3s16, MapBlock*> modified_blocks;
2615 MapBlock *block = generateBlock(p, modified_blocks);
2619 event.type = MEET_OTHER;
2622 // Copy modified_blocks to event
2623 for(core::map<v3s16, MapBlock*>::Iterator
2624 i = modified_blocks.getIterator();
2625 i.atEnd()==false; i++)
2627 event.modified_blocks.insert(i.getNode()->getKey(), false);
2631 dispatchEvent(&event);
2640 s16 ServerMap::findGroundLevel(v2s16 p2d)
2644 Uh, just do something random...
2646 // Find existing map from top to down
2649 v3s16 p(p2d.X, max, p2d.Y);
2650 for(; p.Y>min; p.Y--)
2652 MapNode n = getNodeNoEx(p);
2653 if(n.getContent() != CONTENT_IGNORE)
2658 // If this node is not air, go to plan b
2659 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2661 // Search existing walkable and return it
2662 for(; p.Y>min; p.Y--)
2664 MapNode n = getNodeNoEx(p);
2665 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2674 Determine from map generator noise functions
2677 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2680 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2681 //return (s16)level;
2684 void ServerMap::createDatabase() {
2687 e = sqlite3_exec(m_database,
2688 "CREATE TABLE IF NOT EXISTS `blocks` ("
2689 "`pos` INT NOT NULL PRIMARY KEY,"
2692 , NULL, NULL, NULL);
2693 if(e == SQLITE_ABORT)
2694 throw FileNotGoodException("Could not create database structure");
2696 infostream<<"Server: Database structure was created";
2699 void ServerMap::verifyDatabase() {
2704 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2705 bool needs_create = false;
2709 Open the database connection
2712 createDirs(m_savedir);
2714 if(!fs::PathExists(dbp))
2715 needs_create = true;
2717 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2718 if(d != SQLITE_OK) {
2719 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2720 throw FileNotGoodException("Cannot open database file");
2726 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2727 if(d != SQLITE_OK) {
2728 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2729 throw FileNotGoodException("Cannot prepare read statement");
2732 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2733 if(d != SQLITE_OK) {
2734 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2735 throw FileNotGoodException("Cannot prepare write statement");
2738 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2739 if(d != SQLITE_OK) {
2740 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2741 throw FileNotGoodException("Cannot prepare read statement");
2744 infostream<<"Server: Database opened"<<std::endl;
2748 bool ServerMap::loadFromFolders() {
2749 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2754 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2755 return (sqlite3_int64)pos.Z*16777216 +
2756 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2759 void ServerMap::createDirs(std::string path)
2761 if(fs::CreateAllDirs(path) == false)
2763 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2764 <<"\""<<path<<"\""<<std::endl;
2765 throw BaseException("ServerMap failed to create directory");
2769 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2775 snprintf(cc, 9, "%.4x%.4x",
2776 (unsigned int)pos.X&0xffff,
2777 (unsigned int)pos.Y&0xffff);
2779 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2781 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2782 (unsigned int)pos.X&0xfff,
2783 (unsigned int)pos.Y&0xfff);
2785 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2791 v2s16 ServerMap::getSectorPos(std::string dirname)
2795 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2796 assert(spos != std::string::npos);
2797 if(dirname.size() - spos == 8)
2800 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2802 else if(dirname.size() - spos == 3)
2805 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2806 // Sign-extend the 12 bit values up to 16 bits...
2807 if(x&0x800) x|=0xF000;
2808 if(y&0x800) y|=0xF000;
2815 v2s16 pos((s16)x, (s16)y);
2819 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2821 v2s16 p2d = getSectorPos(sectordir);
2823 if(blockfile.size() != 4){
2824 throw InvalidFilenameException("Invalid block filename");
2827 int r = sscanf(blockfile.c_str(), "%4x", &y);
2829 throw InvalidFilenameException("Invalid block filename");
2830 return v3s16(p2d.X, y, p2d.Y);
2833 std::string ServerMap::getBlockFilename(v3s16 p)
2836 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2840 void ServerMap::save(ModifiedState save_level)
2842 DSTACK(__FUNCTION_NAME);
2843 if(m_map_saving_enabled == false)
2845 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2849 if(save_level == MOD_STATE_CLEAN)
2850 infostream<<"ServerMap: Saving whole map, this can take time."
2853 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2858 // Profile modified reasons
2859 Profiler modprofiler;
2861 u32 sector_meta_count = 0;
2862 u32 block_count = 0;
2863 u32 block_count_all = 0; // Number of blocks in memory
2865 // Don't do anything with sqlite unless something is really saved
2866 bool save_started = false;
2868 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2869 for(; i.atEnd() == false; i++)
2871 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2872 assert(sector->getId() == MAPSECTOR_SERVER);
2874 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2876 saveSectorMeta(sector);
2877 sector_meta_count++;
2879 core::list<MapBlock*> blocks;
2880 sector->getBlocks(blocks);
2881 core::list<MapBlock*>::Iterator j;
2883 for(j=blocks.begin(); j!=blocks.end(); j++)
2885 MapBlock *block = *j;
2889 if(block->getModified() >= save_level)
2894 save_started = true;
2897 modprofiler.add(block->getModifiedReason(), 1);
2902 /*infostream<<"ServerMap: Written block ("
2903 <<block->getPos().X<<","
2904 <<block->getPos().Y<<","
2905 <<block->getPos().Z<<")"
2914 Only print if something happened or saved whole map
2916 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2917 || block_count != 0)
2919 infostream<<"ServerMap: Written: "
2920 <<sector_meta_count<<" sector metadata files, "
2921 <<block_count<<" block files"
2922 <<", "<<block_count_all<<" blocks in memory."
2924 PrintInfo(infostream); // ServerMap/ClientMap:
2925 infostream<<"Blocks modified by: "<<std::endl;
2926 modprofiler.print(infostream);
2930 static s32 unsignedToSigned(s32 i, s32 max_positive)
2932 if(i < max_positive)
2935 return i - 2*max_positive;
2938 // modulo of a negative number does not work consistently in C
2939 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2943 return mod - ((-i) % mod);
2946 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2948 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2950 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2952 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2953 return v3s16(x,y,z);
2956 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2958 if(loadFromFolders()){
2959 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2960 <<"all blocks that are stored in flat files"<<std::endl;
2966 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2968 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2969 v3s16 p = getIntegerAsBlock(block_i);
2970 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2976 void ServerMap::saveMapMeta()
2978 DSTACK(__FUNCTION_NAME);
2980 infostream<<"ServerMap::saveMapMeta(): "
2984 createDirs(m_savedir);
2986 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2987 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2988 if(os.good() == false)
2990 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2991 <<"could not open"<<fullpath<<std::endl;
2992 throw FileNotGoodException("Cannot open chunk metadata");
2996 params.setU64("seed", m_seed);
2998 params.writeLines(os);
3000 os<<"[end_of_params]\n";
3002 m_map_metadata_changed = false;
3005 void ServerMap::loadMapMeta()
3007 DSTACK(__FUNCTION_NAME);
3009 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3012 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3013 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3014 if(is.good() == false)
3016 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3017 <<"could not open"<<fullpath<<std::endl;
3018 throw FileNotGoodException("Cannot open map metadata");
3026 throw SerializationError
3027 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3029 std::getline(is, line);
3030 std::string trimmedline = trim(line);
3031 if(trimmedline == "[end_of_params]")
3033 params.parseConfigLine(line);
3036 m_seed = params.getU64("seed");
3038 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3041 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3043 DSTACK(__FUNCTION_NAME);
3044 // Format used for writing
3045 u8 version = SER_FMT_VER_HIGHEST;
3047 v2s16 pos = sector->getPos();
3048 std::string dir = getSectorDir(pos);
3051 std::string fullpath = dir + DIR_DELIM + "meta";
3052 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3053 if(o.good() == false)
3054 throw FileNotGoodException("Cannot open sector metafile");
3056 sector->serialize(o, version);
3058 sector->differs_from_disk = false;
3061 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3063 DSTACK(__FUNCTION_NAME);
3065 v2s16 p2d = getSectorPos(sectordir);
3067 ServerMapSector *sector = NULL;
3069 std::string fullpath = sectordir + DIR_DELIM + "meta";
3070 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3071 if(is.good() == false)
3073 // If the directory exists anyway, it probably is in some old
3074 // format. Just go ahead and create the sector.
3075 if(fs::PathExists(sectordir))
3077 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3078 <<fullpath<<" doesn't exist but directory does."
3079 <<" Continuing with a sector with no metadata."
3081 sector = new ServerMapSector(this, p2d, m_gamedef);
3082 m_sectors.insert(p2d, sector);
3086 throw FileNotGoodException("Cannot open sector metafile");
3091 sector = ServerMapSector::deSerialize
3092 (is, this, p2d, m_sectors, m_gamedef);
3094 saveSectorMeta(sector);
3097 sector->differs_from_disk = false;
3102 bool ServerMap::loadSectorMeta(v2s16 p2d)
3104 DSTACK(__FUNCTION_NAME);
3106 MapSector *sector = NULL;
3108 // The directory layout we're going to load from.
3109 // 1 - original sectors/xxxxzzzz/
3110 // 2 - new sectors2/xxx/zzz/
3111 // If we load from anything but the latest structure, we will
3112 // immediately save to the new one, and remove the old.
3114 std::string sectordir1 = getSectorDir(p2d, 1);
3115 std::string sectordir;
3116 if(fs::PathExists(sectordir1))
3118 sectordir = sectordir1;
3123 sectordir = getSectorDir(p2d, 2);
3127 sector = loadSectorMeta(sectordir, loadlayout != 2);
3129 catch(InvalidFilenameException &e)
3133 catch(FileNotGoodException &e)
3137 catch(std::exception &e)
3146 bool ServerMap::loadSectorFull(v2s16 p2d)
3148 DSTACK(__FUNCTION_NAME);
3150 MapSector *sector = NULL;
3152 // The directory layout we're going to load from.
3153 // 1 - original sectors/xxxxzzzz/
3154 // 2 - new sectors2/xxx/zzz/
3155 // If we load from anything but the latest structure, we will
3156 // immediately save to the new one, and remove the old.
3158 std::string sectordir1 = getSectorDir(p2d, 1);
3159 std::string sectordir;
3160 if(fs::PathExists(sectordir1))
3162 sectordir = sectordir1;
3167 sectordir = getSectorDir(p2d, 2);
3171 sector = loadSectorMeta(sectordir, loadlayout != 2);
3173 catch(InvalidFilenameException &e)
3177 catch(FileNotGoodException &e)
3181 catch(std::exception &e)
3189 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3191 std::vector<fs::DirListNode>::iterator i2;
3192 for(i2=list2.begin(); i2!=list2.end(); i2++)
3198 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3200 catch(InvalidFilenameException &e)
3202 // This catches unknown crap in directory
3208 infostream<<"Sector converted to new layout - deleting "<<
3209 sectordir1<<std::endl;
3210 fs::RecursiveDelete(sectordir1);
3217 void ServerMap::beginSave() {
3219 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3220 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3223 void ServerMap::endSave() {
3225 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3226 infostream<<"WARNING: endSave() failed, map might not have saved.";
3229 void ServerMap::saveBlock(MapBlock *block)
3231 DSTACK(__FUNCTION_NAME);
3233 Dummy blocks are not written
3235 if(block->isDummy())
3237 /*v3s16 p = block->getPos();
3238 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3239 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3243 // Format used for writing
3244 u8 version = SER_FMT_VER_HIGHEST;
3246 v3s16 p3d = block->getPos();
3250 v2s16 p2d(p3d.X, p3d.Z);
3251 std::string sectordir = getSectorDir(p2d);
3253 createDirs(sectordir);
3255 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3256 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3257 if(o.good() == false)
3258 throw FileNotGoodException("Cannot open block data");
3261 [0] u8 serialization version
3267 std::ostringstream o(std::ios_base::binary);
3269 o.write((char*)&version, 1);
3272 block->serialize(o, version);
3274 // Write extra data stored on disk
3275 block->serializeDiskExtra(o, version);
3277 // Write block to database
3279 std::string tmp = o.str();
3280 const char *bytes = tmp.c_str();
3282 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3283 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3284 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3285 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3286 int written = sqlite3_step(m_database_write);
3287 if(written != SQLITE_DONE)
3288 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3289 <<sqlite3_errmsg(m_database)<<std::endl;
3290 // Make ready for later reuse
3291 sqlite3_reset(m_database_write);
3293 // We just wrote it to the disk so clear modified flag
3294 block->resetModified();
3297 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3299 DSTACK(__FUNCTION_NAME);
3301 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3304 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3305 if(is.good() == false)
3306 throw FileNotGoodException("Cannot open block file");
3308 v3s16 p3d = getBlockPos(sectordir, blockfile);
3309 v2s16 p2d(p3d.X, p3d.Z);
3311 assert(sector->getPos() == p2d);
3313 u8 version = SER_FMT_VER_INVALID;
3314 is.read((char*)&version, 1);
3317 throw SerializationError("ServerMap::loadBlock(): Failed"
3318 " to read MapBlock version");
3320 /*u32 block_size = MapBlock::serializedLength(version);
3321 SharedBuffer<u8> data(block_size);
3322 is.read((char*)*data, block_size);*/
3324 // This will always return a sector because we're the server
3325 //MapSector *sector = emergeSector(p2d);
3327 MapBlock *block = NULL;
3328 bool created_new = false;
3329 block = sector->getBlockNoCreateNoEx(p3d.Y);
3332 block = sector->createBlankBlockNoInsert(p3d.Y);
3337 block->deSerialize(is, version);
3339 // Read extra data stored on disk
3340 block->deSerializeDiskExtra(is, version);
3342 // If it's a new block, insert it to the map
3344 sector->insertBlock(block);
3347 Save blocks loaded in old format in new format
3350 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3354 // Should be in database now, so delete the old file
3355 fs::RecursiveDelete(fullpath);
3358 // We just loaded it from the disk, so it's up-to-date.
3359 block->resetModified();
3362 catch(SerializationError &e)
3364 infostream<<"WARNING: Invalid block data on disk "
3365 <<"fullpath="<<fullpath
3366 <<" (SerializationError). "
3367 <<"what()="<<e.what()
3369 //" Ignoring. A new one will be generated.
3372 // TODO: Backup file; name is in fullpath.
3376 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3378 DSTACK(__FUNCTION_NAME);
3381 std::istringstream is(*blob, std::ios_base::binary);
3383 u8 version = SER_FMT_VER_INVALID;
3384 is.read((char*)&version, 1);
3387 throw SerializationError("ServerMap::loadBlock(): Failed"
3388 " to read MapBlock version");
3390 /*u32 block_size = MapBlock::serializedLength(version);
3391 SharedBuffer<u8> data(block_size);
3392 is.read((char*)*data, block_size);*/
3394 // This will always return a sector because we're the server
3395 //MapSector *sector = emergeSector(p2d);
3397 MapBlock *block = NULL;
3398 bool created_new = false;
3399 block = sector->getBlockNoCreateNoEx(p3d.Y);
3402 block = sector->createBlankBlockNoInsert(p3d.Y);
3407 block->deSerialize(is, version);
3409 // Read extra data stored on disk
3410 block->deSerializeDiskExtra(is, version);
3412 // If it's a new block, insert it to the map
3414 sector->insertBlock(block);
3417 Save blocks loaded in old format in new format
3420 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3421 // Only save if asked to; no need to update version
3425 // We just loaded it from, so it's up-to-date.
3426 block->resetModified();
3429 catch(SerializationError &e)
3431 infostream<<"WARNING: Invalid block data in database "
3432 <<" (SerializationError). "
3433 <<"what()="<<e.what()
3435 //" Ignoring. A new one will be generated.
3438 // TODO: Copy to a backup database.
3442 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3444 DSTACK(__FUNCTION_NAME);
3446 v2s16 p2d(blockpos.X, blockpos.Z);
3448 if(!loadFromFolders()) {
3451 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3452 infostream<<"WARNING: Could not bind block position for load: "
3453 <<sqlite3_errmsg(m_database)<<std::endl;
3454 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3456 Make sure sector is loaded
3458 MapSector *sector = createSector(p2d);
3463 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3464 size_t len = sqlite3_column_bytes(m_database_read, 0);
3466 std::string datastr(data, len);
3468 loadBlock(&datastr, blockpos, sector, false);
3470 sqlite3_step(m_database_read);
3471 // We should never get more than 1 row, so ok to reset
3472 sqlite3_reset(m_database_read);
3474 return getBlockNoCreateNoEx(blockpos);
3476 sqlite3_reset(m_database_read);
3478 // Not found in database, try the files
3481 // The directory layout we're going to load from.
3482 // 1 - original sectors/xxxxzzzz/
3483 // 2 - new sectors2/xxx/zzz/
3484 // If we load from anything but the latest structure, we will
3485 // immediately save to the new one, and remove the old.
3487 std::string sectordir1 = getSectorDir(p2d, 1);
3488 std::string sectordir;
3489 if(fs::PathExists(sectordir1))
3491 sectordir = sectordir1;
3496 sectordir = getSectorDir(p2d, 2);
3500 Make sure sector is loaded
3502 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3506 sector = loadSectorMeta(sectordir, loadlayout != 2);
3508 catch(InvalidFilenameException &e)
3512 catch(FileNotGoodException &e)
3516 catch(std::exception &e)
3523 Make sure file exists
3526 std::string blockfilename = getBlockFilename(blockpos);
3527 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3531 Load block and save it to the database
3533 loadBlock(sectordir, blockfilename, sector, true);
3534 return getBlockNoCreateNoEx(blockpos);
3537 void ServerMap::PrintInfo(std::ostream &out)
3548 ClientMap::ClientMap(
3551 MapDrawControl &control,
3552 scene::ISceneNode* parent,
3553 scene::ISceneManager* mgr,
3556 Map(dout_client, gamedef),
3557 scene::ISceneNode(parent, mgr, id),
3560 m_camera_position(0,0,0),
3561 m_camera_direction(0,0,1),
3564 m_camera_mutex.Init();
3565 assert(m_camera_mutex.IsInitialized());
3567 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3568 BS*1000000,BS*1000000,BS*1000000);
3571 ClientMap::~ClientMap()
3573 /*JMutexAutoLock lock(mesh_mutex);
3582 MapSector * ClientMap::emergeSector(v2s16 p2d)
3584 DSTACK(__FUNCTION_NAME);
3585 // Check that it doesn't exist already
3587 return getSectorNoGenerate(p2d);
3589 catch(InvalidPositionException &e)
3594 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3597 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3598 m_sectors.insert(p2d, sector);
3605 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3607 DSTACK(__FUNCTION_NAME);
3608 ClientMapSector *sector = NULL;
3610 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3612 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3616 sector = (ClientMapSector*)n->getValue();
3617 assert(sector->getId() == MAPSECTOR_CLIENT);
3621 sector = new ClientMapSector(this, p2d);
3623 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3624 m_sectors.insert(p2d, sector);
3628 sector->deSerialize(is);
3632 void ClientMap::OnRegisterSceneNode()
3636 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3637 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3640 ISceneNode::OnRegisterSceneNode();
3643 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3644 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3646 float d0 = (float)BS * p0.getDistanceFrom(p1);
3648 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3650 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3652 for(float s=start_off; s<d0+end_off; s+=step){
3653 v3f pf = p0f + uf * s;
3654 v3s16 p = floatToInt(pf, BS);
3655 MapNode n = map->getNodeNoEx(p);
3656 bool is_transparent = false;
3657 const ContentFeatures &f = nodemgr->get(n);
3658 if(f.solidness == 0)
3659 is_transparent = (f.visual_solidness != 2);
3661 is_transparent = (f.solidness != 2);
3662 if(!is_transparent){
3664 if(count >= needed_count)
3672 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3674 INodeDefManager *nodemgr = m_gamedef->ndef();
3676 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3677 DSTACK(__FUNCTION_NAME);
3679 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3682 if(pass == scene::ESNRP_SOLID)
3683 prefix = "CM: solid: ";
3685 prefix = "CM: transparent: ";
3688 This is called two times per frame, reset on the non-transparent one
3690 if(pass == scene::ESNRP_SOLID)
3692 m_last_drawn_sectors.clear();
3696 Get time for measuring timeout.
3698 Measuring time is very useful for long delays when the
3699 machine is swapping a lot.
3701 int time1 = time(0);
3703 //u32 daynight_ratio = m_client->getDayNightRatio();
3705 m_camera_mutex.Lock();
3706 v3f camera_position = m_camera_position;
3707 v3f camera_direction = m_camera_direction;
3708 f32 camera_fov = m_camera_fov;
3709 m_camera_mutex.Unlock();
3712 Get all blocks and draw all visible ones
3715 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3717 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3719 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3720 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3722 // Take a fair amount as we will be dropping more out later
3723 // Umm... these additions are a bit strange but they are needed.
3725 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3726 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3727 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3729 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3730 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3731 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3733 u32 vertex_count = 0;
3734 u32 meshbuffer_count = 0;
3736 // For limiting number of mesh updates per frame
3737 u32 mesh_update_count = 0;
3739 // Number of blocks in rendering range
3740 u32 blocks_in_range = 0;
3741 // Number of blocks occlusion culled
3742 u32 blocks_occlusion_culled = 0;
3743 // Number of blocks in rendering range but don't have a mesh
3744 u32 blocks_in_range_without_mesh = 0;
3745 // Blocks that had mesh that would have been drawn according to
3746 // rendering range (if max blocks limit didn't kick in)
3747 u32 blocks_would_have_drawn = 0;
3748 // Blocks that were drawn and had a mesh
3749 u32 blocks_drawn = 0;
3750 // Blocks which had a corresponding meshbuffer for this pass
3751 u32 blocks_had_pass_meshbuf = 0;
3752 // Blocks from which stuff was actually drawn
3753 u32 blocks_without_stuff = 0;
3756 Collect a set of blocks for drawing
3759 core::map<v3s16, MapBlock*> drawset;
3762 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3764 for(core::map<v2s16, MapSector*>::Iterator
3765 si = m_sectors.getIterator();
3766 si.atEnd() == false; si++)
3768 MapSector *sector = si.getNode()->getValue();
3769 v2s16 sp = sector->getPos();
3771 if(m_control.range_all == false)
3773 if(sp.X < p_blocks_min.X
3774 || sp.X > p_blocks_max.X
3775 || sp.Y < p_blocks_min.Z
3776 || sp.Y > p_blocks_max.Z)
3780 core::list< MapBlock * > sectorblocks;
3781 sector->getBlocks(sectorblocks);
3784 Loop through blocks in sector
3787 u32 sector_blocks_drawn = 0;
3789 core::list< MapBlock * >::Iterator i;
3790 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3792 MapBlock *block = *i;
3795 Compare block position to camera position, skip
3796 if not seen on display
3799 float range = 100000 * BS;
3800 if(m_control.range_all == false)
3801 range = m_control.wanted_range * BS;
3804 if(isBlockInSight(block->getPos(), camera_position,
3805 camera_direction, camera_fov,
3806 range, &d) == false)
3811 // This is ugly (spherical distance limit?)
3812 /*if(m_control.range_all == false &&
3813 d - 0.5*BS*MAP_BLOCKSIZE > range)
3820 Update expired mesh (used for day/night change)
3822 It doesn't work exactly like it should now with the
3823 tasked mesh update but whatever.
3826 bool mesh_expired = false;
3829 JMutexAutoLock lock(block->mesh_mutex);
3831 mesh_expired = block->getMeshExpired();
3833 // Mesh has not been expired and there is no mesh:
3834 // block has no content
3835 if(block->mesh == NULL && mesh_expired == false){
3836 blocks_in_range_without_mesh++;
3841 f32 faraway = BS*50;
3842 //f32 faraway = m_control.wanted_range * BS;
3845 This has to be done with the mesh_mutex unlocked
3847 // Pretty random but this should work somewhat nicely
3848 if(mesh_expired && (
3849 (mesh_update_count < 3
3850 && (d < faraway || mesh_update_count < 2)
3853 (m_control.range_all && mesh_update_count < 20)
3856 /*if(mesh_expired && mesh_update_count < 6
3857 && (d < faraway || mesh_update_count < 3))*/
3859 mesh_update_count++;
3861 // Mesh has been expired: generate new mesh
3862 //block->updateMesh(daynight_ratio);
3863 m_client->addUpdateMeshTask(block->getPos());
3865 mesh_expired = false;
3873 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3874 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3876 float stepfac = 1.1;
3877 float startoff = BS*1;
3878 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3879 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3880 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3881 u32 needed_count = 1;
3883 isOccluded(this, spn, cpn + v3s16(0,0,0),
3884 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3885 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
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)
3903 blocks_occlusion_culled++;
3907 // This block is in range. Reset usage timer.
3908 block->resetUsageTimer();
3911 Ignore if mesh doesn't exist
3914 JMutexAutoLock lock(block->mesh_mutex);
3916 scene::SMesh *mesh = block->mesh;
3919 blocks_in_range_without_mesh++;
3924 // Limit block count in case of a sudden increase
3925 blocks_would_have_drawn++;
3926 if(blocks_drawn >= m_control.wanted_max_blocks
3927 && m_control.range_all == false
3928 && d > m_control.wanted_min_range * BS)
3932 drawset[block->getPos()] = block;
3934 sector_blocks_drawn++;
3937 } // foreach sectorblocks
3939 if(sector_blocks_drawn != 0)
3940 m_last_drawn_sectors[sp] = true;
3945 Draw the selected MapBlocks
3949 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3951 int timecheck_counter = 0;
3952 for(core::map<v3s16, MapBlock*>::Iterator
3953 i = drawset.getIterator();
3954 i.atEnd() == false; i++)
3957 timecheck_counter++;
3958 if(timecheck_counter > 50)
3960 timecheck_counter = 0;
3961 int time2 = time(0);
3962 if(time2 > time1 + 4)
3964 infostream<<"ClientMap::renderMap(): "
3965 "Rendering takes ages, returning."
3972 MapBlock *block = i.getNode()->getValue();
3975 Draw the faces of the block
3978 JMutexAutoLock lock(block->mesh_mutex);
3980 scene::SMesh *mesh = block->mesh;
3983 u32 c = mesh->getMeshBufferCount();
3984 bool stuff_actually_drawn = false;
3985 for(u32 i=0; i<c; i++)
3987 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3988 const video::SMaterial& material = buf->getMaterial();
3989 video::IMaterialRenderer* rnd =
3990 driver->getMaterialRenderer(material.MaterialType);
3991 bool transparent = (rnd && rnd->isTransparent());
3992 // Render transparent on transparent pass and likewise.
3993 if(transparent == is_transparent_pass)
3995 if(buf->getVertexCount() == 0)
3996 errorstream<<"Block ["<<analyze_block(block)
3997 <<"] contains an empty meshbuf"<<std::endl;
3999 This *shouldn't* hurt too much because Irrlicht
4000 doesn't change opengl textures if the old
4001 material has the same texture.
4003 driver->setMaterial(buf->getMaterial());
4004 driver->drawMeshBuffer(buf);
4005 vertex_count += buf->getVertexCount();
4007 stuff_actually_drawn = true;
4010 if(stuff_actually_drawn)
4011 blocks_had_pass_meshbuf++;
4013 blocks_without_stuff++;
4018 // Log only on solid pass because values are the same
4019 if(pass == scene::ESNRP_SOLID){
4020 g_profiler->avg("CM: blocks in range", blocks_in_range);
4021 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
4022 if(blocks_in_range != 0)
4023 g_profiler->avg("CM: blocks in range without mesh (frac)",
4024 (float)blocks_in_range_without_mesh/blocks_in_range);
4025 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4028 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4029 if(blocks_had_pass_meshbuf != 0)
4030 g_profiler->avg(prefix+"meshbuffers per block",
4031 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4032 if(blocks_drawn != 0)
4033 g_profiler->avg(prefix+"empty blocks (frac)",
4034 (float)blocks_without_stuff / blocks_drawn);
4036 m_control.blocks_drawn = blocks_drawn;
4037 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4039 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4040 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4043 void ClientMap::renderPostFx()
4045 INodeDefManager *nodemgr = m_gamedef->ndef();
4047 // Sadly ISceneManager has no "post effects" render pass, in that case we
4048 // could just register for that and handle it in renderMap().
4050 m_camera_mutex.Lock();
4051 v3f camera_position = m_camera_position;
4052 m_camera_mutex.Unlock();
4054 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4056 // - If the player is in a solid node, make everything black.
4057 // - If the player is in liquid, draw a semi-transparent overlay.
4058 const ContentFeatures& features = nodemgr->get(n);
4059 video::SColor post_effect_color = features.post_effect_color;
4060 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4062 post_effect_color = video::SColor(255, 0, 0, 0);
4064 if (post_effect_color.getAlpha() != 0)
4066 // Draw a full-screen rectangle
4067 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4068 v2u32 ss = driver->getScreenSize();
4069 core::rect<s32> rect(0,0, ss.X, ss.Y);
4070 driver->draw2DRectangle(post_effect_color, rect);
4074 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4075 core::map<v3s16, MapBlock*> *affected_blocks)
4077 bool changed = false;
4079 Add it to all blocks touching it
4082 v3s16(0,0,0), // this
4083 v3s16(0,0,1), // back
4084 v3s16(0,1,0), // top
4085 v3s16(1,0,0), // right
4086 v3s16(0,0,-1), // front
4087 v3s16(0,-1,0), // bottom
4088 v3s16(-1,0,0), // left
4090 for(u16 i=0; i<7; i++)
4092 v3s16 p2 = p + dirs[i];
4093 // Block position of neighbor (or requested) node
4094 v3s16 blockpos = getNodeBlockPos(p2);
4095 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4096 if(blockref == NULL)
4098 // Relative position of requested node
4099 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4100 if(blockref->setTempMod(relpos, mod))
4105 if(changed && affected_blocks!=NULL)
4107 for(u16 i=0; i<7; i++)
4109 v3s16 p2 = p + dirs[i];
4110 // Block position of neighbor (or requested) node
4111 v3s16 blockpos = getNodeBlockPos(p2);
4112 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4113 if(blockref == NULL)
4115 affected_blocks->insert(blockpos, blockref);
4121 bool ClientMap::clearTempMod(v3s16 p,
4122 core::map<v3s16, MapBlock*> *affected_blocks)
4124 bool changed = false;
4126 v3s16(0,0,0), // this
4127 v3s16(0,0,1), // back
4128 v3s16(0,1,0), // top
4129 v3s16(1,0,0), // right
4130 v3s16(0,0,-1), // front
4131 v3s16(0,-1,0), // bottom
4132 v3s16(-1,0,0), // left
4134 for(u16 i=0; i<7; i++)
4136 v3s16 p2 = p + dirs[i];
4137 // Block position of neighbor (or requested) node
4138 v3s16 blockpos = getNodeBlockPos(p2);
4139 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4140 if(blockref == NULL)
4142 // Relative position of requested node
4143 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4144 if(blockref->clearTempMod(relpos))
4149 if(changed && affected_blocks!=NULL)
4151 for(u16 i=0; i<7; i++)
4153 v3s16 p2 = p + dirs[i];
4154 // Block position of neighbor (or requested) node
4155 v3s16 blockpos = getNodeBlockPos(p2);
4156 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4157 if(blockref == NULL)
4159 affected_blocks->insert(blockpos, blockref);
4165 void ClientMap::expireMeshes(bool only_daynight_diffed)
4167 TimeTaker timer("expireMeshes()");
4169 core::map<v2s16, MapSector*>::Iterator si;
4170 si = m_sectors.getIterator();
4171 for(; si.atEnd() == false; si++)
4173 MapSector *sector = si.getNode()->getValue();
4175 core::list< MapBlock * > sectorblocks;
4176 sector->getBlocks(sectorblocks);
4178 core::list< MapBlock * >::Iterator i;
4179 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4181 MapBlock *block = *i;
4183 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4189 JMutexAutoLock lock(block->mesh_mutex);
4190 if(block->mesh != NULL)
4192 /*block->mesh->drop();
4193 block->mesh = NULL;*/
4194 block->setMeshExpired(true);
4201 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4203 assert(mapType() == MAPTYPE_CLIENT);
4206 v3s16 p = blockpos + v3s16(0,0,0);
4207 MapBlock *b = getBlockNoCreate(p);
4208 b->updateMesh(daynight_ratio);
4209 //b->setMeshExpired(true);
4211 catch(InvalidPositionException &e){}
4214 v3s16 p = blockpos + v3s16(-1,0,0);
4215 MapBlock *b = getBlockNoCreate(p);
4216 b->updateMesh(daynight_ratio);
4217 //b->setMeshExpired(true);
4219 catch(InvalidPositionException &e){}
4221 v3s16 p = blockpos + v3s16(0,-1,0);
4222 MapBlock *b = getBlockNoCreate(p);
4223 b->updateMesh(daynight_ratio);
4224 //b->setMeshExpired(true);
4226 catch(InvalidPositionException &e){}
4228 v3s16 p = blockpos + v3s16(0,0,-1);
4229 MapBlock *b = getBlockNoCreate(p);
4230 b->updateMesh(daynight_ratio);
4231 //b->setMeshExpired(true);
4233 catch(InvalidPositionException &e){}
4238 Update mesh of block in which the node is, and if the node is at the
4239 leading edge, update the appropriate leading blocks too.
4241 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4249 v3s16 blockposes[4];
4250 for(u32 i=0; i<4; i++)
4252 v3s16 np = nodepos + dirs[i];
4253 blockposes[i] = getNodeBlockPos(np);
4254 // Don't update mesh of block if it has been done already
4255 bool already_updated = false;
4256 for(u32 j=0; j<i; j++)
4258 if(blockposes[j] == blockposes[i])
4260 already_updated = true;
4267 MapBlock *b = getBlockNoCreate(blockposes[i]);
4268 b->updateMesh(daynight_ratio);
4273 void ClientMap::PrintInfo(std::ostream &out)
4284 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4289 MapVoxelManipulator::~MapVoxelManipulator()
4291 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4295 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4297 TimeTaker timer1("emerge", &emerge_time);
4299 // Units of these are MapBlocks
4300 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4301 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4303 VoxelArea block_area_nodes
4304 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4306 addArea(block_area_nodes);
4308 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4309 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4310 for(s32 x=p_min.X; x<=p_max.X; x++)
4313 core::map<v3s16, bool>::Node *n;
4314 n = m_loaded_blocks.find(p);
4318 bool block_data_inexistent = false;
4321 TimeTaker timer1("emerge load", &emerge_load_time);
4323 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4324 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4326 a.print(infostream);
4327 infostream<<std::endl;*/
4329 MapBlock *block = m_map->getBlockNoCreate(p);
4330 if(block->isDummy())
4331 block_data_inexistent = true;
4333 block->copyTo(*this);
4335 catch(InvalidPositionException &e)
4337 block_data_inexistent = true;
4340 if(block_data_inexistent)
4342 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4343 // Fill with VOXELFLAG_INEXISTENT
4344 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4345 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4347 s32 i = m_area.index(a.MinEdge.X,y,z);
4348 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4352 m_loaded_blocks.insert(p, !block_data_inexistent);
4355 //infostream<<"emerge done"<<std::endl;
4359 SUGG: Add an option to only update eg. water and air nodes.
4360 This will make it interfere less with important stuff if
4363 void MapVoxelManipulator::blitBack
4364 (core::map<v3s16, MapBlock*> & modified_blocks)
4366 if(m_area.getExtent() == v3s16(0,0,0))
4369 //TimeTaker timer1("blitBack");
4371 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4372 <<m_loaded_blocks.size()<<std::endl;*/
4375 Initialize block cache
4377 v3s16 blockpos_last;
4378 MapBlock *block = NULL;
4379 bool block_checked_in_modified = false;
4381 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4382 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4383 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4387 u8 f = m_flags[m_area.index(p)];
4388 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4391 MapNode &n = m_data[m_area.index(p)];
4393 v3s16 blockpos = getNodeBlockPos(p);
4398 if(block == NULL || blockpos != blockpos_last){
4399 block = m_map->getBlockNoCreate(blockpos);
4400 blockpos_last = blockpos;
4401 block_checked_in_modified = false;
4404 // Calculate relative position in block
4405 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4407 // Don't continue if nothing has changed here
4408 if(block->getNode(relpos) == n)
4411 //m_map->setNode(m_area.MinEdge + p, n);
4412 block->setNode(relpos, n);
4415 Make sure block is in modified_blocks
4417 if(block_checked_in_modified == false)
4419 modified_blocks[blockpos] = block;
4420 block_checked_in_modified = true;
4423 catch(InvalidPositionException &e)
4429 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4430 MapVoxelManipulator(map),
4431 m_create_area(false)
4435 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4439 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4441 // Just create the area so that it can be pointed to
4442 VoxelManipulator::emerge(a, caller_id);
4445 void ManualMapVoxelManipulator::initialEmerge(
4446 v3s16 blockpos_min, v3s16 blockpos_max)
4448 TimeTaker timer1("initialEmerge", &emerge_time);
4450 // Units of these are MapBlocks
4451 v3s16 p_min = blockpos_min;
4452 v3s16 p_max = blockpos_max;
4454 VoxelArea block_area_nodes
4455 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4457 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4460 infostream<<"initialEmerge: area: ";
4461 block_area_nodes.print(infostream);
4462 infostream<<" ("<<size_MB<<"MB)";
4463 infostream<<std::endl;
4466 addArea(block_area_nodes);
4468 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4469 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4470 for(s32 x=p_min.X; x<=p_max.X; x++)
4473 core::map<v3s16, bool>::Node *n;
4474 n = m_loaded_blocks.find(p);
4478 bool block_data_inexistent = false;
4481 TimeTaker timer1("emerge load", &emerge_load_time);
4483 MapBlock *block = m_map->getBlockNoCreate(p);
4484 if(block->isDummy())
4485 block_data_inexistent = true;
4487 block->copyTo(*this);
4489 catch(InvalidPositionException &e)
4491 block_data_inexistent = true;
4494 if(block_data_inexistent)
4497 Mark area inexistent
4499 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4500 // Fill with VOXELFLAG_INEXISTENT
4501 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4502 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4504 s32 i = m_area.index(a.MinEdge.X,y,z);
4505 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4509 m_loaded_blocks.insert(p, !block_data_inexistent);
4513 void ManualMapVoxelManipulator::blitBackAll(
4514 core::map<v3s16, MapBlock*> * modified_blocks)
4516 if(m_area.getExtent() == v3s16(0,0,0))
4520 Copy data of all blocks
4522 for(core::map<v3s16, bool>::Iterator
4523 i = m_loaded_blocks.getIterator();
4524 i.atEnd() == false; i++)
4526 v3s16 p = i.getNode()->getKey();
4527 bool existed = i.getNode()->getValue();
4528 if(existed == false)
4530 // The Great Bug was found using this
4531 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4532 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4536 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4539 infostream<<"WARNING: "<<__FUNCTION_NAME
4540 <<": got NULL block "
4541 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4546 block->copyFrom(*this);
4549 modified_blocks->insert(p, block);