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 block->setNodeNoCheck(relpos, n);
216 Goes recursively through the neighbours of the node.
218 Alters only transparent nodes.
220 If the lighting of the neighbour is lower than the lighting of
221 the node was (before changing it to 0 at the step before), the
222 lighting of the neighbour is set to 0 and then the same stuff
223 repeats for the neighbour.
225 The ending nodes of the routine are stored in light_sources.
226 This is useful when a light is removed. In such case, this
227 routine can be called for the light node and then again for
228 light_sources to re-light the area without the removed light.
230 values of from_nodes are lighting values.
232 void Map::unspreadLight(enum LightBank bank,
233 core::map<v3s16, u8> & from_nodes,
234 core::map<v3s16, bool> & light_sources,
235 core::map<v3s16, MapBlock*> & modified_blocks)
237 INodeDefManager *nodemgr = m_gamedef->ndef();
240 v3s16(0,0,1), // back
242 v3s16(1,0,0), // right
243 v3s16(0,0,-1), // front
244 v3s16(0,-1,0), // bottom
245 v3s16(-1,0,0), // left
248 if(from_nodes.size() == 0)
251 u32 blockchangecount = 0;
253 core::map<v3s16, u8> unlighted_nodes;
254 core::map<v3s16, u8>::Iterator j;
255 j = from_nodes.getIterator();
258 Initialize block cache
261 MapBlock *block = NULL;
262 // Cache this a bit, too
263 bool block_checked_in_modified = false;
265 for(; j.atEnd() == false; j++)
267 v3s16 pos = j.getNode()->getKey();
268 v3s16 blockpos = getNodeBlockPos(pos);
270 // Only fetch a new block if the block position has changed
272 if(block == NULL || blockpos != blockpos_last){
273 block = getBlockNoCreate(blockpos);
274 blockpos_last = blockpos;
276 block_checked_in_modified = false;
280 catch(InvalidPositionException &e)
288 // Calculate relative position in block
289 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
291 // Get node straight from the block
292 MapNode n = block->getNode(relpos);
294 u8 oldlight = j.getNode()->getValue();
296 // Loop through 6 neighbors
297 for(u16 i=0; i<6; i++)
299 // Get the position of the neighbor node
300 v3s16 n2pos = pos + dirs[i];
302 // Get the block where the node is located
303 v3s16 blockpos = getNodeBlockPos(n2pos);
307 // Only fetch a new block if the block position has changed
309 if(block == NULL || blockpos != blockpos_last){
310 block = getBlockNoCreate(blockpos);
311 blockpos_last = blockpos;
313 block_checked_in_modified = false;
317 catch(InvalidPositionException &e)
322 // Calculate relative position in block
323 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
324 // Get node straight from the block
325 MapNode n2 = block->getNode(relpos);
327 bool changed = false;
329 //TODO: Optimize output by optimizing light_sources?
332 If the neighbor is dimmer than what was specified
333 as oldlight (the light of the previous node)
335 if(n2.getLight(bank, nodemgr) < oldlight)
338 And the neighbor is transparent and it has some light
340 if(nodemgr->get(n2).light_propagates
341 && n2.getLight(bank, nodemgr) != 0)
344 Set light to 0 and add to queue
347 u8 current_light = n2.getLight(bank, nodemgr);
348 n2.setLight(bank, 0, nodemgr);
349 block->setNode(relpos, n2);
351 unlighted_nodes.insert(n2pos, current_light);
355 Remove from light_sources if it is there
356 NOTE: This doesn't happen nearly at all
358 /*if(light_sources.find(n2pos))
360 infostream<<"Removed from light_sources"<<std::endl;
361 light_sources.remove(n2pos);
366 if(light_sources.find(n2pos) != NULL)
367 light_sources.remove(n2pos);*/
370 light_sources.insert(n2pos, true);
373 // Add to modified_blocks
374 if(changed == true && block_checked_in_modified == false)
376 // If the block is not found in modified_blocks, add.
377 if(modified_blocks.find(blockpos) == NULL)
379 modified_blocks.insert(blockpos, block);
381 block_checked_in_modified = true;
384 catch(InvalidPositionException &e)
391 /*infostream<<"unspreadLight(): Changed block "
392 <<blockchangecount<<" times"
393 <<" for "<<from_nodes.size()<<" nodes"
396 if(unlighted_nodes.size() > 0)
397 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
401 A single-node wrapper of the above
403 void Map::unLightNeighbors(enum LightBank bank,
404 v3s16 pos, u8 lightwas,
405 core::map<v3s16, bool> & light_sources,
406 core::map<v3s16, MapBlock*> & modified_blocks)
408 core::map<v3s16, u8> from_nodes;
409 from_nodes.insert(pos, lightwas);
411 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
415 Lights neighbors of from_nodes, collects all them and then
418 void Map::spreadLight(enum LightBank bank,
419 core::map<v3s16, bool> & from_nodes,
420 core::map<v3s16, MapBlock*> & modified_blocks)
422 INodeDefManager *nodemgr = m_gamedef->ndef();
424 const v3s16 dirs[6] = {
425 v3s16(0,0,1), // back
427 v3s16(1,0,0), // right
428 v3s16(0,0,-1), // front
429 v3s16(0,-1,0), // bottom
430 v3s16(-1,0,0), // left
433 if(from_nodes.size() == 0)
436 u32 blockchangecount = 0;
438 core::map<v3s16, bool> lighted_nodes;
439 core::map<v3s16, bool>::Iterator j;
440 j = from_nodes.getIterator();
443 Initialize block cache
446 MapBlock *block = NULL;
447 // Cache this a bit, too
448 bool block_checked_in_modified = false;
450 for(; j.atEnd() == false; j++)
451 //for(; j != from_nodes.end(); j++)
453 v3s16 pos = j.getNode()->getKey();
455 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
456 v3s16 blockpos = getNodeBlockPos(pos);
458 // Only fetch a new block if the block position has changed
460 if(block == NULL || blockpos != blockpos_last){
461 block = getBlockNoCreate(blockpos);
462 blockpos_last = blockpos;
464 block_checked_in_modified = false;
468 catch(InvalidPositionException &e)
476 // Calculate relative position in block
477 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
479 // Get node straight from the block
480 MapNode n = block->getNode(relpos);
482 u8 oldlight = n.getLight(bank, nodemgr);
483 u8 newlight = diminish_light(oldlight);
485 // Loop through 6 neighbors
486 for(u16 i=0; i<6; i++){
487 // Get the position of the neighbor node
488 v3s16 n2pos = pos + dirs[i];
490 // Get the block where the node is located
491 v3s16 blockpos = getNodeBlockPos(n2pos);
495 // Only fetch a new block if the block position has changed
497 if(block == NULL || blockpos != blockpos_last){
498 block = getBlockNoCreate(blockpos);
499 blockpos_last = blockpos;
501 block_checked_in_modified = false;
505 catch(InvalidPositionException &e)
510 // Calculate relative position in block
511 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
512 // Get node straight from the block
513 MapNode n2 = block->getNode(relpos);
515 bool changed = false;
517 If the neighbor is brighter than the current node,
518 add to list (it will light up this node on its turn)
520 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
522 lighted_nodes.insert(n2pos, true);
523 //lighted_nodes.push_back(n2pos);
527 If the neighbor is dimmer than how much light this node
528 would spread on it, add to list
530 if(n2.getLight(bank, nodemgr) < newlight)
532 if(nodemgr->get(n2).light_propagates)
534 n2.setLight(bank, newlight, nodemgr);
535 block->setNode(relpos, n2);
536 lighted_nodes.insert(n2pos, true);
537 //lighted_nodes.push_back(n2pos);
542 // Add to modified_blocks
543 if(changed == true && block_checked_in_modified == false)
545 // If the block is not found in modified_blocks, add.
546 if(modified_blocks.find(blockpos) == NULL)
548 modified_blocks.insert(blockpos, block);
550 block_checked_in_modified = true;
553 catch(InvalidPositionException &e)
560 /*infostream<<"spreadLight(): Changed block "
561 <<blockchangecount<<" times"
562 <<" for "<<from_nodes.size()<<" nodes"
565 if(lighted_nodes.size() > 0)
566 spreadLight(bank, lighted_nodes, modified_blocks);
570 A single-node source variation of the above.
572 void Map::lightNeighbors(enum LightBank bank,
574 core::map<v3s16, MapBlock*> & modified_blocks)
576 core::map<v3s16, bool> from_nodes;
577 from_nodes.insert(pos, true);
578 spreadLight(bank, from_nodes, modified_blocks);
581 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
583 INodeDefManager *nodemgr = m_gamedef->ndef();
586 v3s16(0,0,1), // back
588 v3s16(1,0,0), // right
589 v3s16(0,0,-1), // front
590 v3s16(0,-1,0), // bottom
591 v3s16(-1,0,0), // left
594 u8 brightest_light = 0;
595 v3s16 brightest_pos(0,0,0);
596 bool found_something = false;
598 // Loop through 6 neighbors
599 for(u16 i=0; i<6; i++){
600 // Get the position of the neighbor node
601 v3s16 n2pos = p + dirs[i];
606 catch(InvalidPositionException &e)
610 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
611 brightest_light = n2.getLight(bank, nodemgr);
612 brightest_pos = n2pos;
613 found_something = true;
617 if(found_something == false)
618 throw InvalidPositionException();
620 return brightest_pos;
624 Propagates sunlight down from a node.
625 Starting point gets sunlight.
627 Returns the lowest y value of where the sunlight went.
629 Mud is turned into grass in where the sunlight stops.
631 s16 Map::propagateSunlight(v3s16 start,
632 core::map<v3s16, MapBlock*> & modified_blocks)
634 INodeDefManager *nodemgr = m_gamedef->ndef();
639 v3s16 pos(start.X, y, start.Z);
641 v3s16 blockpos = getNodeBlockPos(pos);
644 block = getBlockNoCreate(blockpos);
646 catch(InvalidPositionException &e)
651 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
652 MapNode n = block->getNode(relpos);
654 if(nodemgr->get(n).sunlight_propagates)
656 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
657 block->setNode(relpos, n);
659 modified_blocks.insert(blockpos, block);
663 // Sunlight goes no further
670 void Map::updateLighting(enum LightBank bank,
671 core::map<v3s16, MapBlock*> & a_blocks,
672 core::map<v3s16, MapBlock*> & modified_blocks)
674 INodeDefManager *nodemgr = m_gamedef->ndef();
676 /*m_dout<<DTIME<<"Map::updateLighting(): "
677 <<a_blocks.size()<<" blocks."<<std::endl;*/
679 //TimeTaker timer("updateLighting");
683 //u32 count_was = modified_blocks.size();
685 core::map<v3s16, MapBlock*> blocks_to_update;
687 core::map<v3s16, bool> light_sources;
689 core::map<v3s16, u8> unlight_from;
691 core::map<v3s16, MapBlock*>::Iterator i;
692 i = a_blocks.getIterator();
693 for(; i.atEnd() == false; i++)
695 MapBlock *block = i.getNode()->getValue();
699 // Don't bother with dummy blocks.
703 v3s16 pos = block->getPos();
704 modified_blocks.insert(pos, block);
706 blocks_to_update.insert(pos, block);
709 Clear all light from block
711 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
712 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
713 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
718 MapNode n = block->getNode(v3s16(x,y,z));
719 u8 oldlight = n.getLight(bank, nodemgr);
720 n.setLight(bank, 0, nodemgr);
721 block->setNode(v3s16(x,y,z), n);
723 // Collect borders for unlighting
724 if(x==0 || x == MAP_BLOCKSIZE-1
725 || y==0 || y == MAP_BLOCKSIZE-1
726 || z==0 || z == MAP_BLOCKSIZE-1)
728 v3s16 p_map = p + v3s16(
731 MAP_BLOCKSIZE*pos.Z);
732 unlight_from.insert(p_map, oldlight);
735 catch(InvalidPositionException &e)
738 This would happen when dealing with a
742 infostream<<"updateLighting(): InvalidPositionException"
747 if(bank == LIGHTBANK_DAY)
749 bool bottom_valid = block->propagateSunlight(light_sources);
751 // If bottom is valid, we're done.
755 else if(bank == LIGHTBANK_NIGHT)
757 // For night lighting, sunlight is not propagated
762 // Invalid lighting bank
766 /*infostream<<"Bottom for sunlight-propagated block ("
767 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
770 // Bottom sunlight is not valid; get the block and loop to it
774 block = getBlockNoCreate(pos);
776 catch(InvalidPositionException &e)
785 Enable this to disable proper lighting for speeding up map
786 generation for testing or whatever
789 //if(g_settings->get(""))
791 core::map<v3s16, MapBlock*>::Iterator i;
792 i = blocks_to_update.getIterator();
793 for(; i.atEnd() == false; i++)
795 MapBlock *block = i.getNode()->getValue();
796 v3s16 p = block->getPos();
797 block->setLightingExpired(false);
805 TimeTaker timer("unspreadLight");
806 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
811 u32 diff = modified_blocks.size() - count_was;
812 count_was = modified_blocks.size();
813 infostream<<"unspreadLight modified "<<diff<<std::endl;
817 TimeTaker timer("spreadLight");
818 spreadLight(bank, light_sources, modified_blocks);
823 u32 diff = modified_blocks.size() - count_was;
824 count_was = modified_blocks.size();
825 infostream<<"spreadLight modified "<<diff<<std::endl;
830 //MapVoxelManipulator vmanip(this);
832 // Make a manual voxel manipulator and load all the blocks
833 // that touch the requested blocks
834 ManualMapVoxelManipulator vmanip(this);
835 core::map<v3s16, MapBlock*>::Iterator i;
836 i = blocks_to_update.getIterator();
837 for(; i.atEnd() == false; i++)
839 MapBlock *block = i.getNode()->getValue();
840 v3s16 p = block->getPos();
842 // Add all surrounding blocks
843 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
846 Add all surrounding blocks that have up-to-date lighting
847 NOTE: This doesn't quite do the job (not everything
848 appropriate is lighted)
850 /*for(s16 z=-1; z<=1; z++)
851 for(s16 y=-1; y<=1; y++)
852 for(s16 x=-1; x<=1; x++)
855 MapBlock *block = getBlockNoCreateNoEx(p);
860 if(block->getLightingExpired())
862 vmanip.initialEmerge(p, p);
865 // Lighting of block will be updated completely
866 block->setLightingExpired(false);
870 //TimeTaker timer("unSpreadLight");
871 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
874 //TimeTaker timer("spreadLight");
875 vmanip.spreadLight(bank, light_sources, nodemgr);
878 //TimeTaker timer("blitBack");
879 vmanip.blitBack(modified_blocks);
881 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
885 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
888 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
889 core::map<v3s16, MapBlock*> & modified_blocks)
891 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
892 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
895 Update information about whether day and night light differ
897 for(core::map<v3s16, MapBlock*>::Iterator
898 i = modified_blocks.getIterator();
899 i.atEnd() == false; i++)
901 MapBlock *block = i.getNode()->getValue();
902 block->updateDayNightDiff();
908 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
909 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
911 INodeDefManager *nodemgr = m_gamedef->ndef();
914 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
915 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
918 From this node to nodes underneath:
919 If lighting is sunlight (1.0), unlight neighbours and
924 v3s16 toppos = p + v3s16(0,1,0);
925 v3s16 bottompos = p + v3s16(0,-1,0);
927 bool node_under_sunlight = true;
928 core::map<v3s16, bool> light_sources;
931 If there is a node at top and it doesn't have sunlight,
932 there has not been any sunlight going down.
934 Otherwise there probably is.
937 MapNode topnode = getNode(toppos);
939 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
940 node_under_sunlight = false;
942 catch(InvalidPositionException &e)
947 Remove all light that has come out of this node
950 enum LightBank banks[] =
955 for(s32 i=0; i<2; i++)
957 enum LightBank bank = banks[i];
959 u8 lightwas = getNode(p).getLight(bank, nodemgr);
961 // Add the block of the added node to modified_blocks
962 v3s16 blockpos = getNodeBlockPos(p);
963 MapBlock * block = getBlockNoCreate(blockpos);
964 assert(block != NULL);
965 modified_blocks.insert(blockpos, block);
967 assert(isValidPosition(p));
969 // Unlight neighbours of node.
970 // This means setting light of all consequent dimmer nodes
972 // This also collects the nodes at the border which will spread
973 // light again into this.
974 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
976 n.setLight(bank, 0, nodemgr);
980 If node lets sunlight through and is under sunlight, it has
983 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
985 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
989 Set the node on the map
998 NodeMetadata *meta_proto = nodemgr->get(n).initial_metadata;
1001 NodeMetadata *meta = meta_proto->clone(m_gamedef);
1002 meta->setOwner(player_name);
1003 setNodeMetadata(p, meta);
1007 If node is under sunlight and doesn't let sunlight through,
1008 take all sunlighted nodes under it and clear light from them
1009 and from where the light has been spread.
1010 TODO: This could be optimized by mass-unlighting instead
1013 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1017 //m_dout<<DTIME<<"y="<<y<<std::endl;
1018 v3s16 n2pos(p.X, y, p.Z);
1022 n2 = getNode(n2pos);
1024 catch(InvalidPositionException &e)
1029 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1031 unLightNeighbors(LIGHTBANK_DAY,
1032 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1033 light_sources, modified_blocks);
1034 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1042 for(s32 i=0; i<2; i++)
1044 enum LightBank bank = banks[i];
1047 Spread light from all nodes that might be capable of doing so
1049 spreadLight(bank, light_sources, modified_blocks);
1053 Update information about whether day and night light differ
1055 for(core::map<v3s16, MapBlock*>::Iterator
1056 i = modified_blocks.getIterator();
1057 i.atEnd() == false; i++)
1059 MapBlock *block = i.getNode()->getValue();
1060 block->updateDayNightDiff();
1064 Add neighboring liquid nodes and the node itself if it is
1065 liquid (=water node was added) to transform queue.
1068 v3s16(0,0,0), // self
1069 v3s16(0,0,1), // back
1070 v3s16(0,1,0), // top
1071 v3s16(1,0,0), // right
1072 v3s16(0,0,-1), // front
1073 v3s16(0,-1,0), // bottom
1074 v3s16(-1,0,0), // left
1076 for(u16 i=0; i<7; i++)
1081 v3s16 p2 = p + dirs[i];
1083 MapNode n2 = getNode(p2);
1084 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1086 m_transforming_liquid.push_back(p2);
1089 }catch(InvalidPositionException &e)
1097 void Map::removeNodeAndUpdate(v3s16 p,
1098 core::map<v3s16, MapBlock*> &modified_blocks)
1100 INodeDefManager *nodemgr = m_gamedef->ndef();
1102 /*PrintInfo(m_dout);
1103 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1104 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1106 bool node_under_sunlight = true;
1108 v3s16 toppos = p + v3s16(0,1,0);
1110 // Node will be replaced with this
1111 content_t replace_material = CONTENT_AIR;
1114 If there is a node at top and it doesn't have sunlight,
1115 there will be no sunlight going down.
1118 MapNode topnode = getNode(toppos);
1120 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1121 node_under_sunlight = false;
1123 catch(InvalidPositionException &e)
1127 core::map<v3s16, bool> light_sources;
1129 enum LightBank banks[] =
1134 for(s32 i=0; i<2; i++)
1136 enum LightBank bank = banks[i];
1139 Unlight neighbors (in case the node is a light source)
1141 unLightNeighbors(bank, p,
1142 getNode(p).getLight(bank, nodemgr),
1143 light_sources, modified_blocks);
1147 Remove node metadata
1150 removeNodeMetadata(p);
1154 This also clears the lighting.
1158 n.setContent(replace_material);
1161 for(s32 i=0; i<2; i++)
1163 enum LightBank bank = banks[i];
1166 Recalculate lighting
1168 spreadLight(bank, light_sources, modified_blocks);
1171 // Add the block of the removed node to modified_blocks
1172 v3s16 blockpos = getNodeBlockPos(p);
1173 MapBlock * block = getBlockNoCreate(blockpos);
1174 assert(block != NULL);
1175 modified_blocks.insert(blockpos, block);
1178 If the removed node was under sunlight, propagate the
1179 sunlight down from it and then light all neighbors
1180 of the propagated blocks.
1182 if(node_under_sunlight)
1184 s16 ybottom = propagateSunlight(p, modified_blocks);
1185 /*m_dout<<DTIME<<"Node was under sunlight. "
1186 "Propagating sunlight";
1187 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1189 for(; y >= ybottom; y--)
1191 v3s16 p2(p.X, y, p.Z);
1192 /*m_dout<<DTIME<<"lighting neighbors of node ("
1193 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1195 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1200 // Set the lighting of this node to 0
1201 // TODO: Is this needed? Lighting is cleared up there already.
1203 MapNode n = getNode(p);
1204 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1207 catch(InvalidPositionException &e)
1213 for(s32 i=0; i<2; i++)
1215 enum LightBank bank = banks[i];
1217 // Get the brightest neighbour node and propagate light from it
1218 v3s16 n2p = getBrightestNeighbour(bank, p);
1220 MapNode n2 = getNode(n2p);
1221 lightNeighbors(bank, n2p, modified_blocks);
1223 catch(InvalidPositionException &e)
1229 Update information about whether day and night light differ
1231 for(core::map<v3s16, MapBlock*>::Iterator
1232 i = modified_blocks.getIterator();
1233 i.atEnd() == false; i++)
1235 MapBlock *block = i.getNode()->getValue();
1236 block->updateDayNightDiff();
1240 Add neighboring liquid nodes and this node to transform queue.
1241 (it's vital for the node itself to get updated last.)
1244 v3s16(0,0,1), // back
1245 v3s16(0,1,0), // top
1246 v3s16(1,0,0), // right
1247 v3s16(0,0,-1), // front
1248 v3s16(0,-1,0), // bottom
1249 v3s16(-1,0,0), // left
1250 v3s16(0,0,0), // self
1252 for(u16 i=0; i<7; i++)
1257 v3s16 p2 = p + dirs[i];
1259 MapNode n2 = getNode(p2);
1260 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1262 m_transforming_liquid.push_back(p2);
1265 }catch(InvalidPositionException &e)
1271 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1274 event.type = MEET_ADDNODE;
1278 bool succeeded = true;
1280 core::map<v3s16, MapBlock*> modified_blocks;
1281 std::string st = std::string("");
1282 addNodeAndUpdate(p, n, modified_blocks, st);
1284 // Copy modified_blocks to event
1285 for(core::map<v3s16, MapBlock*>::Iterator
1286 i = modified_blocks.getIterator();
1287 i.atEnd()==false; i++)
1289 event.modified_blocks.insert(i.getNode()->getKey(), false);
1292 catch(InvalidPositionException &e){
1296 dispatchEvent(&event);
1301 bool Map::removeNodeWithEvent(v3s16 p)
1304 event.type = MEET_REMOVENODE;
1307 bool succeeded = true;
1309 core::map<v3s16, MapBlock*> modified_blocks;
1310 removeNodeAndUpdate(p, modified_blocks);
1312 // Copy modified_blocks to event
1313 for(core::map<v3s16, MapBlock*>::Iterator
1314 i = modified_blocks.getIterator();
1315 i.atEnd()==false; i++)
1317 event.modified_blocks.insert(i.getNode()->getKey(), false);
1320 catch(InvalidPositionException &e){
1324 dispatchEvent(&event);
1329 bool Map::dayNightDiffed(v3s16 blockpos)
1332 v3s16 p = blockpos + v3s16(0,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1340 v3s16 p = blockpos + v3s16(-1,0,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1347 v3s16 p = blockpos + v3s16(0,-1,0);
1348 MapBlock *b = getBlockNoCreate(p);
1349 if(b->dayNightDiffed())
1352 catch(InvalidPositionException &e){}
1354 v3s16 p = blockpos + v3s16(0,0,-1);
1355 MapBlock *b = getBlockNoCreate(p);
1356 if(b->dayNightDiffed())
1359 catch(InvalidPositionException &e){}
1362 v3s16 p = blockpos + v3s16(1,0,0);
1363 MapBlock *b = getBlockNoCreate(p);
1364 if(b->dayNightDiffed())
1367 catch(InvalidPositionException &e){}
1369 v3s16 p = blockpos + v3s16(0,1,0);
1370 MapBlock *b = getBlockNoCreate(p);
1371 if(b->dayNightDiffed())
1374 catch(InvalidPositionException &e){}
1376 v3s16 p = blockpos + v3s16(0,0,1);
1377 MapBlock *b = getBlockNoCreate(p);
1378 if(b->dayNightDiffed())
1381 catch(InvalidPositionException &e){}
1387 Updates usage timers
1389 void Map::timerUpdate(float dtime, float unload_timeout,
1390 core::list<v3s16> *unloaded_blocks)
1392 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1394 core::list<v2s16> sector_deletion_queue;
1395 u32 deleted_blocks_count = 0;
1396 u32 saved_blocks_count = 0;
1398 core::map<v2s16, MapSector*>::Iterator si;
1401 si = m_sectors.getIterator();
1402 for(; si.atEnd() == false; si++)
1404 MapSector *sector = si.getNode()->getValue();
1406 bool all_blocks_deleted = true;
1408 core::list<MapBlock*> blocks;
1409 sector->getBlocks(blocks);
1411 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1412 i != blocks.end(); i++)
1414 MapBlock *block = (*i);
1416 block->incrementUsageTimer(dtime);
1418 if(block->getUsageTimer() > unload_timeout)
1420 v3s16 p = block->getPos();
1423 if(block->getModified() != MOD_STATE_CLEAN
1424 && save_before_unloading)
1427 saved_blocks_count++;
1430 // Delete from memory
1431 sector->deleteBlock(block);
1434 unloaded_blocks->push_back(p);
1436 deleted_blocks_count++;
1440 all_blocks_deleted = false;
1444 if(all_blocks_deleted)
1446 sector_deletion_queue.push_back(si.getNode()->getKey());
1451 // Finally delete the empty sectors
1452 deleteSectors(sector_deletion_queue);
1454 if(deleted_blocks_count != 0)
1456 PrintInfo(infostream); // ServerMap/ClientMap:
1457 infostream<<"Unloaded "<<deleted_blocks_count
1458 <<" blocks from memory";
1459 if(save_before_unloading)
1460 infostream<<", of which "<<saved_blocks_count<<" were written";
1461 infostream<<"."<<std::endl;
1465 void Map::deleteSectors(core::list<v2s16> &list)
1467 core::list<v2s16>::Iterator j;
1468 for(j=list.begin(); j!=list.end(); j++)
1470 MapSector *sector = m_sectors[*j];
1471 // If sector is in sector cache, remove it from there
1472 if(m_sector_cache == sector)
1473 m_sector_cache = NULL;
1474 // Remove from map and delete
1475 m_sectors.remove(*j);
1481 void Map::unloadUnusedData(float timeout,
1482 core::list<v3s16> *deleted_blocks)
1484 core::list<v2s16> sector_deletion_queue;
1485 u32 deleted_blocks_count = 0;
1486 u32 saved_blocks_count = 0;
1488 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1489 for(; si.atEnd() == false; si++)
1491 MapSector *sector = si.getNode()->getValue();
1493 bool all_blocks_deleted = true;
1495 core::list<MapBlock*> blocks;
1496 sector->getBlocks(blocks);
1497 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1498 i != blocks.end(); i++)
1500 MapBlock *block = (*i);
1502 if(block->getUsageTimer() > timeout)
1505 if(block->getModified() != MOD_STATE_CLEAN)
1508 saved_blocks_count++;
1510 // Delete from memory
1511 sector->deleteBlock(block);
1512 deleted_blocks_count++;
1516 all_blocks_deleted = false;
1520 if(all_blocks_deleted)
1522 sector_deletion_queue.push_back(si.getNode()->getKey());
1526 deleteSectors(sector_deletion_queue);
1528 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1529 <<", of which "<<saved_blocks_count<<" were wr."
1532 //return sector_deletion_queue.getSize();
1533 //return deleted_blocks_count;
1537 void Map::PrintInfo(std::ostream &out)
1542 #define WATER_DROP_BOOST 4
1546 NEIGHBOR_SAME_LEVEL,
1549 struct NodeNeighbor {
1555 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1557 INodeDefManager *nodemgr = m_gamedef->ndef();
1559 DSTACK(__FUNCTION_NAME);
1560 //TimeTaker timer("transformLiquids()");
1563 u32 initial_size = m_transforming_liquid.size();
1565 /*if(initial_size != 0)
1566 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1568 // list of nodes that due to viscosity have not reached their max level height
1569 UniqueQueue<v3s16> must_reflow;
1571 // List of MapBlocks that will require a lighting update (due to lava)
1572 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1574 while(m_transforming_liquid.size() != 0)
1576 // This should be done here so that it is done when continue is used
1577 if(loopcount >= initial_size * 3)
1582 Get a queued transforming liquid node
1584 v3s16 p0 = m_transforming_liquid.pop_front();
1586 MapNode n0 = getNodeNoEx(p0);
1589 Collect information about current node
1591 s8 liquid_level = -1;
1592 u8 liquid_kind = CONTENT_IGNORE;
1593 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1594 switch (liquid_type) {
1596 liquid_level = LIQUID_LEVEL_SOURCE;
1597 liquid_kind = nodemgr->get(n0).liquid_alternative_flowing;
1599 case LIQUID_FLOWING:
1600 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1601 liquid_kind = n0.getContent();
1604 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1605 // continue with the next node.
1606 if (n0.getContent() != CONTENT_AIR)
1608 liquid_kind = CONTENT_AIR;
1613 Collect information about the environment
1615 const v3s16 *dirs = g_6dirs;
1616 NodeNeighbor sources[6]; // surrounding sources
1617 int num_sources = 0;
1618 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1620 NodeNeighbor airs[6]; // surrounding air
1622 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1623 int num_neutrals = 0;
1624 bool flowing_down = false;
1625 for (u16 i = 0; i < 6; i++) {
1626 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1629 nt = NEIGHBOR_UPPER;
1632 nt = NEIGHBOR_LOWER;
1635 v3s16 npos = p0 + dirs[i];
1636 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1637 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1639 if (nb.n.getContent() == CONTENT_AIR) {
1640 airs[num_airs++] = nb;
1641 // if the current node is a water source the neighbor
1642 // should be enqueded for transformation regardless of whether the
1643 // current node changes or not.
1644 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1645 m_transforming_liquid.push_back(npos);
1646 // if the current node happens to be a flowing node, it will start to flow down here.
1647 if (nb.t == NEIGHBOR_LOWER) {
1648 flowing_down = true;
1651 neutrals[num_neutrals++] = nb;
1655 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1656 if (liquid_kind == CONTENT_AIR)
1657 liquid_kind = nodemgr->get(nb.n.getContent()).liquid_alternative_flowing;
1658 if (nodemgr->get(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1659 neutrals[num_neutrals++] = nb;
1661 sources[num_sources++] = nb;
1664 case LIQUID_FLOWING:
1665 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1666 if (liquid_kind == CONTENT_AIR)
1667 liquid_kind = nodemgr->get(nb.n.getContent()).liquid_alternative_flowing;
1668 if (nodemgr->get(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1669 neutrals[num_neutrals++] = nb;
1671 flows[num_flows++] = nb;
1672 if (nb.t == NEIGHBOR_LOWER)
1673 flowing_down = true;
1680 decide on the type (and possibly level) of the current node
1682 content_t new_node_content;
1683 s8 new_node_level = -1;
1684 s8 max_node_level = -1;
1685 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1686 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1687 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1688 // it's perfectly safe to use liquid_kind here to determine the new node content.
1689 new_node_content = nodemgr->get(liquid_kind).liquid_alternative_source;
1690 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1691 // liquid_kind is set properly, see above
1692 new_node_content = liquid_kind;
1693 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1695 // no surrounding sources, so get the maximum level that can flow into this node
1696 for (u16 i = 0; i < num_flows; i++) {
1697 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1698 switch (flows[i].t) {
1699 case NEIGHBOR_UPPER:
1700 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1701 max_node_level = LIQUID_LEVEL_MAX;
1702 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1703 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1704 } else if (nb_liquid_level > max_node_level)
1705 max_node_level = nb_liquid_level;
1707 case NEIGHBOR_LOWER:
1709 case NEIGHBOR_SAME_LEVEL:
1710 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1711 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1712 max_node_level = nb_liquid_level - 1;
1718 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1719 if (viscosity > 1 && max_node_level != liquid_level) {
1720 // amount to gain, limited by viscosity
1721 // must be at least 1 in absolute value
1722 s8 level_inc = max_node_level - liquid_level;
1723 if (level_inc < -viscosity || level_inc > viscosity)
1724 new_node_level = liquid_level + level_inc/viscosity;
1725 else if (level_inc < 0)
1726 new_node_level = liquid_level - 1;
1727 else if (level_inc > 0)
1728 new_node_level = liquid_level + 1;
1729 if (new_node_level != max_node_level)
1730 must_reflow.push_back(p0);
1732 new_node_level = max_node_level;
1734 if (new_node_level >= 0)
1735 new_node_content = liquid_kind;
1737 new_node_content = CONTENT_AIR;
1742 check if anything has changed. if not, just continue with the next node.
1744 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1745 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1746 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1752 update the current node
1754 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1755 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1756 // set level to last 3 bits, flowing down bit to 4th bit
1757 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1759 // set the liquid level and flow bit to 0
1760 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1762 n0.setContent(new_node_content);
1764 v3s16 blockpos = getNodeBlockPos(p0);
1765 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1767 modified_blocks.insert(blockpos, block);
1768 // If node emits light, MapBlock requires lighting update
1769 if(nodemgr->get(n0).light_source != 0)
1770 lighting_modified_blocks[block->getPos()] = block;
1774 enqueue neighbors for update if neccessary
1776 switch (nodemgr->get(n0.getContent()).liquid_type) {
1778 case LIQUID_FLOWING:
1779 // make sure source flows into all neighboring nodes
1780 for (u16 i = 0; i < num_flows; i++)
1781 if (flows[i].t != NEIGHBOR_UPPER)
1782 m_transforming_liquid.push_back(flows[i].p);
1783 for (u16 i = 0; i < num_airs; i++)
1784 if (airs[i].t != NEIGHBOR_UPPER)
1785 m_transforming_liquid.push_back(airs[i].p);
1788 // this flow has turned to air; neighboring flows might need to do the same
1789 for (u16 i = 0; i < num_flows; i++)
1790 m_transforming_liquid.push_back(flows[i].p);
1794 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1795 while (must_reflow.size() > 0)
1796 m_transforming_liquid.push_back(must_reflow.pop_front());
1797 updateLighting(lighting_modified_blocks, modified_blocks);
1800 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1802 v3s16 blockpos = getNodeBlockPos(p);
1803 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1804 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1806 infostream<<"Map::getNodeMetadata(): Need to emerge "
1807 <<PP(blockpos)<<std::endl;
1808 block = emergeBlock(blockpos, false);
1812 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1816 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1820 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1822 v3s16 blockpos = getNodeBlockPos(p);
1823 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1824 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1826 infostream<<"Map::setNodeMetadata(): Need to emerge "
1827 <<PP(blockpos)<<std::endl;
1828 block = emergeBlock(blockpos, false);
1832 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1836 block->m_node_metadata->set(p_rel, meta);
1839 void Map::removeNodeMetadata(v3s16 p)
1841 v3s16 blockpos = getNodeBlockPos(p);
1842 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1843 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1846 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1850 block->m_node_metadata->remove(p_rel);
1853 void Map::nodeMetadataStep(float dtime,
1854 core::map<v3s16, MapBlock*> &changed_blocks)
1858 Currently there is no way to ensure that all the necessary
1859 blocks are loaded when this is run. (They might get unloaded)
1860 NOTE: ^- Actually, that might not be so. In a quick test it
1861 reloaded a block with a furnace when I walked back to it from
1864 core::map<v2s16, MapSector*>::Iterator si;
1865 si = m_sectors.getIterator();
1866 for(; si.atEnd() == false; si++)
1868 MapSector *sector = si.getNode()->getValue();
1869 core::list< MapBlock * > sectorblocks;
1870 sector->getBlocks(sectorblocks);
1871 core::list< MapBlock * >::Iterator i;
1872 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1874 MapBlock *block = *i;
1875 bool changed = block->m_node_metadata->step(dtime);
1877 changed_blocks[block->getPos()] = block;
1886 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1887 Map(dout_server, gamedef),
1889 m_map_metadata_changed(true),
1891 m_database_read(NULL),
1892 m_database_write(NULL)
1894 infostream<<__FUNCTION_NAME<<std::endl;
1896 //m_chunksize = 8; // Takes a few seconds
1898 if (g_settings->get("fixed_map_seed").empty())
1900 m_seed = (((u64)(myrand()%0xffff)<<0)
1901 + ((u64)(myrand()%0xffff)<<16)
1902 + ((u64)(myrand()%0xffff)<<32)
1903 + ((u64)(myrand()%0xffff)<<48));
1907 m_seed = g_settings->getU64("fixed_map_seed");
1911 Experimental and debug stuff
1918 Try to load map; if not found, create a new one.
1921 m_savedir = savedir;
1922 m_map_saving_enabled = false;
1926 // If directory exists, check contents and load if possible
1927 if(fs::PathExists(m_savedir))
1929 // If directory is empty, it is safe to save into it.
1930 if(fs::GetDirListing(m_savedir).size() == 0)
1932 infostream<<"Server: Empty save directory is valid."
1934 m_map_saving_enabled = true;
1939 // Load map metadata (seed, chunksize)
1942 catch(FileNotGoodException &e){
1943 infostream<<"WARNING: Could not load map metadata"
1944 //<<" Disabling chunk-based generator."
1950 // Load chunk metadata
1953 catch(FileNotGoodException &e){
1954 infostream<<"WARNING: Could not load chunk metadata."
1955 <<" Disabling chunk-based generator."
1960 /*infostream<<"Server: Successfully loaded chunk "
1961 "metadata and sector (0,0) from "<<savedir<<
1962 ", assuming valid save directory."
1965 infostream<<"Server: Successfully loaded map "
1966 <<"and chunk metadata from "<<savedir
1967 <<", assuming valid save directory."
1970 m_map_saving_enabled = true;
1971 // Map loaded, not creating new one
1975 // If directory doesn't exist, it is safe to save to it
1977 m_map_saving_enabled = true;
1980 catch(std::exception &e)
1982 infostream<<"WARNING: Server: Failed to load map from "<<savedir
1983 <<", exception: "<<e.what()<<std::endl;
1984 infostream<<"Please remove the map or fix it."<<std::endl;
1985 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1988 infostream<<"Initializing new map."<<std::endl;
1990 // Create zero sector
1991 emergeSector(v2s16(0,0));
1993 // Initially write whole map
1997 ServerMap::~ServerMap()
1999 infostream<<__FUNCTION_NAME<<std::endl;
2003 if(m_map_saving_enabled)
2005 // Save only changed parts
2007 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2011 infostream<<"Server: map not saved"<<std::endl;
2014 catch(std::exception &e)
2016 infostream<<"Server: Failed to save map to "<<m_savedir
2017 <<", exception: "<<e.what()<<std::endl;
2021 Close database if it was opened
2024 sqlite3_finalize(m_database_read);
2025 if(m_database_write)
2026 sqlite3_finalize(m_database_write);
2028 sqlite3_close(m_database);
2034 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2035 for(; i.atEnd() == false; i++)
2037 MapChunk *chunk = i.getNode()->getValue();
2043 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2045 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2046 if(enable_mapgen_debug_info)
2047 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2048 <<blockpos.Z<<")"<<std::endl;
2050 // Do nothing if not inside limits (+-1 because of neighbors)
2051 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2052 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2058 data->no_op = false;
2059 data->seed = m_seed;
2060 data->blockpos = blockpos;
2061 data->nodemgr = m_gamedef->ndef();
2064 Create the whole area of this and the neighboring blocks
2067 //TimeTaker timer("initBlockMake() create area");
2069 for(s16 x=-1; x<=1; x++)
2070 for(s16 z=-1; z<=1; z++)
2072 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2073 // Sector metadata is loaded from disk if not already loaded.
2074 ServerMapSector *sector = createSector(sectorpos);
2077 for(s16 y=-1; y<=1; y++)
2079 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2080 //MapBlock *block = createBlock(p);
2081 // 1) get from memory, 2) load from disk
2082 MapBlock *block = emergeBlock(p, false);
2083 // 3) create a blank one
2086 block = createBlock(p);
2089 Block gets sunlight if this is true.
2091 Refer to the map generator heuristics.
2093 bool ug = mapgen::block_is_underground(data->seed, p);
2094 block->setIsUnderground(ug);
2097 // Lighting will not be valid after make_chunk is called
2098 block->setLightingExpired(true);
2099 // Lighting will be calculated
2100 //block->setLightingExpired(false);
2106 Now we have a big empty area.
2108 Make a ManualMapVoxelManipulator that contains this and the
2112 // The area that contains this block and it's neighbors
2113 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2114 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2116 data->vmanip = new ManualMapVoxelManipulator(this);
2117 //data->vmanip->setMap(this);
2121 //TimeTaker timer("initBlockMake() initialEmerge");
2122 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2125 // Data is ready now.
2128 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2129 core::map<v3s16, MapBlock*> &changed_blocks)
2131 v3s16 blockpos = data->blockpos;
2132 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2133 <<blockpos.Z<<")"<<std::endl;*/
2137 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2141 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2143 /*infostream<<"Resulting vmanip:"<<std::endl;
2144 data->vmanip.print(infostream);*/
2147 Blit generated stuff to map
2148 NOTE: blitBackAll adds nearly everything to changed_blocks
2152 //TimeTaker timer("finishBlockMake() blitBackAll");
2153 data->vmanip->blitBackAll(&changed_blocks);
2156 if(enable_mapgen_debug_info)
2157 infostream<<"finishBlockMake: changed_blocks.size()="
2158 <<changed_blocks.size()<<std::endl;
2161 Copy transforming liquid information
2163 while(data->transforming_liquid.size() > 0)
2165 v3s16 p = data->transforming_liquid.pop_front();
2166 m_transforming_liquid.push_back(p);
2172 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2176 Set is_underground flag for lighting with sunlight.
2178 Refer to map generator heuristics.
2180 NOTE: This is done in initChunkMake
2182 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2186 Add sunlight to central block.
2187 This makes in-dark-spawning monsters to not flood the whole thing.
2188 Do not spread the light, though.
2190 /*core::map<v3s16, bool> light_sources;
2191 bool black_air_left = false;
2192 block->propagateSunlight(light_sources, true, &black_air_left);*/
2195 NOTE: Lighting and object adding shouldn't really be here, but
2196 lighting is a bit tricky to move properly to makeBlock.
2197 TODO: Do this the right way anyway, that is, move it to makeBlock.
2198 - There needs to be some way for makeBlock to report back if
2199 the lighting update is going further down because of the
2200 new block blocking light
2205 NOTE: This takes ~60ms, TODO: Investigate why
2208 TimeTaker t("finishBlockMake lighting update");
2210 core::map<v3s16, MapBlock*> lighting_update_blocks;
2213 lighting_update_blocks.insert(block->getPos(), block);
2218 v3s16 p = block->getPos()+v3s16(x,1,z);
2219 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2223 // All modified blocks
2224 // NOTE: Should this be done? If this is not done, then the lighting
2225 // of the others will be updated in a different place, one by one, i
2226 // think... or they might not? Well, at least they are left marked as
2227 // "lighting expired"; it seems that is not handled at all anywhere,
2228 // so enabling this will slow it down A LOT because otherwise it
2229 // would not do this at all. This causes the black trees.
2230 for(core::map<v3s16, MapBlock*>::Iterator
2231 i = changed_blocks.getIterator();
2232 i.atEnd() == false; i++)
2234 lighting_update_blocks.insert(i.getNode()->getKey(),
2235 i.getNode()->getValue());
2237 /*// Also force-add all the upmost blocks for proper sunlight
2238 for(s16 x=-1; x<=1; x++)
2239 for(s16 z=-1; z<=1; z++)
2241 v3s16 p = block->getPos()+v3s16(x,1,z);
2242 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2245 updateLighting(lighting_update_blocks, changed_blocks);
2248 Set lighting to non-expired state in all of them.
2249 This is cheating, but it is not fast enough if all of them
2250 would actually be updated.
2252 for(s16 x=-1; x<=1; x++)
2253 for(s16 y=-1; y<=1; y++)
2254 for(s16 z=-1; z<=1; z++)
2256 v3s16 p = block->getPos()+v3s16(x,y,z);
2257 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2260 if(enable_mapgen_debug_info == false)
2261 t.stop(true); // Hide output
2265 Add random objects to block
2267 mapgen::add_random_objects(block);
2270 Go through changed blocks
2272 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2273 i.atEnd() == false; i++)
2275 MapBlock *block = i.getNode()->getValue();
2278 Update day/night difference cache of the MapBlocks
2280 block->updateDayNightDiff();
2282 Set block as modified
2284 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2288 Set central block as generated
2290 block->setGenerated(true);
2293 Save changed parts of map
2294 NOTE: Will be saved later.
2298 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2299 <<blockpos.Z<<")"<<std::endl;*/
2301 if(enable_mapgen_debug_info)
2304 Analyze resulting blocks
2306 for(s16 x=-1; x<=1; x++)
2307 for(s16 y=-1; y<=1; y++)
2308 for(s16 z=-1; z<=1; z++)
2310 v3s16 p = block->getPos()+v3s16(x,y,z);
2311 MapBlock *block = getBlockNoCreateNoEx(p);
2313 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2314 infostream<<"Generated "<<spos<<": "
2315 <<analyze_block(block)<<std::endl;
2323 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2325 DSTACKF("%s: p2d=(%d,%d)",
2330 Check if it exists already in memory
2332 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2337 Try to load it from disk (with blocks)
2339 //if(loadSectorFull(p2d) == true)
2342 Try to load metadata from disk
2345 if(loadSectorMeta(p2d) == true)
2347 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2350 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2351 throw InvalidPositionException("");
2357 Do not create over-limit
2359 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2360 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2361 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2362 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2363 throw InvalidPositionException("createSector(): pos. over limit");
2366 Generate blank sector
2369 sector = new ServerMapSector(this, p2d, m_gamedef);
2371 // Sector position on map in nodes
2372 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2377 m_sectors.insert(p2d, sector);
2383 This is a quick-hand function for calling makeBlock().
2385 MapBlock * ServerMap::generateBlock(
2387 core::map<v3s16, MapBlock*> &modified_blocks
2390 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2392 /*infostream<<"generateBlock(): "
2393 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2396 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2398 TimeTaker timer("generateBlock");
2400 //MapBlock *block = original_dummy;
2402 v2s16 p2d(p.X, p.Z);
2403 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2406 Do not generate over-limit
2408 if(blockpos_over_limit(p))
2410 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2411 throw InvalidPositionException("generateBlock(): pos. over limit");
2415 Create block make data
2417 mapgen::BlockMakeData data;
2418 initBlockMake(&data, p);
2424 TimeTaker t("mapgen::make_block()");
2425 mapgen::make_block(&data);
2427 if(enable_mapgen_debug_info == false)
2428 t.stop(true); // Hide output
2432 Blit data back on map, update lighting, add mobs and whatever this does
2434 finishBlockMake(&data, modified_blocks);
2439 MapBlock *block = getBlockNoCreateNoEx(p);
2447 bool erroneus_content = false;
2448 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2449 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2450 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2453 MapNode n = block->getNode(p);
2454 if(n.getContent() == CONTENT_IGNORE)
2456 infostream<<"CONTENT_IGNORE at "
2457 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2459 erroneus_content = true;
2463 if(erroneus_content)
2472 Generate a completely empty block
2476 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2477 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2479 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2482 n.setContent(CONTENT_AIR);
2483 block->setNode(v3s16(x0,y0,z0), n);
2489 if(enable_mapgen_debug_info == false)
2490 timer.stop(true); // Hide output
2495 MapBlock * ServerMap::createBlock(v3s16 p)
2497 DSTACKF("%s: p=(%d,%d,%d)",
2498 __FUNCTION_NAME, p.X, p.Y, p.Z);
2501 Do not create over-limit
2503 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2504 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2505 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2506 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2507 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2508 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2509 throw InvalidPositionException("createBlock(): pos. over limit");
2511 v2s16 p2d(p.X, p.Z);
2514 This will create or load a sector if not found in memory.
2515 If block exists on disk, it will be loaded.
2517 NOTE: On old save formats, this will be slow, as it generates
2518 lighting on blocks for them.
2520 ServerMapSector *sector;
2522 sector = (ServerMapSector*)createSector(p2d);
2523 assert(sector->getId() == MAPSECTOR_SERVER);
2525 catch(InvalidPositionException &e)
2527 infostream<<"createBlock: createSector() failed"<<std::endl;
2531 NOTE: This should not be done, or at least the exception
2532 should not be passed on as std::exception, because it
2533 won't be catched at all.
2535 /*catch(std::exception &e)
2537 infostream<<"createBlock: createSector() failed: "
2538 <<e.what()<<std::endl;
2543 Try to get a block from the sector
2546 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2549 if(block->isDummy())
2554 block = sector->createBlankBlock(block_y);
2558 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2560 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2562 p.X, p.Y, p.Z, allow_generate);
2565 MapBlock *block = getBlockNoCreateNoEx(p);
2566 if(block && block->isDummy() == false)
2571 MapBlock *block = loadBlock(p);
2578 core::map<v3s16, MapBlock*> modified_blocks;
2579 MapBlock *block = generateBlock(p, modified_blocks);
2583 event.type = MEET_OTHER;
2586 // Copy modified_blocks to event
2587 for(core::map<v3s16, MapBlock*>::Iterator
2588 i = modified_blocks.getIterator();
2589 i.atEnd()==false; i++)
2591 event.modified_blocks.insert(i.getNode()->getKey(), false);
2595 dispatchEvent(&event);
2604 s16 ServerMap::findGroundLevel(v2s16 p2d)
2608 Uh, just do something random...
2610 // Find existing map from top to down
2613 v3s16 p(p2d.X, max, p2d.Y);
2614 for(; p.Y>min; p.Y--)
2616 MapNode n = getNodeNoEx(p);
2617 if(n.getContent() != CONTENT_IGNORE)
2622 // If this node is not air, go to plan b
2623 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2625 // Search existing walkable and return it
2626 for(; p.Y>min; p.Y--)
2628 MapNode n = getNodeNoEx(p);
2629 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2638 Determine from map generator noise functions
2641 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2644 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2645 //return (s16)level;
2648 void ServerMap::createDatabase() {
2651 e = sqlite3_exec(m_database,
2652 "CREATE TABLE IF NOT EXISTS `blocks` ("
2653 "`pos` INT NOT NULL PRIMARY KEY,"
2656 , NULL, NULL, NULL);
2657 if(e == SQLITE_ABORT)
2658 throw FileNotGoodException("Could not create database structure");
2660 infostream<<"Server: Database structure was created";
2663 void ServerMap::verifyDatabase() {
2668 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2669 bool needs_create = false;
2673 Open the database connection
2676 createDirs(m_savedir);
2678 if(!fs::PathExists(dbp))
2679 needs_create = true;
2681 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2682 if(d != SQLITE_OK) {
2683 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2684 throw FileNotGoodException("Cannot open database file");
2690 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2691 if(d != SQLITE_OK) {
2692 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2693 throw FileNotGoodException("Cannot prepare read statement");
2696 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2697 if(d != SQLITE_OK) {
2698 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2699 throw FileNotGoodException("Cannot prepare write statement");
2702 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2703 if(d != SQLITE_OK) {
2704 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2705 throw FileNotGoodException("Cannot prepare read statement");
2708 infostream<<"Server: Database opened"<<std::endl;
2712 bool ServerMap::loadFromFolders() {
2713 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2718 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2719 return (sqlite3_int64)pos.Z*16777216 +
2720 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2723 void ServerMap::createDirs(std::string path)
2725 if(fs::CreateAllDirs(path) == false)
2727 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2728 <<"\""<<path<<"\""<<std::endl;
2729 throw BaseException("ServerMap failed to create directory");
2733 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2739 snprintf(cc, 9, "%.4x%.4x",
2740 (unsigned int)pos.X&0xffff,
2741 (unsigned int)pos.Y&0xffff);
2743 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2745 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2746 (unsigned int)pos.X&0xfff,
2747 (unsigned int)pos.Y&0xfff);
2749 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2755 v2s16 ServerMap::getSectorPos(std::string dirname)
2759 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2760 assert(spos != std::string::npos);
2761 if(dirname.size() - spos == 8)
2764 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2766 else if(dirname.size() - spos == 3)
2769 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2770 // Sign-extend the 12 bit values up to 16 bits...
2771 if(x&0x800) x|=0xF000;
2772 if(y&0x800) y|=0xF000;
2779 v2s16 pos((s16)x, (s16)y);
2783 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2785 v2s16 p2d = getSectorPos(sectordir);
2787 if(blockfile.size() != 4){
2788 throw InvalidFilenameException("Invalid block filename");
2791 int r = sscanf(blockfile.c_str(), "%4x", &y);
2793 throw InvalidFilenameException("Invalid block filename");
2794 return v3s16(p2d.X, y, p2d.Y);
2797 std::string ServerMap::getBlockFilename(v3s16 p)
2800 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2804 void ServerMap::save(bool only_changed)
2806 DSTACK(__FUNCTION_NAME);
2807 if(m_map_saving_enabled == false)
2809 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2813 if(only_changed == false)
2814 infostream<<"ServerMap: Saving whole map, this can take time."
2817 if(only_changed == false || m_map_metadata_changed)
2822 u32 sector_meta_count = 0;
2823 u32 block_count = 0;
2824 u32 block_count_all = 0; // Number of blocks in memory
2827 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2828 for(; i.atEnd() == false; i++)
2830 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2831 assert(sector->getId() == MAPSECTOR_SERVER);
2833 if(sector->differs_from_disk || only_changed == false)
2835 saveSectorMeta(sector);
2836 sector_meta_count++;
2838 core::list<MapBlock*> blocks;
2839 sector->getBlocks(blocks);
2840 core::list<MapBlock*>::Iterator j;
2842 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2843 for(j=blocks.begin(); j!=blocks.end(); j++)
2845 MapBlock *block = *j;
2849 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2850 || only_changed == false)
2855 /*infostream<<"ServerMap: Written block ("
2856 <<block->getPos().X<<","
2857 <<block->getPos().Y<<","
2858 <<block->getPos().Z<<")"
2861 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
2867 Only print if something happened or saved whole map
2869 if(only_changed == false || sector_meta_count != 0
2870 || block_count != 0)
2872 infostream<<"ServerMap: Written: "
2873 <<sector_meta_count<<" sector metadata files, "
2874 <<block_count<<" block files"
2875 <<", "<<block_count_all<<" blocks in memory."
2880 static s32 unsignedToSigned(s32 i, s32 max_positive)
2882 if(i < max_positive)
2885 return i - 2*max_positive;
2888 // modulo of a negative number does not work consistently in C
2889 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2893 return mod - ((-i) % mod);
2896 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2898 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2900 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2902 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2903 return v3s16(x,y,z);
2906 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2908 if(loadFromFolders()){
2909 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2910 <<"all blocks that are stored in flat files"<<std::endl;
2916 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2918 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2919 v3s16 p = getIntegerAsBlock(block_i);
2920 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2926 void ServerMap::saveMapMeta()
2928 DSTACK(__FUNCTION_NAME);
2930 infostream<<"ServerMap::saveMapMeta(): "
2934 createDirs(m_savedir);
2936 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2937 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2938 if(os.good() == false)
2940 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2941 <<"could not open"<<fullpath<<std::endl;
2942 throw FileNotGoodException("Cannot open chunk metadata");
2946 params.setU64("seed", m_seed);
2948 params.writeLines(os);
2950 os<<"[end_of_params]\n";
2952 m_map_metadata_changed = false;
2955 void ServerMap::loadMapMeta()
2957 DSTACK(__FUNCTION_NAME);
2959 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2962 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2963 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2964 if(is.good() == false)
2966 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2967 <<"could not open"<<fullpath<<std::endl;
2968 throw FileNotGoodException("Cannot open map metadata");
2976 throw SerializationError
2977 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2979 std::getline(is, line);
2980 std::string trimmedline = trim(line);
2981 if(trimmedline == "[end_of_params]")
2983 params.parseConfigLine(line);
2986 m_seed = params.getU64("seed");
2988 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
2991 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2993 DSTACK(__FUNCTION_NAME);
2994 // Format used for writing
2995 u8 version = SER_FMT_VER_HIGHEST;
2997 v2s16 pos = sector->getPos();
2998 std::string dir = getSectorDir(pos);
3001 std::string fullpath = dir + DIR_DELIM + "meta";
3002 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3003 if(o.good() == false)
3004 throw FileNotGoodException("Cannot open sector metafile");
3006 sector->serialize(o, version);
3008 sector->differs_from_disk = false;
3011 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3013 DSTACK(__FUNCTION_NAME);
3015 v2s16 p2d = getSectorPos(sectordir);
3017 ServerMapSector *sector = NULL;
3019 std::string fullpath = sectordir + DIR_DELIM + "meta";
3020 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3021 if(is.good() == false)
3023 // If the directory exists anyway, it probably is in some old
3024 // format. Just go ahead and create the sector.
3025 if(fs::PathExists(sectordir))
3027 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3028 <<fullpath<<" doesn't exist but directory does."
3029 <<" Continuing with a sector with no metadata."
3031 sector = new ServerMapSector(this, p2d, m_gamedef);
3032 m_sectors.insert(p2d, sector);
3036 throw FileNotGoodException("Cannot open sector metafile");
3041 sector = ServerMapSector::deSerialize
3042 (is, this, p2d, m_sectors, m_gamedef);
3044 saveSectorMeta(sector);
3047 sector->differs_from_disk = false;
3052 bool ServerMap::loadSectorMeta(v2s16 p2d)
3054 DSTACK(__FUNCTION_NAME);
3056 MapSector *sector = NULL;
3058 // The directory layout we're going to load from.
3059 // 1 - original sectors/xxxxzzzz/
3060 // 2 - new sectors2/xxx/zzz/
3061 // If we load from anything but the latest structure, we will
3062 // immediately save to the new one, and remove the old.
3064 std::string sectordir1 = getSectorDir(p2d, 1);
3065 std::string sectordir;
3066 if(fs::PathExists(sectordir1))
3068 sectordir = sectordir1;
3073 sectordir = getSectorDir(p2d, 2);
3077 sector = loadSectorMeta(sectordir, loadlayout != 2);
3079 catch(InvalidFilenameException &e)
3083 catch(FileNotGoodException &e)
3087 catch(std::exception &e)
3096 bool ServerMap::loadSectorFull(v2s16 p2d)
3098 DSTACK(__FUNCTION_NAME);
3100 MapSector *sector = NULL;
3102 // The directory layout we're going to load from.
3103 // 1 - original sectors/xxxxzzzz/
3104 // 2 - new sectors2/xxx/zzz/
3105 // If we load from anything but the latest structure, we will
3106 // immediately save to the new one, and remove the old.
3108 std::string sectordir1 = getSectorDir(p2d, 1);
3109 std::string sectordir;
3110 if(fs::PathExists(sectordir1))
3112 sectordir = sectordir1;
3117 sectordir = getSectorDir(p2d, 2);
3121 sector = loadSectorMeta(sectordir, loadlayout != 2);
3123 catch(InvalidFilenameException &e)
3127 catch(FileNotGoodException &e)
3131 catch(std::exception &e)
3139 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3141 std::vector<fs::DirListNode>::iterator i2;
3142 for(i2=list2.begin(); i2!=list2.end(); i2++)
3148 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3150 catch(InvalidFilenameException &e)
3152 // This catches unknown crap in directory
3158 infostream<<"Sector converted to new layout - deleting "<<
3159 sectordir1<<std::endl;
3160 fs::RecursiveDelete(sectordir1);
3167 void ServerMap::beginSave() {
3169 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3170 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3173 void ServerMap::endSave() {
3175 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3176 infostream<<"WARNING: endSave() failed, map might not have saved.";
3179 void ServerMap::saveBlock(MapBlock *block)
3181 DSTACK(__FUNCTION_NAME);
3183 Dummy blocks are not written
3185 if(block->isDummy())
3187 /*v3s16 p = block->getPos();
3188 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3189 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3193 // Format used for writing
3194 u8 version = SER_FMT_VER_HIGHEST;
3196 v3s16 p3d = block->getPos();
3200 v2s16 p2d(p3d.X, p3d.Z);
3201 std::string sectordir = getSectorDir(p2d);
3203 createDirs(sectordir);
3205 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3206 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3207 if(o.good() == false)
3208 throw FileNotGoodException("Cannot open block data");
3211 [0] u8 serialization version
3217 std::ostringstream o(std::ios_base::binary);
3219 o.write((char*)&version, 1);
3222 block->serialize(o, version);
3224 // Write extra data stored on disk
3225 block->serializeDiskExtra(o, version);
3227 // Write block to database
3229 std::string tmp = o.str();
3230 const char *bytes = tmp.c_str();
3232 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3233 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3234 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3235 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3236 int written = sqlite3_step(m_database_write);
3237 if(written != SQLITE_DONE)
3238 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3239 <<sqlite3_errmsg(m_database)<<std::endl;
3240 // Make ready for later reuse
3241 sqlite3_reset(m_database_write);
3243 // We just wrote it to the disk so clear modified flag
3244 block->resetModified();
3247 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3249 DSTACK(__FUNCTION_NAME);
3251 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3254 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3255 if(is.good() == false)
3256 throw FileNotGoodException("Cannot open block file");
3258 v3s16 p3d = getBlockPos(sectordir, blockfile);
3259 v2s16 p2d(p3d.X, p3d.Z);
3261 assert(sector->getPos() == p2d);
3263 u8 version = SER_FMT_VER_INVALID;
3264 is.read((char*)&version, 1);
3267 throw SerializationError("ServerMap::loadBlock(): Failed"
3268 " to read MapBlock version");
3270 /*u32 block_size = MapBlock::serializedLength(version);
3271 SharedBuffer<u8> data(block_size);
3272 is.read((char*)*data, block_size);*/
3274 // This will always return a sector because we're the server
3275 //MapSector *sector = emergeSector(p2d);
3277 MapBlock *block = NULL;
3278 bool created_new = false;
3279 block = sector->getBlockNoCreateNoEx(p3d.Y);
3282 block = sector->createBlankBlockNoInsert(p3d.Y);
3287 block->deSerialize(is, version);
3289 // Read extra data stored on disk
3290 block->deSerializeDiskExtra(is, version);
3292 // If it's a new block, insert it to the map
3294 sector->insertBlock(block);
3297 Save blocks loaded in old format in new format
3300 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3304 // Should be in database now, so delete the old file
3305 fs::RecursiveDelete(fullpath);
3308 // We just loaded it from the disk, so it's up-to-date.
3309 block->resetModified();
3312 catch(SerializationError &e)
3314 infostream<<"WARNING: Invalid block data on disk "
3315 <<"fullpath="<<fullpath
3316 <<" (SerializationError). "
3317 <<"what()="<<e.what()
3319 //" Ignoring. A new one will be generated.
3322 // TODO: Backup file; name is in fullpath.
3326 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3328 DSTACK(__FUNCTION_NAME);
3331 std::istringstream is(*blob, std::ios_base::binary);
3333 u8 version = SER_FMT_VER_INVALID;
3334 is.read((char*)&version, 1);
3337 throw SerializationError("ServerMap::loadBlock(): Failed"
3338 " to read MapBlock version");
3340 /*u32 block_size = MapBlock::serializedLength(version);
3341 SharedBuffer<u8> data(block_size);
3342 is.read((char*)*data, block_size);*/
3344 // This will always return a sector because we're the server
3345 //MapSector *sector = emergeSector(p2d);
3347 MapBlock *block = NULL;
3348 bool created_new = false;
3349 block = sector->getBlockNoCreateNoEx(p3d.Y);
3352 block = sector->createBlankBlockNoInsert(p3d.Y);
3357 block->deSerialize(is, version);
3359 // Read extra data stored on disk
3360 block->deSerializeDiskExtra(is, version);
3362 // If it's a new block, insert it to the map
3364 sector->insertBlock(block);
3367 Save blocks loaded in old format in new format
3370 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3371 // Only save if asked to; no need to update version
3375 // We just loaded it from, so it's up-to-date.
3376 block->resetModified();
3379 catch(SerializationError &e)
3381 infostream<<"WARNING: Invalid block data in database "
3382 <<" (SerializationError). "
3383 <<"what()="<<e.what()
3385 //" Ignoring. A new one will be generated.
3388 // TODO: Copy to a backup database.
3392 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3394 DSTACK(__FUNCTION_NAME);
3396 v2s16 p2d(blockpos.X, blockpos.Z);
3398 if(!loadFromFolders()) {
3401 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3402 infostream<<"WARNING: Could not bind block position for load: "
3403 <<sqlite3_errmsg(m_database)<<std::endl;
3404 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3406 Make sure sector is loaded
3408 MapSector *sector = createSector(p2d);
3413 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3414 size_t len = sqlite3_column_bytes(m_database_read, 0);
3416 std::string datastr(data, len);
3418 loadBlock(&datastr, blockpos, sector, false);
3420 sqlite3_step(m_database_read);
3421 // We should never get more than 1 row, so ok to reset
3422 sqlite3_reset(m_database_read);
3424 return getBlockNoCreateNoEx(blockpos);
3426 sqlite3_reset(m_database_read);
3428 // Not found in database, try the files
3431 // The directory layout we're going to load from.
3432 // 1 - original sectors/xxxxzzzz/
3433 // 2 - new sectors2/xxx/zzz/
3434 // If we load from anything but the latest structure, we will
3435 // immediately save to the new one, and remove the old.
3437 std::string sectordir1 = getSectorDir(p2d, 1);
3438 std::string sectordir;
3439 if(fs::PathExists(sectordir1))
3441 sectordir = sectordir1;
3446 sectordir = getSectorDir(p2d, 2);
3450 Make sure sector is loaded
3452 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3456 sector = loadSectorMeta(sectordir, loadlayout != 2);
3458 catch(InvalidFilenameException &e)
3462 catch(FileNotGoodException &e)
3466 catch(std::exception &e)
3473 Make sure file exists
3476 std::string blockfilename = getBlockFilename(blockpos);
3477 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3481 Load block and save it to the database
3483 loadBlock(sectordir, blockfilename, sector, true);
3484 return getBlockNoCreateNoEx(blockpos);
3487 void ServerMap::PrintInfo(std::ostream &out)
3498 ClientMap::ClientMap(
3501 MapDrawControl &control,
3502 scene::ISceneNode* parent,
3503 scene::ISceneManager* mgr,
3506 Map(dout_client, gamedef),
3507 scene::ISceneNode(parent, mgr, id),
3510 m_camera_position(0,0,0),
3511 m_camera_direction(0,0,1),
3514 m_camera_mutex.Init();
3515 assert(m_camera_mutex.IsInitialized());
3517 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3518 BS*1000000,BS*1000000,BS*1000000);
3521 ClientMap::~ClientMap()
3523 /*JMutexAutoLock lock(mesh_mutex);
3532 MapSector * ClientMap::emergeSector(v2s16 p2d)
3534 DSTACK(__FUNCTION_NAME);
3535 // Check that it doesn't exist already
3537 return getSectorNoGenerate(p2d);
3539 catch(InvalidPositionException &e)
3544 ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
3547 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3548 m_sectors.insert(p2d, sector);
3555 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3557 DSTACK(__FUNCTION_NAME);
3558 ClientMapSector *sector = NULL;
3560 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3562 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3566 sector = (ClientMapSector*)n->getValue();
3567 assert(sector->getId() == MAPSECTOR_CLIENT);
3571 sector = new ClientMapSector(this, p2d);
3573 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3574 m_sectors.insert(p2d, sector);
3578 sector->deSerialize(is);
3582 void ClientMap::OnRegisterSceneNode()
3586 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3587 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3590 ISceneNode::OnRegisterSceneNode();
3593 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3594 float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
3596 float d0 = (float)BS * p0.getDistanceFrom(p1);
3598 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3600 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3602 for(float s=start_off; s<d0+end_off; s+=step){
3603 v3f pf = p0f + uf * s;
3604 v3s16 p = floatToInt(pf, BS);
3605 MapNode n = map->getNodeNoEx(p);
3606 bool is_transparent = false;
3607 const ContentFeatures &f = nodemgr->get(n);
3608 if(f.solidness == 0)
3609 is_transparent = (f.visual_solidness != 2);
3611 is_transparent = (f.solidness != 2);
3612 if(!is_transparent){
3614 if(count >= needed_count)
3622 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3624 INodeDefManager *nodemgr = m_gamedef->ndef();
3626 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3627 DSTACK(__FUNCTION_NAME);
3629 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3632 if(pass == scene::ESNRP_SOLID)
3633 prefix = "CM: solid: ";
3635 prefix = "CM: transparent: ";
3638 This is called two times per frame, reset on the non-transparent one
3640 if(pass == scene::ESNRP_SOLID)
3642 m_last_drawn_sectors.clear();
3646 Get time for measuring timeout.
3648 Measuring time is very useful for long delays when the
3649 machine is swapping a lot.
3651 int time1 = time(0);
3653 //u32 daynight_ratio = m_client->getDayNightRatio();
3655 m_camera_mutex.Lock();
3656 v3f camera_position = m_camera_position;
3657 v3f camera_direction = m_camera_direction;
3658 f32 camera_fov = m_camera_fov;
3659 m_camera_mutex.Unlock();
3662 Get all blocks and draw all visible ones
3665 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3667 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3669 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3670 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3672 // Take a fair amount as we will be dropping more out later
3673 // Umm... these additions are a bit strange but they are needed.
3675 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3676 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3677 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3679 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3680 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3681 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3683 u32 vertex_count = 0;
3684 u32 meshbuffer_count = 0;
3686 // For limiting number of mesh updates per frame
3687 u32 mesh_update_count = 0;
3689 // Number of blocks in rendering range
3690 u32 blocks_in_range = 0;
3691 // Number of blocks occlusion culled
3692 u32 blocks_occlusion_culled = 0;
3693 // Number of blocks in rendering range but don't have a mesh
3694 u32 blocks_in_range_without_mesh = 0;
3695 // Blocks that had mesh that would have been drawn according to
3696 // rendering range (if max blocks limit didn't kick in)
3697 u32 blocks_would_have_drawn = 0;
3698 // Blocks that were drawn and had a mesh
3699 u32 blocks_drawn = 0;
3700 // Blocks which had a corresponding meshbuffer for this pass
3701 u32 blocks_had_pass_meshbuf = 0;
3702 // Blocks from which stuff was actually drawn
3703 u32 blocks_without_stuff = 0;
3706 Collect a set of blocks for drawing
3709 core::map<v3s16, MapBlock*> drawset;
3712 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3714 for(core::map<v2s16, MapSector*>::Iterator
3715 si = m_sectors.getIterator();
3716 si.atEnd() == false; si++)
3718 MapSector *sector = si.getNode()->getValue();
3719 v2s16 sp = sector->getPos();
3721 if(m_control.range_all == false)
3723 if(sp.X < p_blocks_min.X
3724 || sp.X > p_blocks_max.X
3725 || sp.Y < p_blocks_min.Z
3726 || sp.Y > p_blocks_max.Z)
3730 core::list< MapBlock * > sectorblocks;
3731 sector->getBlocks(sectorblocks);
3734 Loop through blocks in sector
3737 u32 sector_blocks_drawn = 0;
3739 core::list< MapBlock * >::Iterator i;
3740 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3742 MapBlock *block = *i;
3745 Compare block position to camera position, skip
3746 if not seen on display
3749 float range = 100000 * BS;
3750 if(m_control.range_all == false)
3751 range = m_control.wanted_range * BS;
3754 if(isBlockInSight(block->getPos(), camera_position,
3755 camera_direction, camera_fov,
3756 range, &d) == false)
3761 // This is ugly (spherical distance limit?)
3762 /*if(m_control.range_all == false &&
3763 d - 0.5*BS*MAP_BLOCKSIZE > range)
3770 Update expired mesh (used for day/night change)
3772 It doesn't work exactly like it should now with the
3773 tasked mesh update but whatever.
3776 bool mesh_expired = false;
3779 JMutexAutoLock lock(block->mesh_mutex);
3781 mesh_expired = block->getMeshExpired();
3783 // Mesh has not been expired and there is no mesh:
3784 // block has no content
3785 if(block->mesh == NULL && mesh_expired == false){
3786 blocks_in_range_without_mesh++;
3791 f32 faraway = BS*50;
3792 //f32 faraway = m_control.wanted_range * BS;
3795 This has to be done with the mesh_mutex unlocked
3797 // Pretty random but this should work somewhat nicely
3798 if(mesh_expired && (
3799 (mesh_update_count < 3
3800 && (d < faraway || mesh_update_count < 2)
3803 (m_control.range_all && mesh_update_count < 20)
3806 /*if(mesh_expired && mesh_update_count < 6
3807 && (d < faraway || mesh_update_count < 3))*/
3809 mesh_update_count++;
3811 // Mesh has been expired: generate new mesh
3812 //block->updateMesh(daynight_ratio);
3813 m_client->addUpdateMeshTask(block->getPos());
3815 mesh_expired = false;
3823 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3824 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3826 float stepfac = 1.1;
3827 float startoff = BS*1;
3828 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3829 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3830 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3831 u32 needed_count = 1;
3833 isOccluded(this, spn, cpn + v3s16(0,0,0),
3834 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3835 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3836 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3837 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3838 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3839 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3840 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3841 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3842 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3843 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3844 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3845 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3846 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3847 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3848 step, stepfac, startoff, endoff, needed_count, nodemgr) &&
3849 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3850 step, stepfac, startoff, endoff, needed_count, nodemgr)
3853 blocks_occlusion_culled++;
3857 // This block is in range. Reset usage timer.
3858 block->resetUsageTimer();
3861 Ignore if mesh doesn't exist
3864 JMutexAutoLock lock(block->mesh_mutex);
3866 scene::SMesh *mesh = block->mesh;
3869 blocks_in_range_without_mesh++;
3874 // Limit block count in case of a sudden increase
3875 blocks_would_have_drawn++;
3876 if(blocks_drawn >= m_control.wanted_max_blocks
3877 && m_control.range_all == false
3878 && d > m_control.wanted_min_range * BS)
3882 drawset[block->getPos()] = block;
3884 sector_blocks_drawn++;
3887 } // foreach sectorblocks
3889 if(sector_blocks_drawn != 0)
3890 m_last_drawn_sectors[sp] = true;
3895 Draw the selected MapBlocks
3899 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3901 int timecheck_counter = 0;
3902 for(core::map<v3s16, MapBlock*>::Iterator
3903 i = drawset.getIterator();
3904 i.atEnd() == false; i++)
3907 timecheck_counter++;
3908 if(timecheck_counter > 50)
3910 timecheck_counter = 0;
3911 int time2 = time(0);
3912 if(time2 > time1 + 4)
3914 infostream<<"ClientMap::renderMap(): "
3915 "Rendering takes ages, returning."
3922 MapBlock *block = i.getNode()->getValue();
3925 Draw the faces of the block
3928 JMutexAutoLock lock(block->mesh_mutex);
3930 scene::SMesh *mesh = block->mesh;
3933 u32 c = mesh->getMeshBufferCount();
3934 bool stuff_actually_drawn = false;
3935 for(u32 i=0; i<c; i++)
3937 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3938 const video::SMaterial& material = buf->getMaterial();
3939 video::IMaterialRenderer* rnd =
3940 driver->getMaterialRenderer(material.MaterialType);
3941 bool transparent = (rnd && rnd->isTransparent());
3942 // Render transparent on transparent pass and likewise.
3943 if(transparent == is_transparent_pass)
3945 if(buf->getVertexCount() == 0)
3946 errorstream<<"Block ["<<analyze_block(block)
3947 <<"] contains an empty meshbuf"<<std::endl;
3949 This *shouldn't* hurt too much because Irrlicht
3950 doesn't change opengl textures if the old
3951 material has the same texture.
3953 driver->setMaterial(buf->getMaterial());
3954 driver->drawMeshBuffer(buf);
3955 vertex_count += buf->getVertexCount();
3957 stuff_actually_drawn = true;
3960 if(stuff_actually_drawn)
3961 blocks_had_pass_meshbuf++;
3963 blocks_without_stuff++;
3968 // Log only on solid pass because values are the same
3969 if(pass == scene::ESNRP_SOLID){
3970 g_profiler->avg("CM: blocks in range", blocks_in_range);
3971 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3972 if(blocks_in_range != 0)
3973 g_profiler->avg("CM: blocks in range without mesh (frac)",
3974 (float)blocks_in_range_without_mesh/blocks_in_range);
3975 g_profiler->avg("CM: blocks drawn", blocks_drawn);
3978 g_profiler->avg(prefix+"vertices drawn", vertex_count);
3979 if(blocks_had_pass_meshbuf != 0)
3980 g_profiler->avg(prefix+"meshbuffers per block",
3981 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
3982 if(blocks_drawn != 0)
3983 g_profiler->avg(prefix+"empty blocks (frac)",
3984 (float)blocks_without_stuff / blocks_drawn);
3986 m_control.blocks_drawn = blocks_drawn;
3987 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3989 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3990 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3993 void ClientMap::renderPostFx()
3995 INodeDefManager *nodemgr = m_gamedef->ndef();
3997 // Sadly ISceneManager has no "post effects" render pass, in that case we
3998 // could just register for that and handle it in renderMap().
4000 m_camera_mutex.Lock();
4001 v3f camera_position = m_camera_position;
4002 m_camera_mutex.Unlock();
4004 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4006 // - If the player is in a solid node, make everything black.
4007 // - If the player is in liquid, draw a semi-transparent overlay.
4008 const ContentFeatures& features = nodemgr->get(n);
4009 video::SColor post_effect_color = features.post_effect_color;
4010 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4012 post_effect_color = video::SColor(255, 0, 0, 0);
4014 if (post_effect_color.getAlpha() != 0)
4016 // Draw a full-screen rectangle
4017 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4018 v2u32 ss = driver->getScreenSize();
4019 core::rect<s32> rect(0,0, ss.X, ss.Y);
4020 driver->draw2DRectangle(post_effect_color, rect);
4024 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4025 core::map<v3s16, MapBlock*> *affected_blocks)
4027 bool changed = false;
4029 Add it to all blocks touching it
4032 v3s16(0,0,0), // this
4033 v3s16(0,0,1), // back
4034 v3s16(0,1,0), // top
4035 v3s16(1,0,0), // right
4036 v3s16(0,0,-1), // front
4037 v3s16(0,-1,0), // bottom
4038 v3s16(-1,0,0), // left
4040 for(u16 i=0; i<7; i++)
4042 v3s16 p2 = p + dirs[i];
4043 // Block position of neighbor (or requested) node
4044 v3s16 blockpos = getNodeBlockPos(p2);
4045 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4046 if(blockref == NULL)
4048 // Relative position of requested node
4049 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4050 if(blockref->setTempMod(relpos, mod))
4055 if(changed && affected_blocks!=NULL)
4057 for(u16 i=0; i<7; i++)
4059 v3s16 p2 = p + dirs[i];
4060 // Block position of neighbor (or requested) node
4061 v3s16 blockpos = getNodeBlockPos(p2);
4062 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4063 if(blockref == NULL)
4065 affected_blocks->insert(blockpos, blockref);
4071 bool ClientMap::clearTempMod(v3s16 p,
4072 core::map<v3s16, MapBlock*> *affected_blocks)
4074 bool changed = false;
4076 v3s16(0,0,0), // this
4077 v3s16(0,0,1), // back
4078 v3s16(0,1,0), // top
4079 v3s16(1,0,0), // right
4080 v3s16(0,0,-1), // front
4081 v3s16(0,-1,0), // bottom
4082 v3s16(-1,0,0), // left
4084 for(u16 i=0; i<7; i++)
4086 v3s16 p2 = p + dirs[i];
4087 // Block position of neighbor (or requested) node
4088 v3s16 blockpos = getNodeBlockPos(p2);
4089 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4090 if(blockref == NULL)
4092 // Relative position of requested node
4093 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4094 if(blockref->clearTempMod(relpos))
4099 if(changed && affected_blocks!=NULL)
4101 for(u16 i=0; i<7; i++)
4103 v3s16 p2 = p + dirs[i];
4104 // Block position of neighbor (or requested) node
4105 v3s16 blockpos = getNodeBlockPos(p2);
4106 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4107 if(blockref == NULL)
4109 affected_blocks->insert(blockpos, blockref);
4115 void ClientMap::expireMeshes(bool only_daynight_diffed)
4117 TimeTaker timer("expireMeshes()");
4119 core::map<v2s16, MapSector*>::Iterator si;
4120 si = m_sectors.getIterator();
4121 for(; si.atEnd() == false; si++)
4123 MapSector *sector = si.getNode()->getValue();
4125 core::list< MapBlock * > sectorblocks;
4126 sector->getBlocks(sectorblocks);
4128 core::list< MapBlock * >::Iterator i;
4129 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4131 MapBlock *block = *i;
4133 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4139 JMutexAutoLock lock(block->mesh_mutex);
4140 if(block->mesh != NULL)
4142 /*block->mesh->drop();
4143 block->mesh = NULL;*/
4144 block->setMeshExpired(true);
4151 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4153 assert(mapType() == MAPTYPE_CLIENT);
4156 v3s16 p = blockpos + v3s16(0,0,0);
4157 MapBlock *b = getBlockNoCreate(p);
4158 b->updateMesh(daynight_ratio);
4159 //b->setMeshExpired(true);
4161 catch(InvalidPositionException &e){}
4164 v3s16 p = blockpos + v3s16(-1,0,0);
4165 MapBlock *b = getBlockNoCreate(p);
4166 b->updateMesh(daynight_ratio);
4167 //b->setMeshExpired(true);
4169 catch(InvalidPositionException &e){}
4171 v3s16 p = blockpos + v3s16(0,-1,0);
4172 MapBlock *b = getBlockNoCreate(p);
4173 b->updateMesh(daynight_ratio);
4174 //b->setMeshExpired(true);
4176 catch(InvalidPositionException &e){}
4178 v3s16 p = blockpos + v3s16(0,0,-1);
4179 MapBlock *b = getBlockNoCreate(p);
4180 b->updateMesh(daynight_ratio);
4181 //b->setMeshExpired(true);
4183 catch(InvalidPositionException &e){}
4188 Update mesh of block in which the node is, and if the node is at the
4189 leading edge, update the appropriate leading blocks too.
4191 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4199 v3s16 blockposes[4];
4200 for(u32 i=0; i<4; i++)
4202 v3s16 np = nodepos + dirs[i];
4203 blockposes[i] = getNodeBlockPos(np);
4204 // Don't update mesh of block if it has been done already
4205 bool already_updated = false;
4206 for(u32 j=0; j<i; j++)
4208 if(blockposes[j] == blockposes[i])
4210 already_updated = true;
4217 MapBlock *b = getBlockNoCreate(blockposes[i]);
4218 b->updateMesh(daynight_ratio);
4223 void ClientMap::PrintInfo(std::ostream &out)
4234 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4239 MapVoxelManipulator::~MapVoxelManipulator()
4241 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4245 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4247 TimeTaker timer1("emerge", &emerge_time);
4249 // Units of these are MapBlocks
4250 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4251 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4253 VoxelArea block_area_nodes
4254 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4256 addArea(block_area_nodes);
4258 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4259 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4260 for(s32 x=p_min.X; x<=p_max.X; x++)
4263 core::map<v3s16, bool>::Node *n;
4264 n = m_loaded_blocks.find(p);
4268 bool block_data_inexistent = false;
4271 TimeTaker timer1("emerge load", &emerge_load_time);
4273 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4274 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4276 a.print(infostream);
4277 infostream<<std::endl;*/
4279 MapBlock *block = m_map->getBlockNoCreate(p);
4280 if(block->isDummy())
4281 block_data_inexistent = true;
4283 block->copyTo(*this);
4285 catch(InvalidPositionException &e)
4287 block_data_inexistent = true;
4290 if(block_data_inexistent)
4292 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4293 // Fill with VOXELFLAG_INEXISTENT
4294 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4295 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4297 s32 i = m_area.index(a.MinEdge.X,y,z);
4298 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4302 m_loaded_blocks.insert(p, !block_data_inexistent);
4305 //infostream<<"emerge done"<<std::endl;
4309 SUGG: Add an option to only update eg. water and air nodes.
4310 This will make it interfere less with important stuff if
4313 void MapVoxelManipulator::blitBack
4314 (core::map<v3s16, MapBlock*> & modified_blocks)
4316 if(m_area.getExtent() == v3s16(0,0,0))
4319 //TimeTaker timer1("blitBack");
4321 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4322 <<m_loaded_blocks.size()<<std::endl;*/
4325 Initialize block cache
4327 v3s16 blockpos_last;
4328 MapBlock *block = NULL;
4329 bool block_checked_in_modified = false;
4331 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4332 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4333 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4337 u8 f = m_flags[m_area.index(p)];
4338 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4341 MapNode &n = m_data[m_area.index(p)];
4343 v3s16 blockpos = getNodeBlockPos(p);
4348 if(block == NULL || blockpos != blockpos_last){
4349 block = m_map->getBlockNoCreate(blockpos);
4350 blockpos_last = blockpos;
4351 block_checked_in_modified = false;
4354 // Calculate relative position in block
4355 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4357 // Don't continue if nothing has changed here
4358 if(block->getNode(relpos) == n)
4361 //m_map->setNode(m_area.MinEdge + p, n);
4362 block->setNode(relpos, n);
4365 Make sure block is in modified_blocks
4367 if(block_checked_in_modified == false)
4369 modified_blocks[blockpos] = block;
4370 block_checked_in_modified = true;
4373 catch(InvalidPositionException &e)
4379 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4380 MapVoxelManipulator(map),
4381 m_create_area(false)
4385 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4389 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4391 // Just create the area so that it can be pointed to
4392 VoxelManipulator::emerge(a, caller_id);
4395 void ManualMapVoxelManipulator::initialEmerge(
4396 v3s16 blockpos_min, v3s16 blockpos_max)
4398 TimeTaker timer1("initialEmerge", &emerge_time);
4400 // Units of these are MapBlocks
4401 v3s16 p_min = blockpos_min;
4402 v3s16 p_max = blockpos_max;
4404 VoxelArea block_area_nodes
4405 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4407 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4410 infostream<<"initialEmerge: area: ";
4411 block_area_nodes.print(infostream);
4412 infostream<<" ("<<size_MB<<"MB)";
4413 infostream<<std::endl;
4416 addArea(block_area_nodes);
4418 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4419 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4420 for(s32 x=p_min.X; x<=p_max.X; x++)
4423 core::map<v3s16, bool>::Node *n;
4424 n = m_loaded_blocks.find(p);
4428 bool block_data_inexistent = false;
4431 TimeTaker timer1("emerge load", &emerge_load_time);
4433 MapBlock *block = m_map->getBlockNoCreate(p);
4434 if(block->isDummy())
4435 block_data_inexistent = true;
4437 block->copyTo(*this);
4439 catch(InvalidPositionException &e)
4441 block_data_inexistent = true;
4444 if(block_data_inexistent)
4447 Mark area inexistent
4449 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4450 // Fill with VOXELFLAG_INEXISTENT
4451 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4452 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4454 s32 i = m_area.index(a.MinEdge.X,y,z);
4455 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4459 m_loaded_blocks.insert(p, !block_data_inexistent);
4463 void ManualMapVoxelManipulator::blitBackAll(
4464 core::map<v3s16, MapBlock*> * modified_blocks)
4466 if(m_area.getExtent() == v3s16(0,0,0))
4470 Copy data of all blocks
4472 for(core::map<v3s16, bool>::Iterator
4473 i = m_loaded_blocks.getIterator();
4474 i.atEnd() == false; i++)
4476 v3s16 p = i.getNode()->getKey();
4477 bool existed = i.getNode()->getValue();
4478 if(existed == false)
4480 // The Great Bug was found using this
4481 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4482 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4486 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4489 infostream<<"WARNING: "<<__FUNCTION_NAME
4490 <<": got NULL block "
4491 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4496 block->copyFrom(*this);
4499 modified_blocks->insert(p, block);