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++)
864 MapBlock *block = getBlockNoCreateNoEx(p);
869 if(block->getLightingExpired())
871 vmanip.initialEmerge(p, p);
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);
1010 meta->setOwner(player_name);
1011 setNodeMetadata(p, meta);
1015 If node is under sunlight and doesn't let sunlight through,
1016 take all sunlighted nodes under it and clear light from them
1017 and from where the light has been spread.
1018 TODO: This could be optimized by mass-unlighting instead
1021 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1025 //m_dout<<DTIME<<"y="<<y<<std::endl;
1026 v3s16 n2pos(p.X, y, p.Z);
1030 n2 = getNode(n2pos);
1032 catch(InvalidPositionException &e)
1037 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1039 unLightNeighbors(LIGHTBANK_DAY,
1040 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1041 light_sources, modified_blocks);
1042 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1050 for(s32 i=0; i<2; i++)
1052 enum LightBank bank = banks[i];
1055 Spread light from all nodes that might be capable of doing so
1057 spreadLight(bank, light_sources, modified_blocks);
1061 Update information about whether day and night light differ
1063 for(core::map<v3s16, MapBlock*>::Iterator
1064 i = modified_blocks.getIterator();
1065 i.atEnd() == false; i++)
1067 MapBlock *block = i.getNode()->getValue();
1068 block->updateDayNightDiff();
1072 Add neighboring liquid nodes and the node itself if it is
1073 liquid (=water node was added) to transform queue.
1076 v3s16(0,0,0), // self
1077 v3s16(0,0,1), // back
1078 v3s16(0,1,0), // top
1079 v3s16(1,0,0), // right
1080 v3s16(0,0,-1), // front
1081 v3s16(0,-1,0), // bottom
1082 v3s16(-1,0,0), // left
1084 for(u16 i=0; i<7; i++)
1089 v3s16 p2 = p + dirs[i];
1091 MapNode n2 = getNode(p2);
1092 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1094 m_transforming_liquid.push_back(p2);
1097 }catch(InvalidPositionException &e)
1105 void Map::removeNodeAndUpdate(v3s16 p,
1106 core::map<v3s16, MapBlock*> &modified_blocks)
1108 INodeDefManager *nodemgr = m_gamedef->ndef();
1110 /*PrintInfo(m_dout);
1111 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1112 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1114 bool node_under_sunlight = true;
1116 v3s16 toppos = p + v3s16(0,1,0);
1118 // Node will be replaced with this
1119 content_t replace_material = CONTENT_AIR;
1122 If there is a node at top and it doesn't have sunlight,
1123 there will be no sunlight going down.
1126 MapNode topnode = getNode(toppos);
1128 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1129 node_under_sunlight = false;
1131 catch(InvalidPositionException &e)
1135 core::map<v3s16, bool> light_sources;
1137 enum LightBank banks[] =
1142 for(s32 i=0; i<2; i++)
1144 enum LightBank bank = banks[i];
1147 Unlight neighbors (in case the node is a light source)
1149 unLightNeighbors(bank, p,
1150 getNode(p).getLight(bank, nodemgr),
1151 light_sources, modified_blocks);
1155 Remove node metadata
1158 removeNodeMetadata(p);
1162 This also clears the lighting.
1166 n.setContent(replace_material);
1169 for(s32 i=0; i<2; i++)
1171 enum LightBank bank = banks[i];
1174 Recalculate lighting
1176 spreadLight(bank, light_sources, modified_blocks);
1179 // Add the block of the removed node to modified_blocks
1180 v3s16 blockpos = getNodeBlockPos(p);
1181 MapBlock * block = getBlockNoCreate(blockpos);
1182 assert(block != NULL);
1183 modified_blocks.insert(blockpos, block);
1186 If the removed node was under sunlight, propagate the
1187 sunlight down from it and then light all neighbors
1188 of the propagated blocks.
1190 if(node_under_sunlight)
1192 s16 ybottom = propagateSunlight(p, modified_blocks);
1193 /*m_dout<<DTIME<<"Node was under sunlight. "
1194 "Propagating sunlight";
1195 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1197 for(; y >= ybottom; y--)
1199 v3s16 p2(p.X, y, p.Z);
1200 /*m_dout<<DTIME<<"lighting neighbors of node ("
1201 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1203 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1208 // Set the lighting of this node to 0
1209 // TODO: Is this needed? Lighting is cleared up there already.
1211 MapNode n = getNode(p);
1212 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1215 catch(InvalidPositionException &e)
1221 for(s32 i=0; i<2; i++)
1223 enum LightBank bank = banks[i];
1225 // Get the brightest neighbour node and propagate light from it
1226 v3s16 n2p = getBrightestNeighbour(bank, p);
1228 MapNode n2 = getNode(n2p);
1229 lightNeighbors(bank, n2p, modified_blocks);
1231 catch(InvalidPositionException &e)
1237 Update information about whether day and night light differ
1239 for(core::map<v3s16, MapBlock*>::Iterator
1240 i = modified_blocks.getIterator();
1241 i.atEnd() == false; i++)
1243 MapBlock *block = i.getNode()->getValue();
1244 block->updateDayNightDiff();
1248 Add neighboring liquid nodes and this node to transform queue.
1249 (it's vital for the node itself to get updated last.)
1252 v3s16(0,0,1), // back
1253 v3s16(0,1,0), // top
1254 v3s16(1,0,0), // right
1255 v3s16(0,0,-1), // front
1256 v3s16(0,-1,0), // bottom
1257 v3s16(-1,0,0), // left
1258 v3s16(0,0,0), // self
1260 for(u16 i=0; i<7; i++)
1265 v3s16 p2 = p + dirs[i];
1267 MapNode n2 = getNode(p2);
1268 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1270 m_transforming_liquid.push_back(p2);
1273 }catch(InvalidPositionException &e)
1279 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1282 event.type = MEET_ADDNODE;
1286 bool succeeded = true;
1288 core::map<v3s16, MapBlock*> modified_blocks;
1289 std::string st = std::string("");
1290 addNodeAndUpdate(p, n, modified_blocks, st);
1292 // Copy modified_blocks to event
1293 for(core::map<v3s16, MapBlock*>::Iterator
1294 i = modified_blocks.getIterator();
1295 i.atEnd()==false; i++)
1297 event.modified_blocks.insert(i.getNode()->getKey(), false);
1300 catch(InvalidPositionException &e){
1304 dispatchEvent(&event);
1309 bool Map::removeNodeWithEvent(v3s16 p)
1312 event.type = MEET_REMOVENODE;
1315 bool succeeded = true;
1317 core::map<v3s16, MapBlock*> modified_blocks;
1318 removeNodeAndUpdate(p, modified_blocks);
1320 // Copy modified_blocks to event
1321 for(core::map<v3s16, MapBlock*>::Iterator
1322 i = modified_blocks.getIterator();
1323 i.atEnd()==false; i++)
1325 event.modified_blocks.insert(i.getNode()->getKey(), false);
1328 catch(InvalidPositionException &e){
1332 dispatchEvent(&event);
1337 bool Map::dayNightDiffed(v3s16 blockpos)
1340 v3s16 p = blockpos + v3s16(0,0,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1348 v3s16 p = blockpos + v3s16(-1,0,0);
1349 MapBlock *b = getBlockNoCreate(p);
1350 if(b->dayNightDiffed())
1353 catch(InvalidPositionException &e){}
1355 v3s16 p = blockpos + v3s16(0,-1,0);
1356 MapBlock *b = getBlockNoCreate(p);
1357 if(b->dayNightDiffed())
1360 catch(InvalidPositionException &e){}
1362 v3s16 p = blockpos + v3s16(0,0,-1);
1363 MapBlock *b = getBlockNoCreate(p);
1364 if(b->dayNightDiffed())
1367 catch(InvalidPositionException &e){}
1370 v3s16 p = blockpos + v3s16(1,0,0);
1371 MapBlock *b = getBlockNoCreate(p);
1372 if(b->dayNightDiffed())
1375 catch(InvalidPositionException &e){}
1377 v3s16 p = blockpos + v3s16(0,1,0);
1378 MapBlock *b = getBlockNoCreate(p);
1379 if(b->dayNightDiffed())
1382 catch(InvalidPositionException &e){}
1384 v3s16 p = blockpos + v3s16(0,0,1);
1385 MapBlock *b = getBlockNoCreate(p);
1386 if(b->dayNightDiffed())
1389 catch(InvalidPositionException &e){}
1395 Updates usage timers
1397 void Map::timerUpdate(float dtime, float unload_timeout,
1398 core::list<v3s16> *unloaded_blocks)
1400 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1402 // Profile modified reasons
1403 Profiler modprofiler;
1405 core::list<v2s16> sector_deletion_queue;
1406 u32 deleted_blocks_count = 0;
1407 u32 saved_blocks_count = 0;
1409 core::map<v2s16, MapSector*>::Iterator si;
1412 si = m_sectors.getIterator();
1413 for(; si.atEnd() == false; si++)
1415 MapSector *sector = si.getNode()->getValue();
1417 bool all_blocks_deleted = true;
1419 core::list<MapBlock*> blocks;
1420 sector->getBlocks(blocks);
1422 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1423 i != blocks.end(); i++)
1425 MapBlock *block = (*i);
1427 block->incrementUsageTimer(dtime);
1429 if(block->getUsageTimer() > unload_timeout)
1431 v3s16 p = block->getPos();
1434 if(block->getModified() != MOD_STATE_CLEAN
1435 && save_before_unloading)
1437 modprofiler.add(block->getModifiedReason(), 1);
1439 saved_blocks_count++;
1442 // Delete from memory
1443 sector->deleteBlock(block);
1446 unloaded_blocks->push_back(p);
1448 deleted_blocks_count++;
1452 all_blocks_deleted = false;
1456 if(all_blocks_deleted)
1458 sector_deletion_queue.push_back(si.getNode()->getKey());
1463 // Finally delete the empty sectors
1464 deleteSectors(sector_deletion_queue);
1466 if(deleted_blocks_count != 0)
1468 PrintInfo(infostream); // ServerMap/ClientMap:
1469 infostream<<"Unloaded "<<deleted_blocks_count
1470 <<" blocks from memory";
1471 if(save_before_unloading)
1472 infostream<<", of which "<<saved_blocks_count<<" were written";
1473 infostream<<"."<<std::endl;
1474 if(saved_blocks_count != 0){
1475 PrintInfo(infostream); // ServerMap/ClientMap:
1476 infostream<<"Blocks modified by: "<<std::endl;
1477 modprofiler.print(infostream);
1482 void Map::deleteSectors(core::list<v2s16> &list)
1484 core::list<v2s16>::Iterator j;
1485 for(j=list.begin(); j!=list.end(); j++)
1487 MapSector *sector = m_sectors[*j];
1488 // If sector is in sector cache, remove it from there
1489 if(m_sector_cache == sector)
1490 m_sector_cache = NULL;
1491 // Remove from map and delete
1492 m_sectors.remove(*j);
1498 void Map::unloadUnusedData(float timeout,
1499 core::list<v3s16> *deleted_blocks)
1501 core::list<v2s16> sector_deletion_queue;
1502 u32 deleted_blocks_count = 0;
1503 u32 saved_blocks_count = 0;
1505 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1506 for(; si.atEnd() == false; si++)
1508 MapSector *sector = si.getNode()->getValue();
1510 bool all_blocks_deleted = true;
1512 core::list<MapBlock*> blocks;
1513 sector->getBlocks(blocks);
1514 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1515 i != blocks.end(); i++)
1517 MapBlock *block = (*i);
1519 if(block->getUsageTimer() > timeout)
1522 if(block->getModified() != MOD_STATE_CLEAN)
1525 saved_blocks_count++;
1527 // Delete from memory
1528 sector->deleteBlock(block);
1529 deleted_blocks_count++;
1533 all_blocks_deleted = false;
1537 if(all_blocks_deleted)
1539 sector_deletion_queue.push_back(si.getNode()->getKey());
1543 deleteSectors(sector_deletion_queue);
1545 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1546 <<", of which "<<saved_blocks_count<<" were wr."
1549 //return sector_deletion_queue.getSize();
1550 //return deleted_blocks_count;
1554 void Map::PrintInfo(std::ostream &out)
1559 #define WATER_DROP_BOOST 4
1563 NEIGHBOR_SAME_LEVEL,
1566 struct NodeNeighbor {
1572 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1574 INodeDefManager *nodemgr = m_gamedef->ndef();
1576 DSTACK(__FUNCTION_NAME);
1577 //TimeTaker timer("transformLiquids()");
1580 u32 initial_size = m_transforming_liquid.size();
1582 /*if(initial_size != 0)
1583 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1585 // list of nodes that due to viscosity have not reached their max level height
1586 UniqueQueue<v3s16> must_reflow;
1588 // List of MapBlocks that will require a lighting update (due to lava)
1589 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1591 while(m_transforming_liquid.size() != 0)
1593 // This should be done here so that it is done when continue is used
1594 if(loopcount >= initial_size * 3)
1599 Get a queued transforming liquid node
1601 v3s16 p0 = m_transforming_liquid.pop_front();
1603 MapNode n0 = getNodeNoEx(p0);
1606 Collect information about current node
1608 s8 liquid_level = -1;
1609 u8 liquid_kind = CONTENT_IGNORE;
1610 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1611 switch (liquid_type) {
1613 liquid_level = LIQUID_LEVEL_SOURCE;
1614 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1616 case LIQUID_FLOWING:
1617 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1618 liquid_kind = n0.getContent();
1621 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1622 // continue with the next node.
1623 if (n0.getContent() != CONTENT_AIR)
1625 liquid_kind = CONTENT_AIR;
1630 Collect information about the environment
1632 const v3s16 *dirs = g_6dirs;
1633 NodeNeighbor sources[6]; // surrounding sources
1634 int num_sources = 0;
1635 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1637 NodeNeighbor airs[6]; // surrounding air
1639 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1640 int num_neutrals = 0;
1641 bool flowing_down = false;
1642 for (u16 i = 0; i < 6; i++) {
1643 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1646 nt = NEIGHBOR_UPPER;
1649 nt = NEIGHBOR_LOWER;
1652 v3s16 npos = p0 + dirs[i];
1653 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1654 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1656 if (nb.n.getContent() == CONTENT_AIR) {
1657 airs[num_airs++] = nb;
1658 // if the current node is a water source the neighbor
1659 // should be enqueded for transformation regardless of whether the
1660 // current node changes or not.
1661 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1662 m_transforming_liquid.push_back(npos);
1663 // if the current node happens to be a flowing node, it will start to flow down here.
1664 if (nb.t == NEIGHBOR_LOWER) {
1665 flowing_down = true;
1668 neutrals[num_neutrals++] = nb;
1672 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1673 if (liquid_kind == CONTENT_AIR)
1674 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1675 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1676 neutrals[num_neutrals++] = nb;
1678 sources[num_sources++] = nb;
1681 case LIQUID_FLOWING:
1682 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1683 if (liquid_kind == CONTENT_AIR)
1684 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1685 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1686 neutrals[num_neutrals++] = nb;
1688 flows[num_flows++] = nb;
1689 if (nb.t == NEIGHBOR_LOWER)
1690 flowing_down = true;
1697 decide on the type (and possibly level) of the current node
1699 content_t new_node_content;
1700 s8 new_node_level = -1;
1701 s8 max_node_level = -1;
1702 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1703 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1704 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1705 // it's perfectly safe to use liquid_kind here to determine the new node content.
1706 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1707 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1708 // liquid_kind is set properly, see above
1709 new_node_content = liquid_kind;
1710 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1712 // no surrounding sources, so get the maximum level that can flow into this node
1713 for (u16 i = 0; i < num_flows; i++) {
1714 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1715 switch (flows[i].t) {
1716 case NEIGHBOR_UPPER:
1717 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1718 max_node_level = LIQUID_LEVEL_MAX;
1719 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1720 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1721 } else if (nb_liquid_level > max_node_level)
1722 max_node_level = nb_liquid_level;
1724 case NEIGHBOR_LOWER:
1726 case NEIGHBOR_SAME_LEVEL:
1727 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1728 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1729 max_node_level = nb_liquid_level - 1;
1735 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1736 if (viscosity > 1 && max_node_level != liquid_level) {
1737 // amount to gain, limited by viscosity
1738 // must be at least 1 in absolute value
1739 s8 level_inc = max_node_level - liquid_level;
1740 if (level_inc < -viscosity || level_inc > viscosity)
1741 new_node_level = liquid_level + level_inc/viscosity;
1742 else if (level_inc < 0)
1743 new_node_level = liquid_level - 1;
1744 else if (level_inc > 0)
1745 new_node_level = liquid_level + 1;
1746 if (new_node_level != max_node_level)
1747 must_reflow.push_back(p0);
1749 new_node_level = max_node_level;
1751 if (new_node_level >= 0)
1752 new_node_content = liquid_kind;
1754 new_node_content = CONTENT_AIR;
1759 check if anything has changed. if not, just continue with the next node.
1761 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1762 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1763 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1769 update the current node
1771 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1772 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1773 // set level to last 3 bits, flowing down bit to 4th bit
1774 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1776 // set the liquid level and flow bit to 0
1777 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1779 n0.setContent(new_node_content);
1781 v3s16 blockpos = getNodeBlockPos(p0);
1782 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1784 modified_blocks.insert(blockpos, block);
1785 // If node emits light, MapBlock requires lighting update
1786 if(nodemgr->get(n0).light_source != 0)
1787 lighting_modified_blocks[block->getPos()] = block;
1791 enqueue neighbors for update if neccessary
1793 switch (nodemgr->get(n0.getContent()).liquid_type) {
1795 case LIQUID_FLOWING:
1796 // make sure source flows into all neighboring nodes
1797 for (u16 i = 0; i < num_flows; i++)
1798 if (flows[i].t != NEIGHBOR_UPPER)
1799 m_transforming_liquid.push_back(flows[i].p);
1800 for (u16 i = 0; i < num_airs; i++)
1801 if (airs[i].t != NEIGHBOR_UPPER)
1802 m_transforming_liquid.push_back(airs[i].p);
1805 // this flow has turned to air; neighboring flows might need to do the same
1806 for (u16 i = 0; i < num_flows; i++)
1807 m_transforming_liquid.push_back(flows[i].p);
1811 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1812 while (must_reflow.size() > 0)
1813 m_transforming_liquid.push_back(must_reflow.pop_front());
1814 updateLighting(lighting_modified_blocks, modified_blocks);
1817 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1819 v3s16 blockpos = getNodeBlockPos(p);
1820 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1821 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1823 infostream<<"Map::getNodeMetadata(): Need to emerge "
1824 <<PP(blockpos)<<std::endl;
1825 block = emergeBlock(blockpos, false);
1829 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1833 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1837 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1839 v3s16 blockpos = getNodeBlockPos(p);
1840 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1841 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1843 infostream<<"Map::setNodeMetadata(): Need to emerge "
1844 <<PP(blockpos)<<std::endl;
1845 block = emergeBlock(blockpos, false);
1849 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1853 block->m_node_metadata->set(p_rel, meta);
1856 void Map::removeNodeMetadata(v3s16 p)
1858 v3s16 blockpos = getNodeBlockPos(p);
1859 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1860 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1863 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1867 block->m_node_metadata->remove(p_rel);
1870 void Map::nodeMetadataStep(float dtime,
1871 core::map<v3s16, MapBlock*> &changed_blocks)
1875 Currently there is no way to ensure that all the necessary
1876 blocks are loaded when this is run. (They might get unloaded)
1877 NOTE: ^- Actually, that might not be so. In a quick test it
1878 reloaded a block with a furnace when I walked back to it from
1881 core::map<v2s16, MapSector*>::Iterator si;
1882 si = m_sectors.getIterator();
1883 for(; si.atEnd() == false; si++)
1885 MapSector *sector = si.getNode()->getValue();
1886 core::list< MapBlock * > sectorblocks;
1887 sector->getBlocks(sectorblocks);
1888 core::list< MapBlock * >::Iterator i;
1889 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1891 MapBlock *block = *i;
1892 bool changed = block->m_node_metadata->step(dtime);
1894 changed_blocks[block->getPos()] = block;
1903 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1904 Map(dout_server, gamedef),
1906 m_map_metadata_changed(true),
1908 m_database_read(NULL),
1909 m_database_write(NULL)
1911 infostream<<__FUNCTION_NAME<<std::endl;
1913 //m_chunksize = 8; // Takes a few seconds
1915 if (g_settings->get("fixed_map_seed").empty())
1917 m_seed = (((u64)(myrand()%0xffff)<<0)
1918 + ((u64)(myrand()%0xffff)<<16)
1919 + ((u64)(myrand()%0xffff)<<32)
1920 + ((u64)(myrand()%0xffff)<<48));
1924 m_seed = g_settings->getU64("fixed_map_seed");
1928 Experimental and debug stuff
1935 Try to load map; if not found, create a new one.
1938 m_savedir = savedir;
1939 m_map_saving_enabled = false;
1943 // If directory exists, check contents and load if possible
1944 if(fs::PathExists(m_savedir))
1946 // If directory is empty, it is safe to save into it.
1947 if(fs::GetDirListing(m_savedir).size() == 0)
1949 infostream<<"Server: Empty save directory is valid."
1951 m_map_saving_enabled = true;
1956 // Load map metadata (seed, chunksize)
1959 catch(FileNotGoodException &e){
1960 infostream<<"WARNING: Could not load map metadata"
1961 //<<" Disabling chunk-based generator."
1967 // Load chunk metadata
1970 catch(FileNotGoodException &e){
1971 infostream<<"WARNING: Could not load chunk metadata."
1972 <<" Disabling chunk-based generator."
1977 /*infostream<<"Server: Successfully loaded chunk "
1978 "metadata and sector (0,0) from "<<savedir<<
1979 ", assuming valid save directory."
1982 infostream<<"Server: Successfully loaded map "
1983 <<"and chunk metadata from "<<savedir
1984 <<", assuming valid save directory."
1987 m_map_saving_enabled = true;
1988 // Map loaded, not creating new one
1992 // If directory doesn't exist, it is safe to save to it
1994 m_map_saving_enabled = true;
1997 catch(std::exception &e)
1999 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2000 <<", exception: "<<e.what()<<std::endl;
2001 infostream<<"Please remove the map or fix it."<<std::endl;
2002 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2005 infostream<<"Initializing new map."<<std::endl;
2007 // Create zero sector
2008 emergeSector(v2s16(0,0));
2010 // Initially write whole map
2014 ServerMap::~ServerMap()
2016 infostream<<__FUNCTION_NAME<<std::endl;
2020 if(m_map_saving_enabled)
2022 // Save only changed parts
2024 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2028 infostream<<"Server: map not saved"<<std::endl;
2031 catch(std::exception &e)
2033 infostream<<"Server: Failed to save map to "<<m_savedir
2034 <<", exception: "<<e.what()<<std::endl;
2038 Close database if it was opened
2041 sqlite3_finalize(m_database_read);
2042 if(m_database_write)
2043 sqlite3_finalize(m_database_write);
2045 sqlite3_close(m_database);
2051 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2052 for(; i.atEnd() == false; i++)
2054 MapChunk *chunk = i.getNode()->getValue();
2060 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2062 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2063 if(enable_mapgen_debug_info)
2064 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2065 <<blockpos.Z<<")"<<std::endl;
2067 // Do nothing if not inside limits (+-1 because of neighbors)
2068 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2069 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2075 data->no_op = false;
2076 data->seed = m_seed;
2077 data->blockpos = blockpos;
2078 data->nodedef = m_gamedef->ndef();
2081 Create the whole area of this and the neighboring blocks
2084 //TimeTaker timer("initBlockMake() create area");
2086 for(s16 x=-1; x<=1; x++)
2087 for(s16 z=-1; z<=1; z++)
2089 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2090 // Sector metadata is loaded from disk if not already loaded.
2091 ServerMapSector *sector = createSector(sectorpos);
2094 for(s16 y=-1; y<=1; y++)
2096 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2097 //MapBlock *block = createBlock(p);
2098 // 1) get from memory, 2) load from disk
2099 MapBlock *block = emergeBlock(p, false);
2100 // 3) create a blank one
2103 block = createBlock(p);
2106 Block gets sunlight if this is true.
2108 Refer to the map generator heuristics.
2110 bool ug = mapgen::block_is_underground(data->seed, p);
2111 block->setIsUnderground(ug);
2114 // Lighting will not be valid after make_chunk is called
2115 block->setLightingExpired(true);
2116 // Lighting will be calculated
2117 //block->setLightingExpired(false);
2123 Now we have a big empty area.
2125 Make a ManualMapVoxelManipulator that contains this and the
2129 // The area that contains this block and it's neighbors
2130 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2131 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2133 data->vmanip = new ManualMapVoxelManipulator(this);
2134 //data->vmanip->setMap(this);
2138 //TimeTaker timer("initBlockMake() initialEmerge");
2139 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2142 // Data is ready now.
2145 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2146 core::map<v3s16, MapBlock*> &changed_blocks)
2148 v3s16 blockpos = data->blockpos;
2149 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2150 <<blockpos.Z<<")"<<std::endl;*/
2154 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2158 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2160 /*infostream<<"Resulting vmanip:"<<std::endl;
2161 data->vmanip.print(infostream);*/
2164 Blit generated stuff to map
2165 NOTE: blitBackAll adds nearly everything to changed_blocks
2169 //TimeTaker timer("finishBlockMake() blitBackAll");
2170 data->vmanip->blitBackAll(&changed_blocks);
2173 if(enable_mapgen_debug_info)
2174 infostream<<"finishBlockMake: changed_blocks.size()="
2175 <<changed_blocks.size()<<std::endl;
2178 Copy transforming liquid information
2180 while(data->transforming_liquid.size() > 0)
2182 v3s16 p = data->transforming_liquid.pop_front();
2183 m_transforming_liquid.push_back(p);
2189 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2193 Set is_underground flag for lighting with sunlight.
2195 Refer to map generator heuristics.
2197 NOTE: This is done in initChunkMake
2199 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2203 Add sunlight to central block.
2204 This makes in-dark-spawning monsters to not flood the whole thing.
2205 Do not spread the light, though.
2207 /*core::map<v3s16, bool> light_sources;
2208 bool black_air_left = false;
2209 block->propagateSunlight(light_sources, true, &black_air_left);*/
2212 NOTE: Lighting and object adding shouldn't really be here, but
2213 lighting is a bit tricky to move properly to makeBlock.
2214 TODO: Do this the right way anyway, that is, move it to makeBlock.
2215 - There needs to be some way for makeBlock to report back if
2216 the lighting update is going further down because of the
2217 new block blocking light
2222 NOTE: This takes ~60ms, TODO: Investigate why
2225 TimeTaker t("finishBlockMake lighting update");
2227 core::map<v3s16, MapBlock*> lighting_update_blocks;
2230 lighting_update_blocks.insert(block->getPos(), block);
2235 v3s16 p = block->getPos()+v3s16(x,1,z);
2236 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2240 // All modified blocks
2241 // NOTE: Should this be done? If this is not done, then the lighting
2242 // of the others will be updated in a different place, one by one, i
2243 // think... or they might not? Well, at least they are left marked as
2244 // "lighting expired"; it seems that is not handled at all anywhere,
2245 // so enabling this will slow it down A LOT because otherwise it
2246 // would not do this at all. This causes the black trees.
2247 for(core::map<v3s16, MapBlock*>::Iterator
2248 i = changed_blocks.getIterator();
2249 i.atEnd() == false; i++)
2251 lighting_update_blocks.insert(i.getNode()->getKey(),
2252 i.getNode()->getValue());
2254 /*// Also force-add all the upmost blocks for proper sunlight
2255 for(s16 x=-1; x<=1; x++)
2256 for(s16 z=-1; z<=1; z++)
2258 v3s16 p = block->getPos()+v3s16(x,1,z);
2259 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2262 updateLighting(lighting_update_blocks, changed_blocks);
2265 Set lighting to non-expired state in all of them.
2266 This is cheating, but it is not fast enough if all of them
2267 would actually be updated.
2269 for(s16 x=-1; x<=1; x++)
2270 for(s16 y=-1; y<=1; y++)
2271 for(s16 z=-1; z<=1; z++)
2273 v3s16 p = block->getPos()+v3s16(x,y,z);
2274 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2277 if(enable_mapgen_debug_info == false)
2278 t.stop(true); // Hide output
2282 Add random objects to block
2284 mapgen::add_random_objects(block);
2287 Go through changed blocks
2289 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2290 i.atEnd() == false; i++)
2292 MapBlock *block = i.getNode()->getValue();
2295 Update day/night difference cache of the MapBlocks
2297 block->updateDayNightDiff();
2299 Set block as modified
2301 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2302 "finishBlockMake updateDayNightDiff");
2306 Set central block as generated
2308 block->setGenerated(true);
2311 Save changed parts of map
2312 NOTE: Will be saved later.
2316 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2317 <<blockpos.Z<<")"<<std::endl;*/
2319 if(enable_mapgen_debug_info)
2322 Analyze resulting blocks
2324 for(s16 x=-1; x<=1; x++)
2325 for(s16 y=-1; y<=1; y++)
2326 for(s16 z=-1; z<=1; z++)
2328 v3s16 p = block->getPos()+v3s16(x,y,z);
2329 MapBlock *block = getBlockNoCreateNoEx(p);
2331 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2332 infostream<<"Generated "<<spos<<": "
2333 <<analyze_block(block)<<std::endl;
2341 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2343 DSTACKF("%s: p2d=(%d,%d)",
2348 Check if it exists already in memory
2350 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2355 Try to load it from disk (with blocks)
2357 //if(loadSectorFull(p2d) == true)
2360 Try to load metadata from disk
2363 if(loadSectorMeta(p2d) == true)
2365 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2368 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2369 throw InvalidPositionException("");
2375 Do not create over-limit
2377 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2378 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2379 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2380 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2381 throw InvalidPositionException("createSector(): pos. over limit");
2384 Generate blank sector
2387 sector = new ServerMapSector(this, p2d, m_gamedef);
2389 // Sector position on map in nodes
2390 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2395 m_sectors.insert(p2d, sector);
2401 This is a quick-hand function for calling makeBlock().
2403 MapBlock * ServerMap::generateBlock(
2405 core::map<v3s16, MapBlock*> &modified_blocks
2408 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2410 /*infostream<<"generateBlock(): "
2411 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2414 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2416 TimeTaker timer("generateBlock");
2418 //MapBlock *block = original_dummy;
2420 v2s16 p2d(p.X, p.Z);
2421 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2424 Do not generate over-limit
2426 if(blockpos_over_limit(p))
2428 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2429 throw InvalidPositionException("generateBlock(): pos. over limit");
2433 Create block make data
2435 mapgen::BlockMakeData data;
2436 initBlockMake(&data, p);
2442 TimeTaker t("mapgen::make_block()");
2443 mapgen::make_block(&data);
2445 if(enable_mapgen_debug_info == false)
2446 t.stop(true); // Hide output
2450 Blit data back on map, update lighting, add mobs and whatever this does
2452 finishBlockMake(&data, modified_blocks);
2457 MapBlock *block = getBlockNoCreateNoEx(p);
2465 bool erroneus_content = false;
2466 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2467 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2468 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2471 MapNode n = block->getNode(p);
2472 if(n.getContent() == CONTENT_IGNORE)
2474 infostream<<"CONTENT_IGNORE at "
2475 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2477 erroneus_content = true;
2481 if(erroneus_content)
2490 Generate a completely empty block
2494 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2495 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2497 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2500 n.setContent(CONTENT_AIR);
2501 block->setNode(v3s16(x0,y0,z0), n);
2507 if(enable_mapgen_debug_info == false)
2508 timer.stop(true); // Hide output
2513 MapBlock * ServerMap::createBlock(v3s16 p)
2515 DSTACKF("%s: p=(%d,%d,%d)",
2516 __FUNCTION_NAME, p.X, p.Y, p.Z);
2519 Do not create over-limit
2521 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2522 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2523 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2524 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2525 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2526 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2527 throw InvalidPositionException("createBlock(): pos. over limit");
2529 v2s16 p2d(p.X, p.Z);
2532 This will create or load a sector if not found in memory.
2533 If block exists on disk, it will be loaded.
2535 NOTE: On old save formats, this will be slow, as it generates
2536 lighting on blocks for them.
2538 ServerMapSector *sector;
2540 sector = (ServerMapSector*)createSector(p2d);
2541 assert(sector->getId() == MAPSECTOR_SERVER);
2543 catch(InvalidPositionException &e)
2545 infostream<<"createBlock: createSector() failed"<<std::endl;
2549 NOTE: This should not be done, or at least the exception
2550 should not be passed on as std::exception, because it
2551 won't be catched at all.
2553 /*catch(std::exception &e)
2555 infostream<<"createBlock: createSector() failed: "
2556 <<e.what()<<std::endl;
2561 Try to get a block from the sector
2564 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2567 if(block->isDummy())
2572 block = sector->createBlankBlock(block_y);
2576 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2578 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2580 p.X, p.Y, p.Z, allow_generate);
2583 MapBlock *block = getBlockNoCreateNoEx(p);
2584 if(block && block->isDummy() == false)
2589 MapBlock *block = loadBlock(p);
2596 core::map<v3s16, MapBlock*> modified_blocks;
2597 MapBlock *block = generateBlock(p, modified_blocks);
2601 event.type = MEET_OTHER;
2604 // Copy modified_blocks to event
2605 for(core::map<v3s16, MapBlock*>::Iterator
2606 i = modified_blocks.getIterator();
2607 i.atEnd()==false; i++)
2609 event.modified_blocks.insert(i.getNode()->getKey(), false);
2613 dispatchEvent(&event);
2622 s16 ServerMap::findGroundLevel(v2s16 p2d)
2626 Uh, just do something random...
2628 // Find existing map from top to down
2631 v3s16 p(p2d.X, max, p2d.Y);
2632 for(; p.Y>min; p.Y--)
2634 MapNode n = getNodeNoEx(p);
2635 if(n.getContent() != CONTENT_IGNORE)
2640 // If this node is not air, go to plan b
2641 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2643 // Search existing walkable and return it
2644 for(; p.Y>min; p.Y--)
2646 MapNode n = getNodeNoEx(p);
2647 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2656 Determine from map generator noise functions
2659 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2662 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2663 //return (s16)level;
2666 void ServerMap::createDatabase() {
2669 e = sqlite3_exec(m_database,
2670 "CREATE TABLE IF NOT EXISTS `blocks` ("
2671 "`pos` INT NOT NULL PRIMARY KEY,"
2674 , NULL, NULL, NULL);
2675 if(e == SQLITE_ABORT)
2676 throw FileNotGoodException("Could not create database structure");
2678 infostream<<"Server: Database structure was created";
2681 void ServerMap::verifyDatabase() {
2686 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2687 bool needs_create = false;
2691 Open the database connection
2694 createDirs(m_savedir);
2696 if(!fs::PathExists(dbp))
2697 needs_create = true;
2699 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2700 if(d != SQLITE_OK) {
2701 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2702 throw FileNotGoodException("Cannot open database file");
2708 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2709 if(d != SQLITE_OK) {
2710 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2711 throw FileNotGoodException("Cannot prepare read statement");
2714 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2715 if(d != SQLITE_OK) {
2716 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2717 throw FileNotGoodException("Cannot prepare write statement");
2720 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2721 if(d != SQLITE_OK) {
2722 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2723 throw FileNotGoodException("Cannot prepare read statement");
2726 infostream<<"Server: Database opened"<<std::endl;
2730 bool ServerMap::loadFromFolders() {
2731 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2736 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2737 return (sqlite3_int64)pos.Z*16777216 +
2738 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2741 void ServerMap::createDirs(std::string path)
2743 if(fs::CreateAllDirs(path) == false)
2745 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2746 <<"\""<<path<<"\""<<std::endl;
2747 throw BaseException("ServerMap failed to create directory");
2751 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2757 snprintf(cc, 9, "%.4x%.4x",
2758 (unsigned int)pos.X&0xffff,
2759 (unsigned int)pos.Y&0xffff);
2761 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2763 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2764 (unsigned int)pos.X&0xfff,
2765 (unsigned int)pos.Y&0xfff);
2767 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2773 v2s16 ServerMap::getSectorPos(std::string dirname)
2777 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2778 assert(spos != std::string::npos);
2779 if(dirname.size() - spos == 8)
2782 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2784 else if(dirname.size() - spos == 3)
2787 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2788 // Sign-extend the 12 bit values up to 16 bits...
2789 if(x&0x800) x|=0xF000;
2790 if(y&0x800) y|=0xF000;
2797 v2s16 pos((s16)x, (s16)y);
2801 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2803 v2s16 p2d = getSectorPos(sectordir);
2805 if(blockfile.size() != 4){
2806 throw InvalidFilenameException("Invalid block filename");
2809 int r = sscanf(blockfile.c_str(), "%4x", &y);
2811 throw InvalidFilenameException("Invalid block filename");
2812 return v3s16(p2d.X, y, p2d.Y);
2815 std::string ServerMap::getBlockFilename(v3s16 p)
2818 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2822 void ServerMap::save(bool only_changed)
2824 DSTACK(__FUNCTION_NAME);
2825 if(m_map_saving_enabled == false)
2827 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2831 if(only_changed == false)
2832 infostream<<"ServerMap: Saving whole map, this can take time."
2835 if(only_changed == false || m_map_metadata_changed)
2840 // Profile modified reasons
2841 Profiler modprofiler;
2843 u32 sector_meta_count = 0;
2844 u32 block_count = 0;
2845 u32 block_count_all = 0; // Number of blocks in memory
2847 // Don't do anything with sqlite unless something is really saved
2848 bool save_started = false;
2850 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2851 for(; i.atEnd() == false; i++)
2853 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2854 assert(sector->getId() == MAPSECTOR_SERVER);
2856 if(sector->differs_from_disk || only_changed == false)
2858 saveSectorMeta(sector);
2859 sector_meta_count++;
2861 core::list<MapBlock*> blocks;
2862 sector->getBlocks(blocks);
2863 core::list<MapBlock*>::Iterator j;
2865 for(j=blocks.begin(); j!=blocks.end(); j++)
2867 MapBlock *block = *j;
2871 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2872 || only_changed == false)
2877 save_started = true;
2880 modprofiler.add(block->getModifiedReason(), 1);
2885 /*infostream<<"ServerMap: Written block ("
2886 <<block->getPos().X<<","
2887 <<block->getPos().Y<<","
2888 <<block->getPos().Z<<")"
2897 Only print if something happened or saved whole map
2899 if(only_changed == false || sector_meta_count != 0
2900 || block_count != 0)
2902 infostream<<"ServerMap: Written: "
2903 <<sector_meta_count<<" sector metadata files, "
2904 <<block_count<<" block files"
2905 <<", "<<block_count_all<<" blocks in memory."
2907 PrintInfo(infostream); // ServerMap/ClientMap:
2908 infostream<<"Blocks modified by: "<<std::endl;
2909 modprofiler.print(infostream);
2913 static s32 unsignedToSigned(s32 i, s32 max_positive)
2915 if(i < max_positive)
2918 return i - 2*max_positive;
2921 // modulo of a negative number does not work consistently in C
2922 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2926 return mod - ((-i) % mod);
2929 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2931 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2933 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2935 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2936 return v3s16(x,y,z);
2939 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2941 if(loadFromFolders()){
2942 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2943 <<"all blocks that are stored in flat files"<<std::endl;
2949 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2951 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2952 v3s16 p = getIntegerAsBlock(block_i);
2953 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2959 void ServerMap::saveMapMeta()
2961 DSTACK(__FUNCTION_NAME);
2963 infostream<<"ServerMap::saveMapMeta(): "
2967 createDirs(m_savedir);
2969 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2970 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2971 if(os.good() == false)
2973 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2974 <<"could not open"<<fullpath<<std::endl;
2975 throw FileNotGoodException("Cannot open chunk metadata");
2979 params.setU64("seed", m_seed);
2981 params.writeLines(os);
2983 os<<"[end_of_params]\n";
2985 m_map_metadata_changed = false;
2988 void ServerMap::loadMapMeta()
2990 DSTACK(__FUNCTION_NAME);
2992 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2995 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2996 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2997 if(is.good() == false)
2999 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3000 <<"could not open"<<fullpath<<std::endl;
3001 throw FileNotGoodException("Cannot open map metadata");
3009 throw SerializationError
3010 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3012 std::getline(is, line);
3013 std::string trimmedline = trim(line);
3014 if(trimmedline == "[end_of_params]")
3016 params.parseConfigLine(line);
3019 m_seed = params.getU64("seed");
3021 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3024 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3026 DSTACK(__FUNCTION_NAME);
3027 // Format used for writing
3028 u8 version = SER_FMT_VER_HIGHEST;
3030 v2s16 pos = sector->getPos();
3031 std::string dir = getSectorDir(pos);
3034 std::string fullpath = dir + DIR_DELIM + "meta";
3035 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3036 if(o.good() == false)
3037 throw FileNotGoodException("Cannot open sector metafile");
3039 sector->serialize(o, version);
3041 sector->differs_from_disk = false;
3044 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3046 DSTACK(__FUNCTION_NAME);
3048 v2s16 p2d = getSectorPos(sectordir);
3050 ServerMapSector *sector = NULL;
3052 std::string fullpath = sectordir + DIR_DELIM + "meta";
3053 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3054 if(is.good() == false)
3056 // If the directory exists anyway, it probably is in some old
3057 // format. Just go ahead and create the sector.
3058 if(fs::PathExists(sectordir))
3060 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3061 <<fullpath<<" doesn't exist but directory does."
3062 <<" Continuing with a sector with no metadata."
3064 sector = new ServerMapSector(this, p2d, m_gamedef);
3065 m_sectors.insert(p2d, sector);
3069 throw FileNotGoodException("Cannot open sector metafile");
3074 sector = ServerMapSector::deSerialize
3075 (is, this, p2d, m_sectors, m_gamedef);
3077 saveSectorMeta(sector);
3080 sector->differs_from_disk = false;
3085 bool ServerMap::loadSectorMeta(v2s16 p2d)
3087 DSTACK(__FUNCTION_NAME);
3089 MapSector *sector = NULL;
3091 // The directory layout we're going to load from.
3092 // 1 - original sectors/xxxxzzzz/
3093 // 2 - new sectors2/xxx/zzz/
3094 // If we load from anything but the latest structure, we will
3095 // immediately save to the new one, and remove the old.
3097 std::string sectordir1 = getSectorDir(p2d, 1);
3098 std::string sectordir;
3099 if(fs::PathExists(sectordir1))
3101 sectordir = sectordir1;
3106 sectordir = getSectorDir(p2d, 2);
3110 sector = loadSectorMeta(sectordir, loadlayout != 2);
3112 catch(InvalidFilenameException &e)
3116 catch(FileNotGoodException &e)
3120 catch(std::exception &e)
3129 bool ServerMap::loadSectorFull(v2s16 p2d)
3131 DSTACK(__FUNCTION_NAME);
3133 MapSector *sector = NULL;
3135 // The directory layout we're going to load from.
3136 // 1 - original sectors/xxxxzzzz/
3137 // 2 - new sectors2/xxx/zzz/
3138 // If we load from anything but the latest structure, we will
3139 // immediately save to the new one, and remove the old.
3141 std::string sectordir1 = getSectorDir(p2d, 1);
3142 std::string sectordir;
3143 if(fs::PathExists(sectordir1))
3145 sectordir = sectordir1;
3150 sectordir = getSectorDir(p2d, 2);
3154 sector = loadSectorMeta(sectordir, loadlayout != 2);
3156 catch(InvalidFilenameException &e)
3160 catch(FileNotGoodException &e)
3164 catch(std::exception &e)
3172 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3174 std::vector<fs::DirListNode>::iterator i2;
3175 for(i2=list2.begin(); i2!=list2.end(); i2++)
3181 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3183 catch(InvalidFilenameException &e)
3185 // This catches unknown crap in directory
3191 infostream<<"Sector converted to new layout - deleting "<<
3192 sectordir1<<std::endl;
3193 fs::RecursiveDelete(sectordir1);
3200 void ServerMap::beginSave() {
3202 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3203 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3206 void ServerMap::endSave() {
3208 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3209 infostream<<"WARNING: endSave() failed, map might not have saved.";
3212 void ServerMap::saveBlock(MapBlock *block)
3214 DSTACK(__FUNCTION_NAME);
3216 Dummy blocks are not written
3218 if(block->isDummy())
3220 /*v3s16 p = block->getPos();
3221 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3222 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3226 // Format used for writing
3227 u8 version = SER_FMT_VER_HIGHEST;
3229 v3s16 p3d = block->getPos();
3233 v2s16 p2d(p3d.X, p3d.Z);
3234 std::string sectordir = getSectorDir(p2d);
3236 createDirs(sectordir);
3238 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3239 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3240 if(o.good() == false)
3241 throw FileNotGoodException("Cannot open block data");
3244 [0] u8 serialization version
3250 std::ostringstream o(std::ios_base::binary);
3252 o.write((char*)&version, 1);
3255 block->serialize(o, version);
3257 // Write extra data stored on disk
3258 block->serializeDiskExtra(o, version);
3260 // Write block to database
3262 std::string tmp = o.str();
3263 const char *bytes = tmp.c_str();
3265 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3266 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3267 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3268 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3269 int written = sqlite3_step(m_database_write);
3270 if(written != SQLITE_DONE)
3271 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3272 <<sqlite3_errmsg(m_database)<<std::endl;
3273 // Make ready for later reuse
3274 sqlite3_reset(m_database_write);
3276 // We just wrote it to the disk so clear modified flag
3277 block->resetModified();
3280 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3282 DSTACK(__FUNCTION_NAME);
3284 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3287 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3288 if(is.good() == false)
3289 throw FileNotGoodException("Cannot open block file");
3291 v3s16 p3d = getBlockPos(sectordir, blockfile);
3292 v2s16 p2d(p3d.X, p3d.Z);
3294 assert(sector->getPos() == p2d);
3296 u8 version = SER_FMT_VER_INVALID;
3297 is.read((char*)&version, 1);
3300 throw SerializationError("ServerMap::loadBlock(): Failed"
3301 " to read MapBlock version");
3303 /*u32 block_size = MapBlock::serializedLength(version);
3304 SharedBuffer<u8> data(block_size);
3305 is.read((char*)*data, block_size);*/
3307 // This will always return a sector because we're the server
3308 //MapSector *sector = emergeSector(p2d);
3310 MapBlock *block = NULL;
3311 bool created_new = false;
3312 block = sector->getBlockNoCreateNoEx(p3d.Y);
3315 block = sector->createBlankBlockNoInsert(p3d.Y);
3320 block->deSerialize(is, version);
3322 // Read extra data stored on disk
3323 block->deSerializeDiskExtra(is, version);
3325 // If it's a new block, insert it to the map
3327 sector->insertBlock(block);
3330 Save blocks loaded in old format in new format
3333 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3337 // Should be in database now, so delete the old file
3338 fs::RecursiveDelete(fullpath);
3341 // We just loaded it from the disk, so it's up-to-date.
3342 block->resetModified();
3345 catch(SerializationError &e)
3347 infostream<<"WARNING: Invalid block data on disk "
3348 <<"fullpath="<<fullpath
3349 <<" (SerializationError). "
3350 <<"what()="<<e.what()
3352 //" Ignoring. A new one will be generated.
3355 // TODO: Backup file; name is in fullpath.
3359 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3361 DSTACK(__FUNCTION_NAME);
3364 std::istringstream is(*blob, std::ios_base::binary);
3366 u8 version = SER_FMT_VER_INVALID;
3367 is.read((char*)&version, 1);
3370 throw SerializationError("ServerMap::loadBlock(): Failed"
3371 " to read MapBlock version");
3373 /*u32 block_size = MapBlock::serializedLength(version);
3374 SharedBuffer<u8> data(block_size);
3375 is.read((char*)*data, block_size);*/
3377 // This will always return a sector because we're the server
3378 //MapSector *sector = emergeSector(p2d);
3380 MapBlock *block = NULL;
3381 bool created_new = false;
3382 block = sector->getBlockNoCreateNoEx(p3d.Y);
3385 block = sector->createBlankBlockNoInsert(p3d.Y);
3390 block->deSerialize(is, version);
3392 // Read extra data stored on disk
3393 block->deSerializeDiskExtra(is, version);
3395 // If it's a new block, insert it to the map
3397 sector->insertBlock(block);
3400 Save blocks loaded in old format in new format
3403 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3404 // Only save if asked to; no need to update version
3408 // We just loaded it from, so it's up-to-date.
3409 block->resetModified();
3412 catch(SerializationError &e)
3414 infostream<<"WARNING: Invalid block data in database "
3415 <<" (SerializationError). "
3416 <<"what()="<<e.what()
3418 //" Ignoring. A new one will be generated.
3421 // TODO: Copy to a backup database.
3425 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3427 DSTACK(__FUNCTION_NAME);
3429 v2s16 p2d(blockpos.X, blockpos.Z);
3431 if(!loadFromFolders()) {
3434 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3435 infostream<<"WARNING: Could not bind block position for load: "
3436 <<sqlite3_errmsg(m_database)<<std::endl;
3437 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3439 Make sure sector is loaded
3441 MapSector *sector = createSector(p2d);
3446 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3447 size_t len = sqlite3_column_bytes(m_database_read, 0);
3449 std::string datastr(data, len);
3451 loadBlock(&datastr, blockpos, sector, false);
3453 sqlite3_step(m_database_read);
3454 // We should never get more than 1 row, so ok to reset
3455 sqlite3_reset(m_database_read);
3457 return getBlockNoCreateNoEx(blockpos);
3459 sqlite3_reset(m_database_read);
3461 // Not found in database, try the files
3464 // The directory layout we're going to load from.
3465 // 1 - original sectors/xxxxzzzz/
3466 // 2 - new sectors2/xxx/zzz/
3467 // If we load from anything but the latest structure, we will
3468 // immediately save to the new one, and remove the old.
3470 std::string sectordir1 = getSectorDir(p2d, 1);
3471 std::string sectordir;
3472 if(fs::PathExists(sectordir1))
3474 sectordir = sectordir1;
3479 sectordir = getSectorDir(p2d, 2);
3483 Make sure sector is loaded
3485 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3489 sector = loadSectorMeta(sectordir, loadlayout != 2);
3491 catch(InvalidFilenameException &e)
3495 catch(FileNotGoodException &e)
3499 catch(std::exception &e)
3506 Make sure file exists
3509 std::string blockfilename = getBlockFilename(blockpos);
3510 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3514 Load block and save it to the database
3516 loadBlock(sectordir, blockfilename, sector, true);
3517 return getBlockNoCreateNoEx(blockpos);
3520 void ServerMap::PrintInfo(std::ostream &out)
3531 ClientMap::ClientMap(
3534 MapDrawControl &control,
3535 scene::ISceneNode* parent,
3536 scene::ISceneManager* mgr,
3539 Map(dout_client, gamedef),
3540 scene::ISceneNode(parent, mgr, id),
3543 m_camera_position(0,0,0),
3544 m_camera_direction(0,0,1),
3547 m_camera_mutex.Init();
3548 assert(m_camera_mutex.IsInitialized());
3550 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3551 BS*1000000,BS*1000000,BS*1000000);
3554 ClientMap::~ClientMap()
3556 /*JMutexAutoLock lock(mesh_mutex);
3565 MapSector * ClientMap::emergeSector(v2s16 p2d)
3567 DSTACK(__FUNCTION_NAME);
3568 // Check that it doesn't exist already
3570 return getSectorNoGenerate(p2d);
3572 catch(InvalidPositionException &e)
3577 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3580 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3581 m_sectors.insert(p2d, sector);
3588 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3590 DSTACK(__FUNCTION_NAME);
3591 ClientMapSector *sector = NULL;
3593 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3595 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3599 sector = (ClientMapSector*)n->getValue();
3600 assert(sector->getId() == MAPSECTOR_CLIENT);
3604 sector = new ClientMapSector(this, p2d);
3606 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3607 m_sectors.insert(p2d, sector);
3611 sector->deSerialize(is);
3615 void ClientMap::OnRegisterSceneNode()
3619 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3620 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3623 ISceneNode::OnRegisterSceneNode();
3626 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3627 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3629 float d0 = (float)BS * p0.getDistanceFrom(p1);
3631 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3633 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3635 for(float s=start_off; s<d0+end_off; s+=step){
3636 v3f pf = p0f + uf * s;
3637 v3s16 p = floatToInt(pf, BS);
3638 MapNode n = map->getNodeNoEx(p);
3639 bool is_transparent = false;
3640 const ContentFeatures &f = nodemgr->get(n);
3641 if(f.solidness == 0)
3642 is_transparent = (f.visual_solidness != 2);
3644 is_transparent = (f.solidness != 2);
3645 if(!is_transparent){
3647 if(count >= needed_count)
3655 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3657 INodeDefManager *nodemgr = m_gamedef->ndef();
3659 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3660 DSTACK(__FUNCTION_NAME);
3662 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3665 if(pass == scene::ESNRP_SOLID)
3666 prefix = "CM: solid: ";
3668 prefix = "CM: transparent: ";
3671 This is called two times per frame, reset on the non-transparent one
3673 if(pass == scene::ESNRP_SOLID)
3675 m_last_drawn_sectors.clear();
3679 Get time for measuring timeout.
3681 Measuring time is very useful for long delays when the
3682 machine is swapping a lot.
3684 int time1 = time(0);
3686 //u32 daynight_ratio = m_client->getDayNightRatio();
3688 m_camera_mutex.Lock();
3689 v3f camera_position = m_camera_position;
3690 v3f camera_direction = m_camera_direction;
3691 f32 camera_fov = m_camera_fov;
3692 m_camera_mutex.Unlock();
3695 Get all blocks and draw all visible ones
3698 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3700 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3702 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3703 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3705 // Take a fair amount as we will be dropping more out later
3706 // Umm... these additions are a bit strange but they are needed.
3708 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3709 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3710 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3712 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3713 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3714 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3716 u32 vertex_count = 0;
3717 u32 meshbuffer_count = 0;
3719 // For limiting number of mesh updates per frame
3720 u32 mesh_update_count = 0;
3722 // Number of blocks in rendering range
3723 u32 blocks_in_range = 0;
3724 // Number of blocks occlusion culled
3725 u32 blocks_occlusion_culled = 0;
3726 // Number of blocks in rendering range but don't have a mesh
3727 u32 blocks_in_range_without_mesh = 0;
3728 // Blocks that had mesh that would have been drawn according to
3729 // rendering range (if max blocks limit didn't kick in)
3730 u32 blocks_would_have_drawn = 0;
3731 // Blocks that were drawn and had a mesh
3732 u32 blocks_drawn = 0;
3733 // Blocks which had a corresponding meshbuffer for this pass
3734 u32 blocks_had_pass_meshbuf = 0;
3735 // Blocks from which stuff was actually drawn
3736 u32 blocks_without_stuff = 0;
3739 Collect a set of blocks for drawing
3742 core::map<v3s16, MapBlock*> drawset;
3745 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3747 for(core::map<v2s16, MapSector*>::Iterator
3748 si = m_sectors.getIterator();
3749 si.atEnd() == false; si++)
3751 MapSector *sector = si.getNode()->getValue();
3752 v2s16 sp = sector->getPos();
3754 if(m_control.range_all == false)
3756 if(sp.X < p_blocks_min.X
3757 || sp.X > p_blocks_max.X
3758 || sp.Y < p_blocks_min.Z
3759 || sp.Y > p_blocks_max.Z)
3763 core::list< MapBlock * > sectorblocks;
3764 sector->getBlocks(sectorblocks);
3767 Loop through blocks in sector
3770 u32 sector_blocks_drawn = 0;
3772 core::list< MapBlock * >::Iterator i;
3773 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3775 MapBlock *block = *i;
3778 Compare block position to camera position, skip
3779 if not seen on display
3782 float range = 100000 * BS;
3783 if(m_control.range_all == false)
3784 range = m_control.wanted_range * BS;
3787 if(isBlockInSight(block->getPos(), camera_position,
3788 camera_direction, camera_fov,
3789 range, &d) == false)
3794 // This is ugly (spherical distance limit?)
3795 /*if(m_control.range_all == false &&
3796 d - 0.5*BS*MAP_BLOCKSIZE > range)
3803 Update expired mesh (used for day/night change)
3805 It doesn't work exactly like it should now with the
3806 tasked mesh update but whatever.
3809 bool mesh_expired = false;
3812 JMutexAutoLock lock(block->mesh_mutex);
3814 mesh_expired = block->getMeshExpired();
3816 // Mesh has not been expired and there is no mesh:
3817 // block has no content
3818 if(block->mesh == NULL && mesh_expired == false){
3819 blocks_in_range_without_mesh++;
3824 f32 faraway = BS*50;
3825 //f32 faraway = m_control.wanted_range * BS;
3828 This has to be done with the mesh_mutex unlocked
3830 // Pretty random but this should work somewhat nicely
3831 if(mesh_expired && (
3832 (mesh_update_count < 3
3833 && (d < faraway || mesh_update_count < 2)
3836 (m_control.range_all && mesh_update_count < 20)
3839 /*if(mesh_expired && mesh_update_count < 6
3840 && (d < faraway || mesh_update_count < 3))*/
3842 mesh_update_count++;
3844 // Mesh has been expired: generate new mesh
3845 //block->updateMesh(daynight_ratio);
3846 m_client->addUpdateMeshTask(block->getPos());
3848 mesh_expired = false;
3856 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3857 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3859 float stepfac = 1.1;
3860 float startoff = BS*1;
3861 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3862 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3863 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3864 u32 needed_count = 1;
3866 isOccluded(this, spn, cpn + v3s16(0,0,0),
3867 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3868 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3869 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3870 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3871 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3872 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3873 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3874 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3875 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3876 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3877 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3878 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3879 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3880 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3881 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3882 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3883 step, stepfac, startoff, endoff, needed_count, nodemgr)
3886 blocks_occlusion_culled++;
3890 // This block is in range. Reset usage timer.
3891 block->resetUsageTimer();
3894 Ignore if mesh doesn't exist
3897 JMutexAutoLock lock(block->mesh_mutex);
3899 scene::SMesh *mesh = block->mesh;
3902 blocks_in_range_without_mesh++;
3907 // Limit block count in case of a sudden increase
3908 blocks_would_have_drawn++;
3909 if(blocks_drawn >= m_control.wanted_max_blocks
3910 && m_control.range_all == false
3911 && d > m_control.wanted_min_range * BS)
3915 drawset[block->getPos()] = block;
3917 sector_blocks_drawn++;
3920 } // foreach sectorblocks
3922 if(sector_blocks_drawn != 0)
3923 m_last_drawn_sectors[sp] = true;
3928 Draw the selected MapBlocks
3932 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3934 int timecheck_counter = 0;
3935 for(core::map<v3s16, MapBlock*>::Iterator
3936 i = drawset.getIterator();
3937 i.atEnd() == false; i++)
3940 timecheck_counter++;
3941 if(timecheck_counter > 50)
3943 timecheck_counter = 0;
3944 int time2 = time(0);
3945 if(time2 > time1 + 4)
3947 infostream<<"ClientMap::renderMap(): "
3948 "Rendering takes ages, returning."
3955 MapBlock *block = i.getNode()->getValue();
3958 Draw the faces of the block
3961 JMutexAutoLock lock(block->mesh_mutex);
3963 scene::SMesh *mesh = block->mesh;
3966 u32 c = mesh->getMeshBufferCount();
3967 bool stuff_actually_drawn = false;
3968 for(u32 i=0; i<c; i++)
3970 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3971 const video::SMaterial& material = buf->getMaterial();
3972 video::IMaterialRenderer* rnd =
3973 driver->getMaterialRenderer(material.MaterialType);
3974 bool transparent = (rnd && rnd->isTransparent());
3975 // Render transparent on transparent pass and likewise.
3976 if(transparent == is_transparent_pass)
3978 if(buf->getVertexCount() == 0)
3979 errorstream<<"Block ["<<analyze_block(block)
3980 <<"] contains an empty meshbuf"<<std::endl;
3982 This *shouldn't* hurt too much because Irrlicht
3983 doesn't change opengl textures if the old
3984 material has the same texture.
3986 driver->setMaterial(buf->getMaterial());
3987 driver->drawMeshBuffer(buf);
3988 vertex_count += buf->getVertexCount();
3990 stuff_actually_drawn = true;
3993 if(stuff_actually_drawn)
3994 blocks_had_pass_meshbuf++;
3996 blocks_without_stuff++;
4001 // Log only on solid pass because values are the same
4002 if(pass == scene::ESNRP_SOLID){
4003 g_profiler->avg("CM: blocks in range", blocks_in_range);
4004 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
4005 if(blocks_in_range != 0)
4006 g_profiler->avg("CM: blocks in range without mesh (frac)",
4007 (float)blocks_in_range_without_mesh/blocks_in_range);
4008 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4011 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4012 if(blocks_had_pass_meshbuf != 0)
4013 g_profiler->avg(prefix+"meshbuffers per block",
4014 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4015 if(blocks_drawn != 0)
4016 g_profiler->avg(prefix+"empty blocks (frac)",
4017 (float)blocks_without_stuff / blocks_drawn);
4019 m_control.blocks_drawn = blocks_drawn;
4020 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4022 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4023 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4026 void ClientMap::renderPostFx()
4028 INodeDefManager *nodemgr = m_gamedef->ndef();
4030 // Sadly ISceneManager has no "post effects" render pass, in that case we
4031 // could just register for that and handle it in renderMap().
4033 m_camera_mutex.Lock();
4034 v3f camera_position = m_camera_position;
4035 m_camera_mutex.Unlock();
4037 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4039 // - If the player is in a solid node, make everything black.
4040 // - If the player is in liquid, draw a semi-transparent overlay.
4041 const ContentFeatures& features = nodemgr->get(n);
4042 video::SColor post_effect_color = features.post_effect_color;
4043 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4045 post_effect_color = video::SColor(255, 0, 0, 0);
4047 if (post_effect_color.getAlpha() != 0)
4049 // Draw a full-screen rectangle
4050 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4051 v2u32 ss = driver->getScreenSize();
4052 core::rect<s32> rect(0,0, ss.X, ss.Y);
4053 driver->draw2DRectangle(post_effect_color, rect);
4057 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4058 core::map<v3s16, MapBlock*> *affected_blocks)
4060 bool changed = false;
4062 Add it to all blocks touching it
4065 v3s16(0,0,0), // this
4066 v3s16(0,0,1), // back
4067 v3s16(0,1,0), // top
4068 v3s16(1,0,0), // right
4069 v3s16(0,0,-1), // front
4070 v3s16(0,-1,0), // bottom
4071 v3s16(-1,0,0), // left
4073 for(u16 i=0; i<7; i++)
4075 v3s16 p2 = p + dirs[i];
4076 // Block position of neighbor (or requested) node
4077 v3s16 blockpos = getNodeBlockPos(p2);
4078 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4079 if(blockref == NULL)
4081 // Relative position of requested node
4082 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4083 if(blockref->setTempMod(relpos, mod))
4088 if(changed && affected_blocks!=NULL)
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 affected_blocks->insert(blockpos, blockref);
4104 bool ClientMap::clearTempMod(v3s16 p,
4105 core::map<v3s16, MapBlock*> *affected_blocks)
4107 bool changed = false;
4109 v3s16(0,0,0), // this
4110 v3s16(0,0,1), // back
4111 v3s16(0,1,0), // top
4112 v3s16(1,0,0), // right
4113 v3s16(0,0,-1), // front
4114 v3s16(0,-1,0), // bottom
4115 v3s16(-1,0,0), // left
4117 for(u16 i=0; i<7; i++)
4119 v3s16 p2 = p + dirs[i];
4120 // Block position of neighbor (or requested) node
4121 v3s16 blockpos = getNodeBlockPos(p2);
4122 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4123 if(blockref == NULL)
4125 // Relative position of requested node
4126 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4127 if(blockref->clearTempMod(relpos))
4132 if(changed && affected_blocks!=NULL)
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 affected_blocks->insert(blockpos, blockref);
4148 void ClientMap::expireMeshes(bool only_daynight_diffed)
4150 TimeTaker timer("expireMeshes()");
4152 core::map<v2s16, MapSector*>::Iterator si;
4153 si = m_sectors.getIterator();
4154 for(; si.atEnd() == false; si++)
4156 MapSector *sector = si.getNode()->getValue();
4158 core::list< MapBlock * > sectorblocks;
4159 sector->getBlocks(sectorblocks);
4161 core::list< MapBlock * >::Iterator i;
4162 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4164 MapBlock *block = *i;
4166 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4172 JMutexAutoLock lock(block->mesh_mutex);
4173 if(block->mesh != NULL)
4175 /*block->mesh->drop();
4176 block->mesh = NULL;*/
4177 block->setMeshExpired(true);
4184 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4186 assert(mapType() == MAPTYPE_CLIENT);
4189 v3s16 p = blockpos + v3s16(0,0,0);
4190 MapBlock *b = getBlockNoCreate(p);
4191 b->updateMesh(daynight_ratio);
4192 //b->setMeshExpired(true);
4194 catch(InvalidPositionException &e){}
4197 v3s16 p = blockpos + v3s16(-1,0,0);
4198 MapBlock *b = getBlockNoCreate(p);
4199 b->updateMesh(daynight_ratio);
4200 //b->setMeshExpired(true);
4202 catch(InvalidPositionException &e){}
4204 v3s16 p = blockpos + v3s16(0,-1,0);
4205 MapBlock *b = getBlockNoCreate(p);
4206 b->updateMesh(daynight_ratio);
4207 //b->setMeshExpired(true);
4209 catch(InvalidPositionException &e){}
4211 v3s16 p = blockpos + v3s16(0,0,-1);
4212 MapBlock *b = getBlockNoCreate(p);
4213 b->updateMesh(daynight_ratio);
4214 //b->setMeshExpired(true);
4216 catch(InvalidPositionException &e){}
4221 Update mesh of block in which the node is, and if the node is at the
4222 leading edge, update the appropriate leading blocks too.
4224 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4232 v3s16 blockposes[4];
4233 for(u32 i=0; i<4; i++)
4235 v3s16 np = nodepos + dirs[i];
4236 blockposes[i] = getNodeBlockPos(np);
4237 // Don't update mesh of block if it has been done already
4238 bool already_updated = false;
4239 for(u32 j=0; j<i; j++)
4241 if(blockposes[j] == blockposes[i])
4243 already_updated = true;
4250 MapBlock *b = getBlockNoCreate(blockposes[i]);
4251 b->updateMesh(daynight_ratio);
4256 void ClientMap::PrintInfo(std::ostream &out)
4267 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4272 MapVoxelManipulator::~MapVoxelManipulator()
4274 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4278 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4280 TimeTaker timer1("emerge", &emerge_time);
4282 // Units of these are MapBlocks
4283 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4284 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4286 VoxelArea block_area_nodes
4287 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4289 addArea(block_area_nodes);
4291 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4292 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4293 for(s32 x=p_min.X; x<=p_max.X; x++)
4296 core::map<v3s16, bool>::Node *n;
4297 n = m_loaded_blocks.find(p);
4301 bool block_data_inexistent = false;
4304 TimeTaker timer1("emerge load", &emerge_load_time);
4306 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4307 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4309 a.print(infostream);
4310 infostream<<std::endl;*/
4312 MapBlock *block = m_map->getBlockNoCreate(p);
4313 if(block->isDummy())
4314 block_data_inexistent = true;
4316 block->copyTo(*this);
4318 catch(InvalidPositionException &e)
4320 block_data_inexistent = true;
4323 if(block_data_inexistent)
4325 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4326 // Fill with VOXELFLAG_INEXISTENT
4327 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4328 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4330 s32 i = m_area.index(a.MinEdge.X,y,z);
4331 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4335 m_loaded_blocks.insert(p, !block_data_inexistent);
4338 //infostream<<"emerge done"<<std::endl;
4342 SUGG: Add an option to only update eg. water and air nodes.
4343 This will make it interfere less with important stuff if
4346 void MapVoxelManipulator::blitBack
4347 (core::map<v3s16, MapBlock*> & modified_blocks)
4349 if(m_area.getExtent() == v3s16(0,0,0))
4352 //TimeTaker timer1("blitBack");
4354 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4355 <<m_loaded_blocks.size()<<std::endl;*/
4358 Initialize block cache
4360 v3s16 blockpos_last;
4361 MapBlock *block = NULL;
4362 bool block_checked_in_modified = false;
4364 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4365 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4366 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4370 u8 f = m_flags[m_area.index(p)];
4371 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4374 MapNode &n = m_data[m_area.index(p)];
4376 v3s16 blockpos = getNodeBlockPos(p);
4381 if(block == NULL || blockpos != blockpos_last){
4382 block = m_map->getBlockNoCreate(blockpos);
4383 blockpos_last = blockpos;
4384 block_checked_in_modified = false;
4387 // Calculate relative position in block
4388 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4390 // Don't continue if nothing has changed here
4391 if(block->getNode(relpos) == n)
4394 //m_map->setNode(m_area.MinEdge + p, n);
4395 block->setNode(relpos, n);
4398 Make sure block is in modified_blocks
4400 if(block_checked_in_modified == false)
4402 modified_blocks[blockpos] = block;
4403 block_checked_in_modified = true;
4406 catch(InvalidPositionException &e)
4412 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4413 MapVoxelManipulator(map),
4414 m_create_area(false)
4418 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4422 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4424 // Just create the area so that it can be pointed to
4425 VoxelManipulator::emerge(a, caller_id);
4428 void ManualMapVoxelManipulator::initialEmerge(
4429 v3s16 blockpos_min, v3s16 blockpos_max)
4431 TimeTaker timer1("initialEmerge", &emerge_time);
4433 // Units of these are MapBlocks
4434 v3s16 p_min = blockpos_min;
4435 v3s16 p_max = blockpos_max;
4437 VoxelArea block_area_nodes
4438 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4440 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4443 infostream<<"initialEmerge: area: ";
4444 block_area_nodes.print(infostream);
4445 infostream<<" ("<<size_MB<<"MB)";
4446 infostream<<std::endl;
4449 addArea(block_area_nodes);
4451 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4452 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4453 for(s32 x=p_min.X; x<=p_max.X; x++)
4456 core::map<v3s16, bool>::Node *n;
4457 n = m_loaded_blocks.find(p);
4461 bool block_data_inexistent = false;
4464 TimeTaker timer1("emerge load", &emerge_load_time);
4466 MapBlock *block = m_map->getBlockNoCreate(p);
4467 if(block->isDummy())
4468 block_data_inexistent = true;
4470 block->copyTo(*this);
4472 catch(InvalidPositionException &e)
4474 block_data_inexistent = true;
4477 if(block_data_inexistent)
4480 Mark area inexistent
4482 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4483 // Fill with VOXELFLAG_INEXISTENT
4484 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4485 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4487 s32 i = m_area.index(a.MinEdge.X,y,z);
4488 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4492 m_loaded_blocks.insert(p, !block_data_inexistent);
4496 void ManualMapVoxelManipulator::blitBackAll(
4497 core::map<v3s16, MapBlock*> * modified_blocks)
4499 if(m_area.getExtent() == v3s16(0,0,0))
4503 Copy data of all blocks
4505 for(core::map<v3s16, bool>::Iterator
4506 i = m_loaded_blocks.getIterator();
4507 i.atEnd() == false; i++)
4509 v3s16 p = i.getNode()->getKey();
4510 bool existed = i.getNode()->getValue();
4511 if(existed == false)
4513 // The Great Bug was found using this
4514 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4515 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4519 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4522 infostream<<"WARNING: "<<__FUNCTION_NAME
4523 <<": got NULL block "
4524 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4529 block->copyFrom(*this);
4532 modified_blocks->insert(p, block);