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;
1408 u32 block_count_all = 0;
1410 core::map<v2s16, MapSector*>::Iterator si;
1413 si = m_sectors.getIterator();
1414 for(; si.atEnd() == false; si++)
1416 MapSector *sector = si.getNode()->getValue();
1418 bool all_blocks_deleted = true;
1420 core::list<MapBlock*> blocks;
1421 sector->getBlocks(blocks);
1423 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1424 i != blocks.end(); i++)
1426 MapBlock *block = (*i);
1428 block->incrementUsageTimer(dtime);
1430 if(block->getUsageTimer() > unload_timeout)
1432 v3s16 p = block->getPos();
1435 if(block->getModified() != MOD_STATE_CLEAN
1436 && save_before_unloading)
1438 modprofiler.add(block->getModifiedReason(), 1);
1440 saved_blocks_count++;
1443 // Delete from memory
1444 sector->deleteBlock(block);
1447 unloaded_blocks->push_back(p);
1449 deleted_blocks_count++;
1453 all_blocks_deleted = false;
1458 if(all_blocks_deleted)
1460 sector_deletion_queue.push_back(si.getNode()->getKey());
1465 // Finally delete the empty sectors
1466 deleteSectors(sector_deletion_queue);
1468 if(deleted_blocks_count != 0)
1470 PrintInfo(infostream); // ServerMap/ClientMap:
1471 infostream<<"Unloaded "<<deleted_blocks_count
1472 <<" blocks from memory";
1473 if(save_before_unloading)
1474 infostream<<", of which "<<saved_blocks_count<<" were written";
1475 infostream<<", "<<block_count_all<<" blocks in memory";
1476 infostream<<"."<<std::endl;
1477 if(saved_blocks_count != 0){
1478 PrintInfo(infostream); // ServerMap/ClientMap:
1479 infostream<<"Blocks modified by: "<<std::endl;
1480 modprofiler.print(infostream);
1485 void Map::deleteSectors(core::list<v2s16> &list)
1487 core::list<v2s16>::Iterator j;
1488 for(j=list.begin(); j!=list.end(); j++)
1490 MapSector *sector = m_sectors[*j];
1491 // If sector is in sector cache, remove it from there
1492 if(m_sector_cache == sector)
1493 m_sector_cache = NULL;
1494 // Remove from map and delete
1495 m_sectors.remove(*j);
1501 void Map::unloadUnusedData(float timeout,
1502 core::list<v3s16> *deleted_blocks)
1504 core::list<v2s16> sector_deletion_queue;
1505 u32 deleted_blocks_count = 0;
1506 u32 saved_blocks_count = 0;
1508 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1509 for(; si.atEnd() == false; si++)
1511 MapSector *sector = si.getNode()->getValue();
1513 bool all_blocks_deleted = true;
1515 core::list<MapBlock*> blocks;
1516 sector->getBlocks(blocks);
1517 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1518 i != blocks.end(); i++)
1520 MapBlock *block = (*i);
1522 if(block->getUsageTimer() > timeout)
1525 if(block->getModified() != MOD_STATE_CLEAN)
1528 saved_blocks_count++;
1530 // Delete from memory
1531 sector->deleteBlock(block);
1532 deleted_blocks_count++;
1536 all_blocks_deleted = false;
1540 if(all_blocks_deleted)
1542 sector_deletion_queue.push_back(si.getNode()->getKey());
1546 deleteSectors(sector_deletion_queue);
1548 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1549 <<", of which "<<saved_blocks_count<<" were wr."
1552 //return sector_deletion_queue.getSize();
1553 //return deleted_blocks_count;
1557 void Map::PrintInfo(std::ostream &out)
1562 #define WATER_DROP_BOOST 4
1566 NEIGHBOR_SAME_LEVEL,
1569 struct NodeNeighbor {
1575 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1577 INodeDefManager *nodemgr = m_gamedef->ndef();
1579 DSTACK(__FUNCTION_NAME);
1580 //TimeTaker timer("transformLiquids()");
1583 u32 initial_size = m_transforming_liquid.size();
1585 /*if(initial_size != 0)
1586 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1588 // list of nodes that due to viscosity have not reached their max level height
1589 UniqueQueue<v3s16> must_reflow;
1591 // List of MapBlocks that will require a lighting update (due to lava)
1592 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1594 while(m_transforming_liquid.size() != 0)
1596 // This should be done here so that it is done when continue is used
1597 if(loopcount >= initial_size * 3)
1602 Get a queued transforming liquid node
1604 v3s16 p0 = m_transforming_liquid.pop_front();
1606 MapNode n0 = getNodeNoEx(p0);
1609 Collect information about current node
1611 s8 liquid_level = -1;
1612 u8 liquid_kind = CONTENT_IGNORE;
1613 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1614 switch (liquid_type) {
1616 liquid_level = LIQUID_LEVEL_SOURCE;
1617 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1619 case LIQUID_FLOWING:
1620 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1621 liquid_kind = n0.getContent();
1624 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1625 // continue with the next node.
1626 if (n0.getContent() != CONTENT_AIR)
1628 liquid_kind = CONTENT_AIR;
1633 Collect information about the environment
1635 const v3s16 *dirs = g_6dirs;
1636 NodeNeighbor sources[6]; // surrounding sources
1637 int num_sources = 0;
1638 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1640 NodeNeighbor airs[6]; // surrounding air
1642 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1643 int num_neutrals = 0;
1644 bool flowing_down = false;
1645 for (u16 i = 0; i < 6; i++) {
1646 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1649 nt = NEIGHBOR_UPPER;
1652 nt = NEIGHBOR_LOWER;
1655 v3s16 npos = p0 + dirs[i];
1656 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1657 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1659 if (nb.n.getContent() == CONTENT_AIR) {
1660 airs[num_airs++] = nb;
1661 // if the current node is a water source the neighbor
1662 // should be enqueded for transformation regardless of whether the
1663 // current node changes or not.
1664 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1665 m_transforming_liquid.push_back(npos);
1666 // if the current node happens to be a flowing node, it will start to flow down here.
1667 if (nb.t == NEIGHBOR_LOWER) {
1668 flowing_down = true;
1671 neutrals[num_neutrals++] = nb;
1675 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1676 if (liquid_kind == CONTENT_AIR)
1677 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1678 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1679 neutrals[num_neutrals++] = nb;
1681 sources[num_sources++] = nb;
1684 case LIQUID_FLOWING:
1685 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1686 if (liquid_kind == CONTENT_AIR)
1687 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1688 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1689 neutrals[num_neutrals++] = nb;
1691 flows[num_flows++] = nb;
1692 if (nb.t == NEIGHBOR_LOWER)
1693 flowing_down = true;
1700 decide on the type (and possibly level) of the current node
1702 content_t new_node_content;
1703 s8 new_node_level = -1;
1704 s8 max_node_level = -1;
1705 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1706 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1707 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1708 // it's perfectly safe to use liquid_kind here to determine the new node content.
1709 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1710 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1711 // liquid_kind is set properly, see above
1712 new_node_content = liquid_kind;
1713 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1715 // no surrounding sources, so get the maximum level that can flow into this node
1716 for (u16 i = 0; i < num_flows; i++) {
1717 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1718 switch (flows[i].t) {
1719 case NEIGHBOR_UPPER:
1720 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1721 max_node_level = LIQUID_LEVEL_MAX;
1722 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1723 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1724 } else if (nb_liquid_level > max_node_level)
1725 max_node_level = nb_liquid_level;
1727 case NEIGHBOR_LOWER:
1729 case NEIGHBOR_SAME_LEVEL:
1730 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1731 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1732 max_node_level = nb_liquid_level - 1;
1738 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1739 if (viscosity > 1 && max_node_level != liquid_level) {
1740 // amount to gain, limited by viscosity
1741 // must be at least 1 in absolute value
1742 s8 level_inc = max_node_level - liquid_level;
1743 if (level_inc < -viscosity || level_inc > viscosity)
1744 new_node_level = liquid_level + level_inc/viscosity;
1745 else if (level_inc < 0)
1746 new_node_level = liquid_level - 1;
1747 else if (level_inc > 0)
1748 new_node_level = liquid_level + 1;
1749 if (new_node_level != max_node_level)
1750 must_reflow.push_back(p0);
1752 new_node_level = max_node_level;
1754 if (new_node_level >= 0)
1755 new_node_content = liquid_kind;
1757 new_node_content = CONTENT_AIR;
1762 check if anything has changed. if not, just continue with the next node.
1764 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1765 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1766 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1772 update the current node
1774 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1775 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1776 // set level to last 3 bits, flowing down bit to 4th bit
1777 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1779 // set the liquid level and flow bit to 0
1780 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1782 n0.setContent(new_node_content);
1784 v3s16 blockpos = getNodeBlockPos(p0);
1785 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1787 modified_blocks.insert(blockpos, block);
1788 // If node emits light, MapBlock requires lighting update
1789 if(nodemgr->get(n0).light_source != 0)
1790 lighting_modified_blocks[block->getPos()] = block;
1794 enqueue neighbors for update if neccessary
1796 switch (nodemgr->get(n0.getContent()).liquid_type) {
1798 case LIQUID_FLOWING:
1799 // make sure source flows into all neighboring nodes
1800 for (u16 i = 0; i < num_flows; i++)
1801 if (flows[i].t != NEIGHBOR_UPPER)
1802 m_transforming_liquid.push_back(flows[i].p);
1803 for (u16 i = 0; i < num_airs; i++)
1804 if (airs[i].t != NEIGHBOR_UPPER)
1805 m_transforming_liquid.push_back(airs[i].p);
1808 // this flow has turned to air; neighboring flows might need to do the same
1809 for (u16 i = 0; i < num_flows; i++)
1810 m_transforming_liquid.push_back(flows[i].p);
1814 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1815 while (must_reflow.size() > 0)
1816 m_transforming_liquid.push_back(must_reflow.pop_front());
1817 updateLighting(lighting_modified_blocks, modified_blocks);
1820 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1822 v3s16 blockpos = getNodeBlockPos(p);
1823 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1824 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1826 infostream<<"Map::getNodeMetadata(): Need to emerge "
1827 <<PP(blockpos)<<std::endl;
1828 block = emergeBlock(blockpos, false);
1832 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1836 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1840 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1842 v3s16 blockpos = getNodeBlockPos(p);
1843 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1844 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1846 infostream<<"Map::setNodeMetadata(): Need to emerge "
1847 <<PP(blockpos)<<std::endl;
1848 block = emergeBlock(blockpos, false);
1852 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1856 block->m_node_metadata->set(p_rel, meta);
1859 void Map::removeNodeMetadata(v3s16 p)
1861 v3s16 blockpos = getNodeBlockPos(p);
1862 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1863 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1866 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1870 block->m_node_metadata->remove(p_rel);
1873 void Map::nodeMetadataStep(float dtime,
1874 core::map<v3s16, MapBlock*> &changed_blocks)
1878 Currently there is no way to ensure that all the necessary
1879 blocks are loaded when this is run. (They might get unloaded)
1880 NOTE: ^- Actually, that might not be so. In a quick test it
1881 reloaded a block with a furnace when I walked back to it from
1884 core::map<v2s16, MapSector*>::Iterator si;
1885 si = m_sectors.getIterator();
1886 for(; si.atEnd() == false; si++)
1888 MapSector *sector = si.getNode()->getValue();
1889 core::list< MapBlock * > sectorblocks;
1890 sector->getBlocks(sectorblocks);
1891 core::list< MapBlock * >::Iterator i;
1892 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1894 MapBlock *block = *i;
1895 bool changed = block->m_node_metadata->step(dtime);
1897 changed_blocks[block->getPos()] = block;
1906 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1907 Map(dout_server, gamedef),
1909 m_map_metadata_changed(true),
1911 m_database_read(NULL),
1912 m_database_write(NULL)
1914 infostream<<__FUNCTION_NAME<<std::endl;
1916 //m_chunksize = 8; // Takes a few seconds
1918 if (g_settings->get("fixed_map_seed").empty())
1920 m_seed = (((u64)(myrand()%0xffff)<<0)
1921 + ((u64)(myrand()%0xffff)<<16)
1922 + ((u64)(myrand()%0xffff)<<32)
1923 + ((u64)(myrand()%0xffff)<<48));
1927 m_seed = g_settings->getU64("fixed_map_seed");
1931 Experimental and debug stuff
1938 Try to load map; if not found, create a new one.
1941 m_savedir = savedir;
1942 m_map_saving_enabled = false;
1946 // If directory exists, check contents and load if possible
1947 if(fs::PathExists(m_savedir))
1949 // If directory is empty, it is safe to save into it.
1950 if(fs::GetDirListing(m_savedir).size() == 0)
1952 infostream<<"Server: Empty save directory is valid."
1954 m_map_saving_enabled = true;
1959 // Load map metadata (seed, chunksize)
1962 catch(FileNotGoodException &e){
1963 infostream<<"WARNING: Could not load map metadata"
1964 //<<" Disabling chunk-based generator."
1970 // Load chunk metadata
1973 catch(FileNotGoodException &e){
1974 infostream<<"WARNING: Could not load chunk metadata."
1975 <<" Disabling chunk-based generator."
1980 /*infostream<<"Server: Successfully loaded chunk "
1981 "metadata and sector (0,0) from "<<savedir<<
1982 ", assuming valid save directory."
1985 infostream<<"Server: Successfully loaded map "
1986 <<"and chunk metadata from "<<savedir
1987 <<", assuming valid save directory."
1990 m_map_saving_enabled = true;
1991 // Map loaded, not creating new one
1995 // If directory doesn't exist, it is safe to save to it
1997 m_map_saving_enabled = true;
2000 catch(std::exception &e)
2002 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2003 <<", exception: "<<e.what()<<std::endl;
2004 infostream<<"Please remove the map or fix it."<<std::endl;
2005 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2008 infostream<<"Initializing new map."<<std::endl;
2010 // Create zero sector
2011 emergeSector(v2s16(0,0));
2013 // Initially write whole map
2017 ServerMap::~ServerMap()
2019 infostream<<__FUNCTION_NAME<<std::endl;
2023 if(m_map_saving_enabled)
2025 // Save only changed parts
2027 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2031 infostream<<"Server: map not saved"<<std::endl;
2034 catch(std::exception &e)
2036 infostream<<"Server: Failed to save map to "<<m_savedir
2037 <<", exception: "<<e.what()<<std::endl;
2041 Close database if it was opened
2044 sqlite3_finalize(m_database_read);
2045 if(m_database_write)
2046 sqlite3_finalize(m_database_write);
2048 sqlite3_close(m_database);
2054 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2055 for(; i.atEnd() == false; i++)
2057 MapChunk *chunk = i.getNode()->getValue();
2063 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2065 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2066 if(enable_mapgen_debug_info)
2067 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2068 <<blockpos.Z<<")"<<std::endl;
2070 // Do nothing if not inside limits (+-1 because of neighbors)
2071 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2072 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2078 data->no_op = false;
2079 data->seed = m_seed;
2080 data->blockpos = blockpos;
2081 data->nodedef = m_gamedef->ndef();
2084 Create the whole area of this and the neighboring blocks
2087 //TimeTaker timer("initBlockMake() create area");
2089 for(s16 x=-1; x<=1; x++)
2090 for(s16 z=-1; z<=1; z++)
2092 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2093 // Sector metadata is loaded from disk if not already loaded.
2094 ServerMapSector *sector = createSector(sectorpos);
2097 for(s16 y=-1; y<=1; y++)
2099 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2100 //MapBlock *block = createBlock(p);
2101 // 1) get from memory, 2) load from disk
2102 MapBlock *block = emergeBlock(p, false);
2103 // 3) create a blank one
2106 block = createBlock(p);
2109 Block gets sunlight if this is true.
2111 Refer to the map generator heuristics.
2113 bool ug = mapgen::block_is_underground(data->seed, p);
2114 block->setIsUnderground(ug);
2117 // Lighting will not be valid after make_chunk is called
2118 block->setLightingExpired(true);
2119 // Lighting will be calculated
2120 //block->setLightingExpired(false);
2126 Now we have a big empty area.
2128 Make a ManualMapVoxelManipulator that contains this and the
2132 // The area that contains this block and it's neighbors
2133 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2134 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2136 data->vmanip = new ManualMapVoxelManipulator(this);
2137 //data->vmanip->setMap(this);
2141 //TimeTaker timer("initBlockMake() initialEmerge");
2142 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2145 // Data is ready now.
2148 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2149 core::map<v3s16, MapBlock*> &changed_blocks)
2151 v3s16 blockpos = data->blockpos;
2152 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2153 <<blockpos.Z<<")"<<std::endl;*/
2157 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2161 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2163 /*infostream<<"Resulting vmanip:"<<std::endl;
2164 data->vmanip.print(infostream);*/
2166 // Make sure affected blocks are loaded
2167 for(s16 x=-1; x<=1; x++)
2168 for(s16 z=-1; z<=1; z++)
2169 for(s16 y=-1; y<=1; y++)
2171 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2172 // Load from disk if not already in memory
2173 emergeBlock(p, false);
2177 Blit generated stuff to map
2178 NOTE: blitBackAll adds nearly everything to changed_blocks
2182 //TimeTaker timer("finishBlockMake() blitBackAll");
2183 data->vmanip->blitBackAll(&changed_blocks);
2186 if(enable_mapgen_debug_info)
2187 infostream<<"finishBlockMake: changed_blocks.size()="
2188 <<changed_blocks.size()<<std::endl;
2191 Copy transforming liquid information
2193 while(data->transforming_liquid.size() > 0)
2195 v3s16 p = data->transforming_liquid.pop_front();
2196 m_transforming_liquid.push_back(p);
2202 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2206 Set is_underground flag for lighting with sunlight.
2208 Refer to map generator heuristics.
2210 NOTE: This is done in initChunkMake
2212 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2216 Add sunlight to central block.
2217 This makes in-dark-spawning monsters to not flood the whole thing.
2218 Do not spread the light, though.
2220 /*core::map<v3s16, bool> light_sources;
2221 bool black_air_left = false;
2222 block->propagateSunlight(light_sources, true, &black_air_left);*/
2225 NOTE: Lighting and object adding shouldn't really be here, but
2226 lighting is a bit tricky to move properly to makeBlock.
2227 TODO: Do this the right way anyway, that is, move it to makeBlock.
2228 - There needs to be some way for makeBlock to report back if
2229 the lighting update is going further down because of the
2230 new block blocking light
2235 NOTE: This takes ~60ms, TODO: Investigate why
2238 TimeTaker t("finishBlockMake lighting update");
2240 core::map<v3s16, MapBlock*> lighting_update_blocks;
2243 lighting_update_blocks.insert(block->getPos(), block);
2248 v3s16 p = block->getPos()+v3s16(x,1,z);
2249 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2253 // All modified blocks
2254 // NOTE: Should this be done? If this is not done, then the lighting
2255 // of the others will be updated in a different place, one by one, i
2256 // think... or they might not? Well, at least they are left marked as
2257 // "lighting expired"; it seems that is not handled at all anywhere,
2258 // so enabling this will slow it down A LOT because otherwise it
2259 // would not do this at all. This causes the black trees.
2260 for(core::map<v3s16, MapBlock*>::Iterator
2261 i = changed_blocks.getIterator();
2262 i.atEnd() == false; i++)
2264 lighting_update_blocks.insert(i.getNode()->getKey(),
2265 i.getNode()->getValue());
2267 /*// Also force-add all the upmost blocks for proper sunlight
2268 for(s16 x=-1; x<=1; x++)
2269 for(s16 z=-1; z<=1; z++)
2271 v3s16 p = block->getPos()+v3s16(x,1,z);
2272 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2275 updateLighting(lighting_update_blocks, changed_blocks);
2278 Set lighting to non-expired state in all of them.
2279 This is cheating, but it is not fast enough if all of them
2280 would actually be updated.
2282 for(s16 x=-1; x<=1; x++)
2283 for(s16 y=-1; y<=1; y++)
2284 for(s16 z=-1; z<=1; z++)
2286 v3s16 p = block->getPos()+v3s16(x,y,z);
2287 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2290 if(enable_mapgen_debug_info == false)
2291 t.stop(true); // Hide output
2295 Add random objects to block
2297 mapgen::add_random_objects(block);
2300 Go through changed blocks
2302 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2303 i.atEnd() == false; i++)
2305 MapBlock *block = i.getNode()->getValue();
2308 Update day/night difference cache of the MapBlocks
2310 block->updateDayNightDiff();
2312 Set block as modified
2314 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2315 "finishBlockMake updateDayNightDiff");
2319 Set central block as generated
2321 block->setGenerated(true);
2324 Save changed parts of map
2325 NOTE: Will be saved later.
2329 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2330 <<blockpos.Z<<")"<<std::endl;*/
2332 if(enable_mapgen_debug_info)
2335 Analyze resulting blocks
2337 for(s16 x=-1; x<=1; x++)
2338 for(s16 y=-1; y<=1; y++)
2339 for(s16 z=-1; z<=1; z++)
2341 v3s16 p = block->getPos()+v3s16(x,y,z);
2342 MapBlock *block = getBlockNoCreateNoEx(p);
2344 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2345 infostream<<"Generated "<<spos<<": "
2346 <<analyze_block(block)<<std::endl;
2354 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2356 DSTACKF("%s: p2d=(%d,%d)",
2361 Check if it exists already in memory
2363 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2368 Try to load it from disk (with blocks)
2370 //if(loadSectorFull(p2d) == true)
2373 Try to load metadata from disk
2376 if(loadSectorMeta(p2d) == true)
2378 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2381 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2382 throw InvalidPositionException("");
2388 Do not create over-limit
2390 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2391 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2392 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2393 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2394 throw InvalidPositionException("createSector(): pos. over limit");
2397 Generate blank sector
2400 sector = new ServerMapSector(this, p2d, m_gamedef);
2402 // Sector position on map in nodes
2403 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2408 m_sectors.insert(p2d, sector);
2414 This is a quick-hand function for calling makeBlock().
2416 MapBlock * ServerMap::generateBlock(
2418 core::map<v3s16, MapBlock*> &modified_blocks
2421 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2423 /*infostream<<"generateBlock(): "
2424 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2427 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2429 TimeTaker timer("generateBlock");
2431 //MapBlock *block = original_dummy;
2433 v2s16 p2d(p.X, p.Z);
2434 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2437 Do not generate over-limit
2439 if(blockpos_over_limit(p))
2441 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2442 throw InvalidPositionException("generateBlock(): pos. over limit");
2446 Create block make data
2448 mapgen::BlockMakeData data;
2449 initBlockMake(&data, p);
2455 TimeTaker t("mapgen::make_block()");
2456 mapgen::make_block(&data);
2458 if(enable_mapgen_debug_info == false)
2459 t.stop(true); // Hide output
2463 Blit data back on map, update lighting, add mobs and whatever this does
2465 finishBlockMake(&data, modified_blocks);
2470 MapBlock *block = getBlockNoCreateNoEx(p);
2478 bool erroneus_content = false;
2479 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2480 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2481 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2484 MapNode n = block->getNode(p);
2485 if(n.getContent() == CONTENT_IGNORE)
2487 infostream<<"CONTENT_IGNORE at "
2488 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2490 erroneus_content = true;
2494 if(erroneus_content)
2503 Generate a completely empty block
2507 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2508 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2510 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2513 n.setContent(CONTENT_AIR);
2514 block->setNode(v3s16(x0,y0,z0), n);
2520 if(enable_mapgen_debug_info == false)
2521 timer.stop(true); // Hide output
2526 MapBlock * ServerMap::createBlock(v3s16 p)
2528 DSTACKF("%s: p=(%d,%d,%d)",
2529 __FUNCTION_NAME, p.X, p.Y, p.Z);
2532 Do not create over-limit
2534 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2535 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2536 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2537 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2538 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2539 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2540 throw InvalidPositionException("createBlock(): pos. over limit");
2542 v2s16 p2d(p.X, p.Z);
2545 This will create or load a sector if not found in memory.
2546 If block exists on disk, it will be loaded.
2548 NOTE: On old save formats, this will be slow, as it generates
2549 lighting on blocks for them.
2551 ServerMapSector *sector;
2553 sector = (ServerMapSector*)createSector(p2d);
2554 assert(sector->getId() == MAPSECTOR_SERVER);
2556 catch(InvalidPositionException &e)
2558 infostream<<"createBlock: createSector() failed"<<std::endl;
2562 NOTE: This should not be done, or at least the exception
2563 should not be passed on as std::exception, because it
2564 won't be catched at all.
2566 /*catch(std::exception &e)
2568 infostream<<"createBlock: createSector() failed: "
2569 <<e.what()<<std::endl;
2574 Try to get a block from the sector
2577 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2580 if(block->isDummy())
2585 block = sector->createBlankBlock(block_y);
2589 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2591 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2593 p.X, p.Y, p.Z, allow_generate);
2596 MapBlock *block = getBlockNoCreateNoEx(p);
2597 if(block && block->isDummy() == false)
2602 MapBlock *block = loadBlock(p);
2609 core::map<v3s16, MapBlock*> modified_blocks;
2610 MapBlock *block = generateBlock(p, modified_blocks);
2614 event.type = MEET_OTHER;
2617 // Copy modified_blocks to event
2618 for(core::map<v3s16, MapBlock*>::Iterator
2619 i = modified_blocks.getIterator();
2620 i.atEnd()==false; i++)
2622 event.modified_blocks.insert(i.getNode()->getKey(), false);
2626 dispatchEvent(&event);
2635 s16 ServerMap::findGroundLevel(v2s16 p2d)
2639 Uh, just do something random...
2641 // Find existing map from top to down
2644 v3s16 p(p2d.X, max, p2d.Y);
2645 for(; p.Y>min; p.Y--)
2647 MapNode n = getNodeNoEx(p);
2648 if(n.getContent() != CONTENT_IGNORE)
2653 // If this node is not air, go to plan b
2654 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2656 // Search existing walkable and return it
2657 for(; p.Y>min; p.Y--)
2659 MapNode n = getNodeNoEx(p);
2660 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2669 Determine from map generator noise functions
2672 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2675 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2676 //return (s16)level;
2679 void ServerMap::createDatabase() {
2682 e = sqlite3_exec(m_database,
2683 "CREATE TABLE IF NOT EXISTS `blocks` ("
2684 "`pos` INT NOT NULL PRIMARY KEY,"
2687 , NULL, NULL, NULL);
2688 if(e == SQLITE_ABORT)
2689 throw FileNotGoodException("Could not create database structure");
2691 infostream<<"Server: Database structure was created";
2694 void ServerMap::verifyDatabase() {
2699 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2700 bool needs_create = false;
2704 Open the database connection
2707 createDirs(m_savedir);
2709 if(!fs::PathExists(dbp))
2710 needs_create = true;
2712 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2713 if(d != SQLITE_OK) {
2714 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2715 throw FileNotGoodException("Cannot open database file");
2721 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2722 if(d != SQLITE_OK) {
2723 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2724 throw FileNotGoodException("Cannot prepare read statement");
2727 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2728 if(d != SQLITE_OK) {
2729 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2730 throw FileNotGoodException("Cannot prepare write statement");
2733 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2734 if(d != SQLITE_OK) {
2735 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2736 throw FileNotGoodException("Cannot prepare read statement");
2739 infostream<<"Server: Database opened"<<std::endl;
2743 bool ServerMap::loadFromFolders() {
2744 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2749 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2750 return (sqlite3_int64)pos.Z*16777216 +
2751 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2754 void ServerMap::createDirs(std::string path)
2756 if(fs::CreateAllDirs(path) == false)
2758 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2759 <<"\""<<path<<"\""<<std::endl;
2760 throw BaseException("ServerMap failed to create directory");
2764 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2770 snprintf(cc, 9, "%.4x%.4x",
2771 (unsigned int)pos.X&0xffff,
2772 (unsigned int)pos.Y&0xffff);
2774 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2776 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2777 (unsigned int)pos.X&0xfff,
2778 (unsigned int)pos.Y&0xfff);
2780 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2786 v2s16 ServerMap::getSectorPos(std::string dirname)
2790 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2791 assert(spos != std::string::npos);
2792 if(dirname.size() - spos == 8)
2795 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2797 else if(dirname.size() - spos == 3)
2800 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2801 // Sign-extend the 12 bit values up to 16 bits...
2802 if(x&0x800) x|=0xF000;
2803 if(y&0x800) y|=0xF000;
2810 v2s16 pos((s16)x, (s16)y);
2814 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2816 v2s16 p2d = getSectorPos(sectordir);
2818 if(blockfile.size() != 4){
2819 throw InvalidFilenameException("Invalid block filename");
2822 int r = sscanf(blockfile.c_str(), "%4x", &y);
2824 throw InvalidFilenameException("Invalid block filename");
2825 return v3s16(p2d.X, y, p2d.Y);
2828 std::string ServerMap::getBlockFilename(v3s16 p)
2831 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2835 void ServerMap::save(bool only_changed)
2837 DSTACK(__FUNCTION_NAME);
2838 if(m_map_saving_enabled == false)
2840 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2844 if(only_changed == false)
2845 infostream<<"ServerMap: Saving whole map, this can take time."
2848 if(only_changed == false || m_map_metadata_changed)
2853 // Profile modified reasons
2854 Profiler modprofiler;
2856 u32 sector_meta_count = 0;
2857 u32 block_count = 0;
2858 u32 block_count_all = 0; // Number of blocks in memory
2860 // Don't do anything with sqlite unless something is really saved
2861 bool save_started = false;
2863 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2864 for(; i.atEnd() == false; i++)
2866 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2867 assert(sector->getId() == MAPSECTOR_SERVER);
2869 if(sector->differs_from_disk || only_changed == false)
2871 saveSectorMeta(sector);
2872 sector_meta_count++;
2874 core::list<MapBlock*> blocks;
2875 sector->getBlocks(blocks);
2876 core::list<MapBlock*>::Iterator j;
2878 for(j=blocks.begin(); j!=blocks.end(); j++)
2880 MapBlock *block = *j;
2884 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2885 || only_changed == false)
2890 save_started = true;
2893 modprofiler.add(block->getModifiedReason(), 1);
2898 /*infostream<<"ServerMap: Written block ("
2899 <<block->getPos().X<<","
2900 <<block->getPos().Y<<","
2901 <<block->getPos().Z<<")"
2910 Only print if something happened or saved whole map
2912 if(only_changed == false || sector_meta_count != 0
2913 || block_count != 0)
2915 infostream<<"ServerMap: Written: "
2916 <<sector_meta_count<<" sector metadata files, "
2917 <<block_count<<" block files"
2918 <<", "<<block_count_all<<" blocks in memory."
2920 PrintInfo(infostream); // ServerMap/ClientMap:
2921 infostream<<"Blocks modified by: "<<std::endl;
2922 modprofiler.print(infostream);
2926 static s32 unsignedToSigned(s32 i, s32 max_positive)
2928 if(i < max_positive)
2931 return i - 2*max_positive;
2934 // modulo of a negative number does not work consistently in C
2935 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2939 return mod - ((-i) % mod);
2942 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2944 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2946 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2948 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2949 return v3s16(x,y,z);
2952 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2954 if(loadFromFolders()){
2955 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2956 <<"all blocks that are stored in flat files"<<std::endl;
2962 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2964 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2965 v3s16 p = getIntegerAsBlock(block_i);
2966 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2972 void ServerMap::saveMapMeta()
2974 DSTACK(__FUNCTION_NAME);
2976 infostream<<"ServerMap::saveMapMeta(): "
2980 createDirs(m_savedir);
2982 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2983 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2984 if(os.good() == false)
2986 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2987 <<"could not open"<<fullpath<<std::endl;
2988 throw FileNotGoodException("Cannot open chunk metadata");
2992 params.setU64("seed", m_seed);
2994 params.writeLines(os);
2996 os<<"[end_of_params]\n";
2998 m_map_metadata_changed = false;
3001 void ServerMap::loadMapMeta()
3003 DSTACK(__FUNCTION_NAME);
3005 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3008 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3009 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3010 if(is.good() == false)
3012 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3013 <<"could not open"<<fullpath<<std::endl;
3014 throw FileNotGoodException("Cannot open map metadata");
3022 throw SerializationError
3023 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3025 std::getline(is, line);
3026 std::string trimmedline = trim(line);
3027 if(trimmedline == "[end_of_params]")
3029 params.parseConfigLine(line);
3032 m_seed = params.getU64("seed");
3034 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3037 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3039 DSTACK(__FUNCTION_NAME);
3040 // Format used for writing
3041 u8 version = SER_FMT_VER_HIGHEST;
3043 v2s16 pos = sector->getPos();
3044 std::string dir = getSectorDir(pos);
3047 std::string fullpath = dir + DIR_DELIM + "meta";
3048 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3049 if(o.good() == false)
3050 throw FileNotGoodException("Cannot open sector metafile");
3052 sector->serialize(o, version);
3054 sector->differs_from_disk = false;
3057 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3059 DSTACK(__FUNCTION_NAME);
3061 v2s16 p2d = getSectorPos(sectordir);
3063 ServerMapSector *sector = NULL;
3065 std::string fullpath = sectordir + DIR_DELIM + "meta";
3066 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3067 if(is.good() == false)
3069 // If the directory exists anyway, it probably is in some old
3070 // format. Just go ahead and create the sector.
3071 if(fs::PathExists(sectordir))
3073 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3074 <<fullpath<<" doesn't exist but directory does."
3075 <<" Continuing with a sector with no metadata."
3077 sector = new ServerMapSector(this, p2d, m_gamedef);
3078 m_sectors.insert(p2d, sector);
3082 throw FileNotGoodException("Cannot open sector metafile");
3087 sector = ServerMapSector::deSerialize
3088 (is, this, p2d, m_sectors, m_gamedef);
3090 saveSectorMeta(sector);
3093 sector->differs_from_disk = false;
3098 bool ServerMap::loadSectorMeta(v2s16 p2d)
3100 DSTACK(__FUNCTION_NAME);
3102 MapSector *sector = NULL;
3104 // The directory layout we're going to load from.
3105 // 1 - original sectors/xxxxzzzz/
3106 // 2 - new sectors2/xxx/zzz/
3107 // If we load from anything but the latest structure, we will
3108 // immediately save to the new one, and remove the old.
3110 std::string sectordir1 = getSectorDir(p2d, 1);
3111 std::string sectordir;
3112 if(fs::PathExists(sectordir1))
3114 sectordir = sectordir1;
3119 sectordir = getSectorDir(p2d, 2);
3123 sector = loadSectorMeta(sectordir, loadlayout != 2);
3125 catch(InvalidFilenameException &e)
3129 catch(FileNotGoodException &e)
3133 catch(std::exception &e)
3142 bool ServerMap::loadSectorFull(v2s16 p2d)
3144 DSTACK(__FUNCTION_NAME);
3146 MapSector *sector = NULL;
3148 // The directory layout we're going to load from.
3149 // 1 - original sectors/xxxxzzzz/
3150 // 2 - new sectors2/xxx/zzz/
3151 // If we load from anything but the latest structure, we will
3152 // immediately save to the new one, and remove the old.
3154 std::string sectordir1 = getSectorDir(p2d, 1);
3155 std::string sectordir;
3156 if(fs::PathExists(sectordir1))
3158 sectordir = sectordir1;
3163 sectordir = getSectorDir(p2d, 2);
3167 sector = loadSectorMeta(sectordir, loadlayout != 2);
3169 catch(InvalidFilenameException &e)
3173 catch(FileNotGoodException &e)
3177 catch(std::exception &e)
3185 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3187 std::vector<fs::DirListNode>::iterator i2;
3188 for(i2=list2.begin(); i2!=list2.end(); i2++)
3194 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3196 catch(InvalidFilenameException &e)
3198 // This catches unknown crap in directory
3204 infostream<<"Sector converted to new layout - deleting "<<
3205 sectordir1<<std::endl;
3206 fs::RecursiveDelete(sectordir1);
3213 void ServerMap::beginSave() {
3215 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3216 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3219 void ServerMap::endSave() {
3221 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3222 infostream<<"WARNING: endSave() failed, map might not have saved.";
3225 void ServerMap::saveBlock(MapBlock *block)
3227 DSTACK(__FUNCTION_NAME);
3229 Dummy blocks are not written
3231 if(block->isDummy())
3233 /*v3s16 p = block->getPos();
3234 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3235 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3239 // Format used for writing
3240 u8 version = SER_FMT_VER_HIGHEST;
3242 v3s16 p3d = block->getPos();
3246 v2s16 p2d(p3d.X, p3d.Z);
3247 std::string sectordir = getSectorDir(p2d);
3249 createDirs(sectordir);
3251 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3252 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3253 if(o.good() == false)
3254 throw FileNotGoodException("Cannot open block data");
3257 [0] u8 serialization version
3263 std::ostringstream o(std::ios_base::binary);
3265 o.write((char*)&version, 1);
3268 block->serialize(o, version);
3270 // Write extra data stored on disk
3271 block->serializeDiskExtra(o, version);
3273 // Write block to database
3275 std::string tmp = o.str();
3276 const char *bytes = tmp.c_str();
3278 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3279 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3280 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3281 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3282 int written = sqlite3_step(m_database_write);
3283 if(written != SQLITE_DONE)
3284 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3285 <<sqlite3_errmsg(m_database)<<std::endl;
3286 // Make ready for later reuse
3287 sqlite3_reset(m_database_write);
3289 // We just wrote it to the disk so clear modified flag
3290 block->resetModified();
3293 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3295 DSTACK(__FUNCTION_NAME);
3297 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3300 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3301 if(is.good() == false)
3302 throw FileNotGoodException("Cannot open block file");
3304 v3s16 p3d = getBlockPos(sectordir, blockfile);
3305 v2s16 p2d(p3d.X, p3d.Z);
3307 assert(sector->getPos() == p2d);
3309 u8 version = SER_FMT_VER_INVALID;
3310 is.read((char*)&version, 1);
3313 throw SerializationError("ServerMap::loadBlock(): Failed"
3314 " to read MapBlock version");
3316 /*u32 block_size = MapBlock::serializedLength(version);
3317 SharedBuffer<u8> data(block_size);
3318 is.read((char*)*data, block_size);*/
3320 // This will always return a sector because we're the server
3321 //MapSector *sector = emergeSector(p2d);
3323 MapBlock *block = NULL;
3324 bool created_new = false;
3325 block = sector->getBlockNoCreateNoEx(p3d.Y);
3328 block = sector->createBlankBlockNoInsert(p3d.Y);
3333 block->deSerialize(is, version);
3335 // Read extra data stored on disk
3336 block->deSerializeDiskExtra(is, version);
3338 // If it's a new block, insert it to the map
3340 sector->insertBlock(block);
3343 Save blocks loaded in old format in new format
3346 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3350 // Should be in database now, so delete the old file
3351 fs::RecursiveDelete(fullpath);
3354 // We just loaded it from the disk, so it's up-to-date.
3355 block->resetModified();
3358 catch(SerializationError &e)
3360 infostream<<"WARNING: Invalid block data on disk "
3361 <<"fullpath="<<fullpath
3362 <<" (SerializationError). "
3363 <<"what()="<<e.what()
3365 //" Ignoring. A new one will be generated.
3368 // TODO: Backup file; name is in fullpath.
3372 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3374 DSTACK(__FUNCTION_NAME);
3377 std::istringstream is(*blob, std::ios_base::binary);
3379 u8 version = SER_FMT_VER_INVALID;
3380 is.read((char*)&version, 1);
3383 throw SerializationError("ServerMap::loadBlock(): Failed"
3384 " to read MapBlock version");
3386 /*u32 block_size = MapBlock::serializedLength(version);
3387 SharedBuffer<u8> data(block_size);
3388 is.read((char*)*data, block_size);*/
3390 // This will always return a sector because we're the server
3391 //MapSector *sector = emergeSector(p2d);
3393 MapBlock *block = NULL;
3394 bool created_new = false;
3395 block = sector->getBlockNoCreateNoEx(p3d.Y);
3398 block = sector->createBlankBlockNoInsert(p3d.Y);
3403 block->deSerialize(is, version);
3405 // Read extra data stored on disk
3406 block->deSerializeDiskExtra(is, version);
3408 // If it's a new block, insert it to the map
3410 sector->insertBlock(block);
3413 Save blocks loaded in old format in new format
3416 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3417 // Only save if asked to; no need to update version
3421 // We just loaded it from, so it's up-to-date.
3422 block->resetModified();
3425 catch(SerializationError &e)
3427 infostream<<"WARNING: Invalid block data in database "
3428 <<" (SerializationError). "
3429 <<"what()="<<e.what()
3431 //" Ignoring. A new one will be generated.
3434 // TODO: Copy to a backup database.
3438 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3440 DSTACK(__FUNCTION_NAME);
3442 v2s16 p2d(blockpos.X, blockpos.Z);
3444 if(!loadFromFolders()) {
3447 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3448 infostream<<"WARNING: Could not bind block position for load: "
3449 <<sqlite3_errmsg(m_database)<<std::endl;
3450 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3452 Make sure sector is loaded
3454 MapSector *sector = createSector(p2d);
3459 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3460 size_t len = sqlite3_column_bytes(m_database_read, 0);
3462 std::string datastr(data, len);
3464 loadBlock(&datastr, blockpos, sector, false);
3466 sqlite3_step(m_database_read);
3467 // We should never get more than 1 row, so ok to reset
3468 sqlite3_reset(m_database_read);
3470 return getBlockNoCreateNoEx(blockpos);
3472 sqlite3_reset(m_database_read);
3474 // Not found in database, try the files
3477 // The directory layout we're going to load from.
3478 // 1 - original sectors/xxxxzzzz/
3479 // 2 - new sectors2/xxx/zzz/
3480 // If we load from anything but the latest structure, we will
3481 // immediately save to the new one, and remove the old.
3483 std::string sectordir1 = getSectorDir(p2d, 1);
3484 std::string sectordir;
3485 if(fs::PathExists(sectordir1))
3487 sectordir = sectordir1;
3492 sectordir = getSectorDir(p2d, 2);
3496 Make sure sector is loaded
3498 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3502 sector = loadSectorMeta(sectordir, loadlayout != 2);
3504 catch(InvalidFilenameException &e)
3508 catch(FileNotGoodException &e)
3512 catch(std::exception &e)
3519 Make sure file exists
3522 std::string blockfilename = getBlockFilename(blockpos);
3523 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3527 Load block and save it to the database
3529 loadBlock(sectordir, blockfilename, sector, true);
3530 return getBlockNoCreateNoEx(blockpos);
3533 void ServerMap::PrintInfo(std::ostream &out)
3544 ClientMap::ClientMap(
3547 MapDrawControl &control,
3548 scene::ISceneNode* parent,
3549 scene::ISceneManager* mgr,
3552 Map(dout_client, gamedef),
3553 scene::ISceneNode(parent, mgr, id),
3556 m_camera_position(0,0,0),
3557 m_camera_direction(0,0,1),
3560 m_camera_mutex.Init();
3561 assert(m_camera_mutex.IsInitialized());
3563 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3564 BS*1000000,BS*1000000,BS*1000000);
3567 ClientMap::~ClientMap()
3569 /*JMutexAutoLock lock(mesh_mutex);
3578 MapSector * ClientMap::emergeSector(v2s16 p2d)
3580 DSTACK(__FUNCTION_NAME);
3581 // Check that it doesn't exist already
3583 return getSectorNoGenerate(p2d);
3585 catch(InvalidPositionException &e)
3590 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3593 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3594 m_sectors.insert(p2d, sector);
3601 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3603 DSTACK(__FUNCTION_NAME);
3604 ClientMapSector *sector = NULL;
3606 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3608 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3612 sector = (ClientMapSector*)n->getValue();
3613 assert(sector->getId() == MAPSECTOR_CLIENT);
3617 sector = new ClientMapSector(this, p2d);
3619 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3620 m_sectors.insert(p2d, sector);
3624 sector->deSerialize(is);
3628 void ClientMap::OnRegisterSceneNode()
3632 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3633 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3636 ISceneNode::OnRegisterSceneNode();
3639 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3640 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3642 float d0 = (float)BS * p0.getDistanceFrom(p1);
3644 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3646 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3648 for(float s=start_off; s<d0+end_off; s+=step){
3649 v3f pf = p0f + uf * s;
3650 v3s16 p = floatToInt(pf, BS);
3651 MapNode n = map->getNodeNoEx(p);
3652 bool is_transparent = false;
3653 const ContentFeatures &f = nodemgr->get(n);
3654 if(f.solidness == 0)
3655 is_transparent = (f.visual_solidness != 2);
3657 is_transparent = (f.solidness != 2);
3658 if(!is_transparent){
3660 if(count >= needed_count)
3668 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3670 INodeDefManager *nodemgr = m_gamedef->ndef();
3672 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3673 DSTACK(__FUNCTION_NAME);
3675 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3678 if(pass == scene::ESNRP_SOLID)
3679 prefix = "CM: solid: ";
3681 prefix = "CM: transparent: ";
3684 This is called two times per frame, reset on the non-transparent one
3686 if(pass == scene::ESNRP_SOLID)
3688 m_last_drawn_sectors.clear();
3692 Get time for measuring timeout.
3694 Measuring time is very useful for long delays when the
3695 machine is swapping a lot.
3697 int time1 = time(0);
3699 //u32 daynight_ratio = m_client->getDayNightRatio();
3701 m_camera_mutex.Lock();
3702 v3f camera_position = m_camera_position;
3703 v3f camera_direction = m_camera_direction;
3704 f32 camera_fov = m_camera_fov;
3705 m_camera_mutex.Unlock();
3708 Get all blocks and draw all visible ones
3711 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3713 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3715 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3716 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3718 // Take a fair amount as we will be dropping more out later
3719 // Umm... these additions are a bit strange but they are needed.
3721 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3722 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3723 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3725 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3726 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3727 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3729 u32 vertex_count = 0;
3730 u32 meshbuffer_count = 0;
3732 // For limiting number of mesh updates per frame
3733 u32 mesh_update_count = 0;
3735 // Number of blocks in rendering range
3736 u32 blocks_in_range = 0;
3737 // Number of blocks occlusion culled
3738 u32 blocks_occlusion_culled = 0;
3739 // Number of blocks in rendering range but don't have a mesh
3740 u32 blocks_in_range_without_mesh = 0;
3741 // Blocks that had mesh that would have been drawn according to
3742 // rendering range (if max blocks limit didn't kick in)
3743 u32 blocks_would_have_drawn = 0;
3744 // Blocks that were drawn and had a mesh
3745 u32 blocks_drawn = 0;
3746 // Blocks which had a corresponding meshbuffer for this pass
3747 u32 blocks_had_pass_meshbuf = 0;
3748 // Blocks from which stuff was actually drawn
3749 u32 blocks_without_stuff = 0;
3752 Collect a set of blocks for drawing
3755 core::map<v3s16, MapBlock*> drawset;
3758 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3760 for(core::map<v2s16, MapSector*>::Iterator
3761 si = m_sectors.getIterator();
3762 si.atEnd() == false; si++)
3764 MapSector *sector = si.getNode()->getValue();
3765 v2s16 sp = sector->getPos();
3767 if(m_control.range_all == false)
3769 if(sp.X < p_blocks_min.X
3770 || sp.X > p_blocks_max.X
3771 || sp.Y < p_blocks_min.Z
3772 || sp.Y > p_blocks_max.Z)
3776 core::list< MapBlock * > sectorblocks;
3777 sector->getBlocks(sectorblocks);
3780 Loop through blocks in sector
3783 u32 sector_blocks_drawn = 0;
3785 core::list< MapBlock * >::Iterator i;
3786 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3788 MapBlock *block = *i;
3791 Compare block position to camera position, skip
3792 if not seen on display
3795 float range = 100000 * BS;
3796 if(m_control.range_all == false)
3797 range = m_control.wanted_range * BS;
3800 if(isBlockInSight(block->getPos(), camera_position,
3801 camera_direction, camera_fov,
3802 range, &d) == false)
3807 // This is ugly (spherical distance limit?)
3808 /*if(m_control.range_all == false &&
3809 d - 0.5*BS*MAP_BLOCKSIZE > range)
3816 Update expired mesh (used for day/night change)
3818 It doesn't work exactly like it should now with the
3819 tasked mesh update but whatever.
3822 bool mesh_expired = false;
3825 JMutexAutoLock lock(block->mesh_mutex);
3827 mesh_expired = block->getMeshExpired();
3829 // Mesh has not been expired and there is no mesh:
3830 // block has no content
3831 if(block->mesh == NULL && mesh_expired == false){
3832 blocks_in_range_without_mesh++;
3837 f32 faraway = BS*50;
3838 //f32 faraway = m_control.wanted_range * BS;
3841 This has to be done with the mesh_mutex unlocked
3843 // Pretty random but this should work somewhat nicely
3844 if(mesh_expired && (
3845 (mesh_update_count < 3
3846 && (d < faraway || mesh_update_count < 2)
3849 (m_control.range_all && mesh_update_count < 20)
3852 /*if(mesh_expired && mesh_update_count < 6
3853 && (d < faraway || mesh_update_count < 3))*/
3855 mesh_update_count++;
3857 // Mesh has been expired: generate new mesh
3858 //block->updateMesh(daynight_ratio);
3859 m_client->addUpdateMeshTask(block->getPos());
3861 mesh_expired = false;
3869 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3870 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3872 float stepfac = 1.1;
3873 float startoff = BS*1;
3874 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3875 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3876 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3877 u32 needed_count = 1;
3879 isOccluded(this, spn, cpn + v3s16(0,0,0),
3880 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3881 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3882 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3883 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3884 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3885 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3886 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3887 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3888 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3889 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3890 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3891 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3892 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3893 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3894 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3895 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3896 step, stepfac, startoff, endoff, needed_count, nodemgr)
3899 blocks_occlusion_culled++;
3903 // This block is in range. Reset usage timer.
3904 block->resetUsageTimer();
3907 Ignore if mesh doesn't exist
3910 JMutexAutoLock lock(block->mesh_mutex);
3912 scene::SMesh *mesh = block->mesh;
3915 blocks_in_range_without_mesh++;
3920 // Limit block count in case of a sudden increase
3921 blocks_would_have_drawn++;
3922 if(blocks_drawn >= m_control.wanted_max_blocks
3923 && m_control.range_all == false
3924 && d > m_control.wanted_min_range * BS)
3928 drawset[block->getPos()] = block;
3930 sector_blocks_drawn++;
3933 } // foreach sectorblocks
3935 if(sector_blocks_drawn != 0)
3936 m_last_drawn_sectors[sp] = true;
3941 Draw the selected MapBlocks
3945 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3947 int timecheck_counter = 0;
3948 for(core::map<v3s16, MapBlock*>::Iterator
3949 i = drawset.getIterator();
3950 i.atEnd() == false; i++)
3953 timecheck_counter++;
3954 if(timecheck_counter > 50)
3956 timecheck_counter = 0;
3957 int time2 = time(0);
3958 if(time2 > time1 + 4)
3960 infostream<<"ClientMap::renderMap(): "
3961 "Rendering takes ages, returning."
3968 MapBlock *block = i.getNode()->getValue();
3971 Draw the faces of the block
3974 JMutexAutoLock lock(block->mesh_mutex);
3976 scene::SMesh *mesh = block->mesh;
3979 u32 c = mesh->getMeshBufferCount();
3980 bool stuff_actually_drawn = false;
3981 for(u32 i=0; i<c; i++)
3983 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3984 const video::SMaterial& material = buf->getMaterial();
3985 video::IMaterialRenderer* rnd =
3986 driver->getMaterialRenderer(material.MaterialType);
3987 bool transparent = (rnd && rnd->isTransparent());
3988 // Render transparent on transparent pass and likewise.
3989 if(transparent == is_transparent_pass)
3991 if(buf->getVertexCount() == 0)
3992 errorstream<<"Block ["<<analyze_block(block)
3993 <<"] contains an empty meshbuf"<<std::endl;
3995 This *shouldn't* hurt too much because Irrlicht
3996 doesn't change opengl textures if the old
3997 material has the same texture.
3999 driver->setMaterial(buf->getMaterial());
4000 driver->drawMeshBuffer(buf);
4001 vertex_count += buf->getVertexCount();
4003 stuff_actually_drawn = true;
4006 if(stuff_actually_drawn)
4007 blocks_had_pass_meshbuf++;
4009 blocks_without_stuff++;
4014 // Log only on solid pass because values are the same
4015 if(pass == scene::ESNRP_SOLID){
4016 g_profiler->avg("CM: blocks in range", blocks_in_range);
4017 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
4018 if(blocks_in_range != 0)
4019 g_profiler->avg("CM: blocks in range without mesh (frac)",
4020 (float)blocks_in_range_without_mesh/blocks_in_range);
4021 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4024 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4025 if(blocks_had_pass_meshbuf != 0)
4026 g_profiler->avg(prefix+"meshbuffers per block",
4027 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4028 if(blocks_drawn != 0)
4029 g_profiler->avg(prefix+"empty blocks (frac)",
4030 (float)blocks_without_stuff / blocks_drawn);
4032 m_control.blocks_drawn = blocks_drawn;
4033 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4035 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4036 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4039 void ClientMap::renderPostFx()
4041 INodeDefManager *nodemgr = m_gamedef->ndef();
4043 // Sadly ISceneManager has no "post effects" render pass, in that case we
4044 // could just register for that and handle it in renderMap().
4046 m_camera_mutex.Lock();
4047 v3f camera_position = m_camera_position;
4048 m_camera_mutex.Unlock();
4050 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4052 // - If the player is in a solid node, make everything black.
4053 // - If the player is in liquid, draw a semi-transparent overlay.
4054 const ContentFeatures& features = nodemgr->get(n);
4055 video::SColor post_effect_color = features.post_effect_color;
4056 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4058 post_effect_color = video::SColor(255, 0, 0, 0);
4060 if (post_effect_color.getAlpha() != 0)
4062 // Draw a full-screen rectangle
4063 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4064 v2u32 ss = driver->getScreenSize();
4065 core::rect<s32> rect(0,0, ss.X, ss.Y);
4066 driver->draw2DRectangle(post_effect_color, rect);
4070 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4071 core::map<v3s16, MapBlock*> *affected_blocks)
4073 bool changed = false;
4075 Add it to all blocks touching it
4078 v3s16(0,0,0), // this
4079 v3s16(0,0,1), // back
4080 v3s16(0,1,0), // top
4081 v3s16(1,0,0), // right
4082 v3s16(0,0,-1), // front
4083 v3s16(0,-1,0), // bottom
4084 v3s16(-1,0,0), // left
4086 for(u16 i=0; i<7; i++)
4088 v3s16 p2 = p + dirs[i];
4089 // Block position of neighbor (or requested) node
4090 v3s16 blockpos = getNodeBlockPos(p2);
4091 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4092 if(blockref == NULL)
4094 // Relative position of requested node
4095 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4096 if(blockref->setTempMod(relpos, mod))
4101 if(changed && affected_blocks!=NULL)
4103 for(u16 i=0; i<7; i++)
4105 v3s16 p2 = p + dirs[i];
4106 // Block position of neighbor (or requested) node
4107 v3s16 blockpos = getNodeBlockPos(p2);
4108 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4109 if(blockref == NULL)
4111 affected_blocks->insert(blockpos, blockref);
4117 bool ClientMap::clearTempMod(v3s16 p,
4118 core::map<v3s16, MapBlock*> *affected_blocks)
4120 bool changed = false;
4122 v3s16(0,0,0), // this
4123 v3s16(0,0,1), // back
4124 v3s16(0,1,0), // top
4125 v3s16(1,0,0), // right
4126 v3s16(0,0,-1), // front
4127 v3s16(0,-1,0), // bottom
4128 v3s16(-1,0,0), // left
4130 for(u16 i=0; i<7; i++)
4132 v3s16 p2 = p + dirs[i];
4133 // Block position of neighbor (or requested) node
4134 v3s16 blockpos = getNodeBlockPos(p2);
4135 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4136 if(blockref == NULL)
4138 // Relative position of requested node
4139 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4140 if(blockref->clearTempMod(relpos))
4145 if(changed && affected_blocks!=NULL)
4147 for(u16 i=0; i<7; i++)
4149 v3s16 p2 = p + dirs[i];
4150 // Block position of neighbor (or requested) node
4151 v3s16 blockpos = getNodeBlockPos(p2);
4152 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4153 if(blockref == NULL)
4155 affected_blocks->insert(blockpos, blockref);
4161 void ClientMap::expireMeshes(bool only_daynight_diffed)
4163 TimeTaker timer("expireMeshes()");
4165 core::map<v2s16, MapSector*>::Iterator si;
4166 si = m_sectors.getIterator();
4167 for(; si.atEnd() == false; si++)
4169 MapSector *sector = si.getNode()->getValue();
4171 core::list< MapBlock * > sectorblocks;
4172 sector->getBlocks(sectorblocks);
4174 core::list< MapBlock * >::Iterator i;
4175 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4177 MapBlock *block = *i;
4179 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4185 JMutexAutoLock lock(block->mesh_mutex);
4186 if(block->mesh != NULL)
4188 /*block->mesh->drop();
4189 block->mesh = NULL;*/
4190 block->setMeshExpired(true);
4197 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4199 assert(mapType() == MAPTYPE_CLIENT);
4202 v3s16 p = blockpos + v3s16(0,0,0);
4203 MapBlock *b = getBlockNoCreate(p);
4204 b->updateMesh(daynight_ratio);
4205 //b->setMeshExpired(true);
4207 catch(InvalidPositionException &e){}
4210 v3s16 p = blockpos + v3s16(-1,0,0);
4211 MapBlock *b = getBlockNoCreate(p);
4212 b->updateMesh(daynight_ratio);
4213 //b->setMeshExpired(true);
4215 catch(InvalidPositionException &e){}
4217 v3s16 p = blockpos + v3s16(0,-1,0);
4218 MapBlock *b = getBlockNoCreate(p);
4219 b->updateMesh(daynight_ratio);
4220 //b->setMeshExpired(true);
4222 catch(InvalidPositionException &e){}
4224 v3s16 p = blockpos + v3s16(0,0,-1);
4225 MapBlock *b = getBlockNoCreate(p);
4226 b->updateMesh(daynight_ratio);
4227 //b->setMeshExpired(true);
4229 catch(InvalidPositionException &e){}
4234 Update mesh of block in which the node is, and if the node is at the
4235 leading edge, update the appropriate leading blocks too.
4237 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4245 v3s16 blockposes[4];
4246 for(u32 i=0; i<4; i++)
4248 v3s16 np = nodepos + dirs[i];
4249 blockposes[i] = getNodeBlockPos(np);
4250 // Don't update mesh of block if it has been done already
4251 bool already_updated = false;
4252 for(u32 j=0; j<i; j++)
4254 if(blockposes[j] == blockposes[i])
4256 already_updated = true;
4263 MapBlock *b = getBlockNoCreate(blockposes[i]);
4264 b->updateMesh(daynight_ratio);
4269 void ClientMap::PrintInfo(std::ostream &out)
4280 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4285 MapVoxelManipulator::~MapVoxelManipulator()
4287 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4291 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4293 TimeTaker timer1("emerge", &emerge_time);
4295 // Units of these are MapBlocks
4296 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4297 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4299 VoxelArea block_area_nodes
4300 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4302 addArea(block_area_nodes);
4304 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4305 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4306 for(s32 x=p_min.X; x<=p_max.X; x++)
4309 core::map<v3s16, bool>::Node *n;
4310 n = m_loaded_blocks.find(p);
4314 bool block_data_inexistent = false;
4317 TimeTaker timer1("emerge load", &emerge_load_time);
4319 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4320 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4322 a.print(infostream);
4323 infostream<<std::endl;*/
4325 MapBlock *block = m_map->getBlockNoCreate(p);
4326 if(block->isDummy())
4327 block_data_inexistent = true;
4329 block->copyTo(*this);
4331 catch(InvalidPositionException &e)
4333 block_data_inexistent = true;
4336 if(block_data_inexistent)
4338 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4339 // Fill with VOXELFLAG_INEXISTENT
4340 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4341 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4343 s32 i = m_area.index(a.MinEdge.X,y,z);
4344 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4348 m_loaded_blocks.insert(p, !block_data_inexistent);
4351 //infostream<<"emerge done"<<std::endl;
4355 SUGG: Add an option to only update eg. water and air nodes.
4356 This will make it interfere less with important stuff if
4359 void MapVoxelManipulator::blitBack
4360 (core::map<v3s16, MapBlock*> & modified_blocks)
4362 if(m_area.getExtent() == v3s16(0,0,0))
4365 //TimeTaker timer1("blitBack");
4367 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4368 <<m_loaded_blocks.size()<<std::endl;*/
4371 Initialize block cache
4373 v3s16 blockpos_last;
4374 MapBlock *block = NULL;
4375 bool block_checked_in_modified = false;
4377 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4378 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4379 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4383 u8 f = m_flags[m_area.index(p)];
4384 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4387 MapNode &n = m_data[m_area.index(p)];
4389 v3s16 blockpos = getNodeBlockPos(p);
4394 if(block == NULL || blockpos != blockpos_last){
4395 block = m_map->getBlockNoCreate(blockpos);
4396 blockpos_last = blockpos;
4397 block_checked_in_modified = false;
4400 // Calculate relative position in block
4401 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4403 // Don't continue if nothing has changed here
4404 if(block->getNode(relpos) == n)
4407 //m_map->setNode(m_area.MinEdge + p, n);
4408 block->setNode(relpos, n);
4411 Make sure block is in modified_blocks
4413 if(block_checked_in_modified == false)
4415 modified_blocks[blockpos] = block;
4416 block_checked_in_modified = true;
4419 catch(InvalidPositionException &e)
4425 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4426 MapVoxelManipulator(map),
4427 m_create_area(false)
4431 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4435 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4437 // Just create the area so that it can be pointed to
4438 VoxelManipulator::emerge(a, caller_id);
4441 void ManualMapVoxelManipulator::initialEmerge(
4442 v3s16 blockpos_min, v3s16 blockpos_max)
4444 TimeTaker timer1("initialEmerge", &emerge_time);
4446 // Units of these are MapBlocks
4447 v3s16 p_min = blockpos_min;
4448 v3s16 p_max = blockpos_max;
4450 VoxelArea block_area_nodes
4451 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4453 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4456 infostream<<"initialEmerge: area: ";
4457 block_area_nodes.print(infostream);
4458 infostream<<" ("<<size_MB<<"MB)";
4459 infostream<<std::endl;
4462 addArea(block_area_nodes);
4464 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4465 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4466 for(s32 x=p_min.X; x<=p_max.X; x++)
4469 core::map<v3s16, bool>::Node *n;
4470 n = m_loaded_blocks.find(p);
4474 bool block_data_inexistent = false;
4477 TimeTaker timer1("emerge load", &emerge_load_time);
4479 MapBlock *block = m_map->getBlockNoCreate(p);
4480 if(block->isDummy())
4481 block_data_inexistent = true;
4483 block->copyTo(*this);
4485 catch(InvalidPositionException &e)
4487 block_data_inexistent = true;
4490 if(block_data_inexistent)
4493 Mark area inexistent
4495 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4496 // Fill with VOXELFLAG_INEXISTENT
4497 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4498 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4500 s32 i = m_area.index(a.MinEdge.X,y,z);
4501 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4505 m_loaded_blocks.insert(p, !block_data_inexistent);
4509 void ManualMapVoxelManipulator::blitBackAll(
4510 core::map<v3s16, MapBlock*> * modified_blocks)
4512 if(m_area.getExtent() == v3s16(0,0,0))
4516 Copy data of all blocks
4518 for(core::map<v3s16, bool>::Iterator
4519 i = m_loaded_blocks.getIterator();
4520 i.atEnd() == false; i++)
4522 v3s16 p = i.getNode()->getKey();
4523 bool existed = i.getNode()->getValue();
4524 if(existed == false)
4526 // The Great Bug was found using this
4527 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4528 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4532 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4535 infostream<<"WARNING: "<<__FUNCTION_NAME
4536 <<": got NULL block "
4537 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4542 block->copyFrom(*this);
4545 modified_blocks->insert(p, block);