3 Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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"
27 #include "serialization.h"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
35 #include "util/mathconstants.h"
36 #include "rollback_interface.h"
37 #include "environment.h"
39 #include "mapgen_v6.h"
44 #include "database-dummy.h"
45 #include "database-sqlite3.h"
47 #include "database-leveldb.h"
50 #include "database-redis.h"
53 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
56 SQLite format specification:
57 - Initially only replaces sectors/ and sectors2/
59 If map.sqlite does not exist in the save dir
60 or the block was not found in the database
61 the map will try to load from sectors folder.
62 In either case, map.sqlite will be created
63 and all future saves will save there.
65 Structure of map.sqlite:
76 Map::Map(std::ostream &dout, IGameDef *gamedef):
88 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
89 i != m_sectors.end(); ++i)
95 void Map::addEventReceiver(MapEventReceiver *event_receiver)
97 m_event_receivers.insert(event_receiver);
100 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
102 m_event_receivers.erase(event_receiver);
105 void Map::dispatchEvent(MapEditEvent *event)
107 for(std::set<MapEventReceiver*>::iterator
108 i = m_event_receivers.begin();
109 i != m_event_receivers.end(); ++i)
111 (*i)->onMapEditEvent(event);
115 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
117 if(m_sector_cache != NULL && p == m_sector_cache_p){
118 MapSector * sector = m_sector_cache;
122 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
124 if(n == m_sectors.end())
127 MapSector *sector = n->second;
129 // Cache the last result
130 m_sector_cache_p = p;
131 m_sector_cache = sector;
136 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
138 return getSectorNoGenerateNoExNoLock(p);
141 MapSector * Map::getSectorNoGenerate(v2s16 p)
143 MapSector *sector = getSectorNoGenerateNoEx(p);
145 throw InvalidPositionException();
150 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
152 v2s16 p2d(p3d.X, p3d.Z);
153 MapSector * sector = getSectorNoGenerateNoEx(p2d);
156 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
160 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
162 MapBlock *block = getBlockNoCreateNoEx(p3d);
164 throw InvalidPositionException();
168 bool Map::isNodeUnderground(v3s16 p)
170 v3s16 blockpos = getNodeBlockPos(p);
172 MapBlock * block = getBlockNoCreate(blockpos);
173 return block->getIsUnderground();
175 catch(InvalidPositionException &e)
181 bool Map::isValidPosition(v3s16 p)
183 v3s16 blockpos = getNodeBlockPos(p);
184 MapBlock *block = getBlockNoCreate(blockpos);
185 return (block != NULL);
188 // Returns a CONTENT_IGNORE node if not found
189 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 if (is_valid_position != NULL)
195 *is_valid_position = false;
196 return MapNode(CONTENT_IGNORE);
199 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
201 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
202 if (is_valid_position != NULL)
203 *is_valid_position = is_valid_p;
209 // throws InvalidPositionException if not found
210 // TODO: Now this is deprecated, getNodeNoEx should be renamed
211 MapNode Map::getNode(v3s16 p)
213 v3s16 blockpos = getNodeBlockPos(p);
214 MapBlock *block = getBlockNoCreateNoEx(blockpos);
216 throw InvalidPositionException();
217 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
218 bool is_valid_position;
219 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
220 if (!is_valid_position)
221 throw InvalidPositionException();
226 // throws InvalidPositionException if not found
227 void Map::setNode(v3s16 p, MapNode & n)
229 v3s16 blockpos = getNodeBlockPos(p);
230 MapBlock *block = getBlockNoCreate(blockpos);
231 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
232 // Never allow placing CONTENT_IGNORE, it fucks up stuff
233 if(n.getContent() == CONTENT_IGNORE){
235 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
236 <<" while trying to replace \""
237 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
238 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
239 debug_stacks_print_to(infostream);
242 block->setNodeNoCheck(relpos, n);
247 Goes recursively through the neighbours of the node.
249 Alters only transparent nodes.
251 If the lighting of the neighbour is lower than the lighting of
252 the node was (before changing it to 0 at the step before), the
253 lighting of the neighbour is set to 0 and then the same stuff
254 repeats for the neighbour.
256 The ending nodes of the routine are stored in light_sources.
257 This is useful when a light is removed. In such case, this
258 routine can be called for the light node and then again for
259 light_sources to re-light the area without the removed light.
261 values of from_nodes are lighting values.
263 void Map::unspreadLight(enum LightBank bank,
264 std::map<v3s16, u8> & from_nodes,
265 std::set<v3s16> & light_sources,
266 std::map<v3s16, MapBlock*> & modified_blocks)
268 INodeDefManager *nodemgr = m_gamedef->ndef();
271 v3s16(0,0,1), // back
273 v3s16(1,0,0), // right
274 v3s16(0,0,-1), // front
275 v3s16(0,-1,0), // bottom
276 v3s16(-1,0,0), // left
279 if(from_nodes.empty())
282 u32 blockchangecount = 0;
284 std::map<v3s16, u8> unlighted_nodes;
287 Initialize block cache
290 MapBlock *block = NULL;
291 // Cache this a bit, too
292 bool block_checked_in_modified = false;
294 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
295 j != from_nodes.end(); ++j)
297 v3s16 pos = j->first;
298 v3s16 blockpos = getNodeBlockPos(pos);
300 // Only fetch a new block if the block position has changed
302 if(block == NULL || blockpos != blockpos_last){
303 block = getBlockNoCreate(blockpos);
304 blockpos_last = blockpos;
306 block_checked_in_modified = false;
310 catch(InvalidPositionException &e)
318 // Calculate relative position in block
319 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
321 // Get node straight from the block
322 //MapNode n = block->getNode(relpos);
324 u8 oldlight = j->second;
326 // Loop through 6 neighbors
327 for(u16 i=0; i<6; i++)
329 // Get the position of the neighbor node
330 v3s16 n2pos = pos + dirs[i];
332 // Get the block where the node is located
333 v3s16 blockpos = getNodeBlockPos(n2pos);
335 // Only fetch a new block if the block position has changed
337 if(block == NULL || blockpos != blockpos_last){
338 block = getBlockNoCreate(blockpos);
339 blockpos_last = blockpos;
341 block_checked_in_modified = false;
345 catch(InvalidPositionException &e) {
349 // Calculate relative position in block
350 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
351 // Get node straight from the block
352 bool is_valid_position;
353 MapNode n2 = block->getNode(relpos, &is_valid_position);
354 if (!is_valid_position)
357 bool changed = false;
359 //TODO: Optimize output by optimizing light_sources?
362 If the neighbor is dimmer than what was specified
363 as oldlight (the light of the previous node)
365 if(n2.getLight(bank, nodemgr) < oldlight)
368 And the neighbor is transparent and it has some light
370 if(nodemgr->get(n2).light_propagates
371 && n2.getLight(bank, nodemgr) != 0)
374 Set light to 0 and add to queue
377 u8 current_light = n2.getLight(bank, nodemgr);
378 n2.setLight(bank, 0, nodemgr);
379 block->setNode(relpos, n2);
381 unlighted_nodes[n2pos] = current_light;
385 Remove from light_sources if it is there
386 NOTE: This doesn't happen nearly at all
388 /*if(light_sources.find(n2pos))
390 infostream<<"Removed from light_sources"<<std::endl;
391 light_sources.remove(n2pos);
396 if(light_sources.find(n2pos) != NULL)
397 light_sources.remove(n2pos);*/
400 light_sources.insert(n2pos);
403 // Add to modified_blocks
404 if(changed == true && block_checked_in_modified == false)
406 // If the block is not found in modified_blocks, add.
407 if(modified_blocks.find(blockpos) == modified_blocks.end())
409 modified_blocks[blockpos] = block;
411 block_checked_in_modified = true;
416 /*infostream<<"unspreadLight(): Changed block "
417 <<blockchangecount<<" times"
418 <<" for "<<from_nodes.size()<<" nodes"
421 if(!unlighted_nodes.empty())
422 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
426 A single-node wrapper of the above
428 void Map::unLightNeighbors(enum LightBank bank,
429 v3s16 pos, u8 lightwas,
430 std::set<v3s16> & light_sources,
431 std::map<v3s16, MapBlock*> & modified_blocks)
433 std::map<v3s16, u8> from_nodes;
434 from_nodes[pos] = lightwas;
436 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
440 Lights neighbors of from_nodes, collects all them and then
443 void Map::spreadLight(enum LightBank bank,
444 std::set<v3s16> & from_nodes,
445 std::map<v3s16, MapBlock*> & modified_blocks)
447 INodeDefManager *nodemgr = m_gamedef->ndef();
449 const v3s16 dirs[6] = {
450 v3s16(0,0,1), // back
452 v3s16(1,0,0), // right
453 v3s16(0,0,-1), // front
454 v3s16(0,-1,0), // bottom
455 v3s16(-1,0,0), // left
458 if(from_nodes.empty())
461 u32 blockchangecount = 0;
463 std::set<v3s16> lighted_nodes;
466 Initialize block cache
469 MapBlock *block = NULL;
470 // Cache this a bit, too
471 bool block_checked_in_modified = false;
473 for(std::set<v3s16>::iterator j = from_nodes.begin();
474 j != from_nodes.end(); ++j)
477 v3s16 blockpos = getNodeBlockPos(pos);
479 // Only fetch a new block if the block position has changed
481 if(block == NULL || blockpos != blockpos_last){
482 block = getBlockNoCreate(blockpos);
483 blockpos_last = blockpos;
485 block_checked_in_modified = false;
489 catch(InvalidPositionException &e) {
496 // Calculate relative position in block
497 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
499 // Get node straight from the block
500 bool is_valid_position;
501 MapNode n = block->getNode(relpos, &is_valid_position);
503 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
504 u8 newlight = diminish_light(oldlight);
506 // Loop through 6 neighbors
507 for(u16 i=0; i<6; i++){
508 // Get the position of the neighbor node
509 v3s16 n2pos = pos + dirs[i];
511 // Get the block where the node is located
512 v3s16 blockpos = getNodeBlockPos(n2pos);
514 // Only fetch a new block if the block position has changed
516 if(block == NULL || blockpos != blockpos_last){
517 block = getBlockNoCreate(blockpos);
518 blockpos_last = blockpos;
520 block_checked_in_modified = false;
524 catch(InvalidPositionException &e) {
528 // Calculate relative position in block
529 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
530 // Get node straight from the block
531 MapNode n2 = block->getNode(relpos, &is_valid_position);
532 if (!is_valid_position)
535 bool changed = false;
537 If the neighbor is brighter than the current node,
538 add to list (it will light up this node on its turn)
540 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
542 lighted_nodes.insert(n2pos);
546 If the neighbor is dimmer than how much light this node
547 would spread on it, add to list
549 if(n2.getLight(bank, nodemgr) < newlight)
551 if(nodemgr->get(n2).light_propagates)
553 n2.setLight(bank, newlight, nodemgr);
554 block->setNode(relpos, n2);
555 lighted_nodes.insert(n2pos);
560 // Add to modified_blocks
561 if(changed == true && block_checked_in_modified == false)
563 // If the block is not found in modified_blocks, add.
564 if(modified_blocks.find(blockpos) == modified_blocks.end())
566 modified_blocks[blockpos] = block;
568 block_checked_in_modified = true;
573 /*infostream<<"spreadLight(): Changed block "
574 <<blockchangecount<<" times"
575 <<" for "<<from_nodes.size()<<" nodes"
578 if(!lighted_nodes.empty())
579 spreadLight(bank, lighted_nodes, modified_blocks);
583 A single-node source variation of the above.
585 void Map::lightNeighbors(enum LightBank bank,
587 std::map<v3s16, MapBlock*> & modified_blocks)
589 std::set<v3s16> from_nodes;
590 from_nodes.insert(pos);
591 spreadLight(bank, from_nodes, modified_blocks);
594 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
596 INodeDefManager *nodemgr = m_gamedef->ndef();
599 v3s16(0,0,1), // back
601 v3s16(1,0,0), // right
602 v3s16(0,0,-1), // front
603 v3s16(0,-1,0), // bottom
604 v3s16(-1,0,0), // left
607 u8 brightest_light = 0;
608 v3s16 brightest_pos(0,0,0);
609 bool found_something = false;
611 // Loop through 6 neighbors
612 for(u16 i=0; i<6; i++){
613 // Get the position of the neighbor node
614 v3s16 n2pos = p + dirs[i];
616 bool is_valid_position;
617 n2 = getNodeNoEx(n2pos, &is_valid_position);
618 if (!is_valid_position)
621 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
622 brightest_light = n2.getLight(bank, nodemgr);
623 brightest_pos = n2pos;
624 found_something = true;
628 if(found_something == false)
629 throw InvalidPositionException();
631 return brightest_pos;
635 Propagates sunlight down from a node.
636 Starting point gets sunlight.
638 Returns the lowest y value of where the sunlight went.
640 Mud is turned into grass in where the sunlight stops.
642 s16 Map::propagateSunlight(v3s16 start,
643 std::map<v3s16, MapBlock*> & modified_blocks)
645 INodeDefManager *nodemgr = m_gamedef->ndef();
650 v3s16 pos(start.X, y, start.Z);
652 v3s16 blockpos = getNodeBlockPos(pos);
655 block = getBlockNoCreate(blockpos);
657 catch(InvalidPositionException &e)
662 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
663 bool is_valid_position;
664 MapNode n = block->getNode(relpos, &is_valid_position);
665 if (!is_valid_position)
668 if(nodemgr->get(n).sunlight_propagates)
670 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
671 block->setNode(relpos, n);
673 modified_blocks[blockpos] = block;
677 // Sunlight goes no further
684 void Map::updateLighting(enum LightBank bank,
685 std::map<v3s16, MapBlock*> & a_blocks,
686 std::map<v3s16, MapBlock*> & modified_blocks)
688 INodeDefManager *nodemgr = m_gamedef->ndef();
690 /*m_dout<<DTIME<<"Map::updateLighting(): "
691 <<a_blocks.size()<<" blocks."<<std::endl;*/
693 //TimeTaker timer("updateLighting");
697 //u32 count_was = modified_blocks.size();
699 std::map<v3s16, MapBlock*> blocks_to_update;
701 std::set<v3s16> light_sources;
703 std::map<v3s16, u8> unlight_from;
705 int num_bottom_invalid = 0;
708 //TimeTaker t("first stuff");
710 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
711 i != a_blocks.end(); ++i)
713 MapBlock *block = i->second;
717 // Don't bother with dummy blocks.
721 v3s16 pos = block->getPos();
722 v3s16 posnodes = block->getPosRelative();
723 modified_blocks[pos] = block;
724 blocks_to_update[pos] = block;
727 Clear all light from block
729 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
730 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
731 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
734 bool is_valid_position;
735 MapNode n = block->getNode(p, &is_valid_position);
736 if (!is_valid_position) {
737 /* This would happen when dealing with a
740 infostream<<"updateLighting(): InvalidPositionException"
744 u8 oldlight = n.getLight(bank, nodemgr);
745 n.setLight(bank, 0, nodemgr);
746 block->setNode(p, n);
748 // If node sources light, add to list
749 u8 source = nodemgr->get(n).light_source;
751 light_sources.insert(p + posnodes);
753 // Collect borders for unlighting
754 if((x==0 || x == MAP_BLOCKSIZE-1
755 || y==0 || y == MAP_BLOCKSIZE-1
756 || z==0 || z == MAP_BLOCKSIZE-1)
759 v3s16 p_map = p + posnodes;
760 unlight_from[p_map] = oldlight;
766 if(bank == LIGHTBANK_DAY)
768 bool bottom_valid = block->propagateSunlight(light_sources);
771 num_bottom_invalid++;
773 // If bottom is valid, we're done.
777 else if(bank == LIGHTBANK_NIGHT)
779 // For night lighting, sunlight is not propagated
784 // Invalid lighting bank
788 /*infostream<<"Bottom for sunlight-propagated block ("
789 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
792 // Bottom sunlight is not valid; get the block and loop to it
796 block = getBlockNoCreate(pos);
798 catch(InvalidPositionException &e)
809 Enable this to disable proper lighting for speeding up map
810 generation for testing or whatever
813 //if(g_settings->get(""))
815 core::map<v3s16, MapBlock*>::Iterator i;
816 i = blocks_to_update.getIterator();
817 for(; i.atEnd() == false; i++)
819 MapBlock *block = i.getNode()->getValue();
820 v3s16 p = block->getPos();
821 block->setLightingExpired(false);
829 //TimeTaker timer("unspreadLight");
830 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
835 u32 diff = modified_blocks.size() - count_was;
836 count_was = modified_blocks.size();
837 infostream<<"unspreadLight modified "<<diff<<std::endl;
841 //TimeTaker timer("spreadLight");
842 spreadLight(bank, light_sources, modified_blocks);
847 u32 diff = modified_blocks.size() - count_was;
848 count_was = modified_blocks.size();
849 infostream<<"spreadLight modified "<<diff<<std::endl;
855 //MapVoxelManipulator vmanip(this);
857 // Make a manual voxel manipulator and load all the blocks
858 // that touch the requested blocks
859 ManualMapVoxelManipulator vmanip(this);
862 //TimeTaker timer("initialEmerge");
864 core::map<v3s16, MapBlock*>::Iterator i;
865 i = blocks_to_update.getIterator();
866 for(; i.atEnd() == false; i++)
868 MapBlock *block = i.getNode()->getValue();
869 v3s16 p = block->getPos();
871 // Add all surrounding blocks
872 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
875 Add all surrounding blocks that have up-to-date lighting
876 NOTE: This doesn't quite do the job (not everything
877 appropriate is lighted)
879 /*for(s16 z=-1; z<=1; z++)
880 for(s16 y=-1; y<=1; y++)
881 for(s16 x=-1; x<=1; x++)
883 v3s16 p2 = p + v3s16(x,y,z);
884 MapBlock *block = getBlockNoCreateNoEx(p2);
889 if(block->getLightingExpired())
891 vmanip.initialEmerge(p2, p2);
894 // Lighting of block will be updated completely
895 block->setLightingExpired(false);
900 //TimeTaker timer("unSpreadLight");
901 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
904 //TimeTaker timer("spreadLight");
905 vmanip.spreadLight(bank, light_sources, nodemgr);
908 //TimeTaker timer("blitBack");
909 vmanip.blitBack(modified_blocks);
911 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
916 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
919 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
920 std::map<v3s16, MapBlock*> & modified_blocks)
922 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
923 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
926 Update information about whether day and night light differ
928 for(std::map<v3s16, MapBlock*>::iterator
929 i = modified_blocks.begin();
930 i != modified_blocks.end(); ++i)
932 MapBlock *block = i->second;
933 block->expireDayNightDiff();
939 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
940 std::map<v3s16, MapBlock*> &modified_blocks,
941 bool remove_metadata)
943 INodeDefManager *ndef = m_gamedef->ndef();
946 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
947 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
950 From this node to nodes underneath:
951 If lighting is sunlight (1.0), unlight neighbours and
956 v3s16 toppos = p + v3s16(0,1,0);
957 //v3s16 bottompos = p + v3s16(0,-1,0);
959 bool node_under_sunlight = true;
960 std::set<v3s16> light_sources;
963 Collect old node for rollback
965 RollbackNode rollback_oldnode(this, p, m_gamedef);
968 If there is a node at top and it doesn't have sunlight,
969 there has not been any sunlight going down.
971 Otherwise there probably is.
974 bool is_valid_position;
975 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
977 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
978 node_under_sunlight = false;
981 Remove all light that has come out of this node
984 enum LightBank banks[] =
989 for(s32 i=0; i<2; i++)
991 enum LightBank bank = banks[i];
993 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
995 // Add the block of the added node to modified_blocks
996 v3s16 blockpos = getNodeBlockPos(p);
997 MapBlock * block = getBlockNoCreate(blockpos);
998 assert(block != NULL);
999 modified_blocks[blockpos] = block;
1001 assert(isValidPosition(p));
1003 // Unlight neighbours of node.
1004 // This means setting light of all consequent dimmer nodes
1006 // This also collects the nodes at the border which will spread
1007 // light again into this.
1008 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1010 n.setLight(bank, 0, ndef);
1014 If node lets sunlight through and is under sunlight, it has
1017 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1019 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1023 Remove node metadata
1025 if (remove_metadata) {
1026 removeNodeMetadata(p);
1030 Set the node on the map
1036 If node is under sunlight and doesn't let sunlight through,
1037 take all sunlighted nodes under it and clear light from them
1038 and from where the light has been spread.
1039 TODO: This could be optimized by mass-unlighting instead
1042 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1046 //m_dout<<DTIME<<"y="<<y<<std::endl;
1047 v3s16 n2pos(p.X, y, p.Z);
1051 n2 = getNodeNoEx(n2pos, &is_valid_position);
1052 if (!is_valid_position)
1055 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1057 unLightNeighbors(LIGHTBANK_DAY,
1058 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1059 light_sources, modified_blocks);
1060 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1068 for(s32 i=0; i<2; i++)
1070 enum LightBank bank = banks[i];
1073 Spread light from all nodes that might be capable of doing so
1075 spreadLight(bank, light_sources, modified_blocks);
1079 Update information about whether day and night light differ
1081 for(std::map<v3s16, MapBlock*>::iterator
1082 i = modified_blocks.begin();
1083 i != modified_blocks.end(); ++i)
1085 i->second->expireDayNightDiff();
1091 if(m_gamedef->rollback())
1093 RollbackNode rollback_newnode(this, p, m_gamedef);
1094 RollbackAction action;
1095 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1096 m_gamedef->rollback()->reportAction(action);
1100 Add neighboring liquid nodes and the node itself if it is
1101 liquid (=water node was added) to transform queue.
1104 v3s16(0,0,0), // self
1105 v3s16(0,0,1), // back
1106 v3s16(0,1,0), // top
1107 v3s16(1,0,0), // right
1108 v3s16(0,0,-1), // front
1109 v3s16(0,-1,0), // bottom
1110 v3s16(-1,0,0), // left
1112 for(u16 i=0; i<7; i++)
1114 v3s16 p2 = p + dirs[i];
1116 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1117 if(is_valid_position
1118 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1120 m_transforming_liquid.push_back(p2);
1127 void Map::removeNodeAndUpdate(v3s16 p,
1128 std::map<v3s16, MapBlock*> &modified_blocks)
1130 INodeDefManager *ndef = m_gamedef->ndef();
1132 /*PrintInfo(m_dout);
1133 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1134 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1136 bool node_under_sunlight = true;
1138 v3s16 toppos = p + v3s16(0,1,0);
1140 // Node will be replaced with this
1141 content_t replace_material = CONTENT_AIR;
1144 Collect old node for rollback
1146 RollbackNode rollback_oldnode(this, p, m_gamedef);
1149 If there is a node at top and it doesn't have sunlight,
1150 there will be no sunlight going down.
1152 bool is_valid_position;
1153 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1155 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1156 node_under_sunlight = false;
1158 std::set<v3s16> light_sources;
1160 enum LightBank banks[] =
1165 for(s32 i=0; i<2; i++)
1167 enum LightBank bank = banks[i];
1170 Unlight neighbors (in case the node is a light source)
1172 unLightNeighbors(bank, p,
1173 getNodeNoEx(p).getLight(bank, ndef),
1174 light_sources, modified_blocks);
1178 Remove node metadata
1181 removeNodeMetadata(p);
1185 This also clears the lighting.
1189 n.setContent(replace_material);
1192 for(s32 i=0; i<2; i++)
1194 enum LightBank bank = banks[i];
1197 Recalculate lighting
1199 spreadLight(bank, light_sources, modified_blocks);
1202 // Add the block of the removed node to modified_blocks
1203 v3s16 blockpos = getNodeBlockPos(p);
1204 MapBlock * block = getBlockNoCreate(blockpos);
1205 assert(block != NULL);
1206 modified_blocks[blockpos] = block;
1209 If the removed node was under sunlight, propagate the
1210 sunlight down from it and then light all neighbors
1211 of the propagated blocks.
1213 if(node_under_sunlight)
1215 s16 ybottom = propagateSunlight(p, modified_blocks);
1216 /*m_dout<<DTIME<<"Node was under sunlight. "
1217 "Propagating sunlight";
1218 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1220 for(; y >= ybottom; y--)
1222 v3s16 p2(p.X, y, p.Z);
1223 /*m_dout<<DTIME<<"lighting neighbors of node ("
1224 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1226 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1231 // Set the lighting of this node to 0
1232 // TODO: Is this needed? Lighting is cleared up there already.
1233 MapNode n = getNodeNoEx(p, &is_valid_position);
1234 if (is_valid_position) {
1235 n.setLight(LIGHTBANK_DAY, 0, ndef);
1242 for(s32 i=0; i<2; i++)
1244 enum LightBank bank = banks[i];
1246 // Get the brightest neighbour node and propagate light from it
1247 v3s16 n2p = getBrightestNeighbour(bank, p);
1249 //MapNode n2 = getNode(n2p);
1250 lightNeighbors(bank, n2p, modified_blocks);
1252 catch(InvalidPositionException &e)
1258 Update information about whether day and night light differ
1260 for(std::map<v3s16, MapBlock*>::iterator
1261 i = modified_blocks.begin();
1262 i != modified_blocks.end(); ++i)
1264 i->second->expireDayNightDiff();
1270 if(m_gamedef->rollback())
1272 RollbackNode rollback_newnode(this, p, m_gamedef);
1273 RollbackAction action;
1274 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1275 m_gamedef->rollback()->reportAction(action);
1279 Add neighboring liquid nodes and this node to transform queue.
1280 (it's vital for the node itself to get updated last.)
1283 v3s16(0,0,1), // back
1284 v3s16(0,1,0), // top
1285 v3s16(1,0,0), // right
1286 v3s16(0,0,-1), // front
1287 v3s16(0,-1,0), // bottom
1288 v3s16(-1,0,0), // left
1289 v3s16(0,0,0), // self
1291 for(u16 i=0; i<7; i++)
1293 v3s16 p2 = p + dirs[i];
1295 bool is_position_valid;
1296 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1297 if (is_position_valid
1298 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1300 m_transforming_liquid.push_back(p2);
1305 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1308 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1312 bool succeeded = true;
1314 std::map<v3s16, MapBlock*> modified_blocks;
1315 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1317 // Copy modified_blocks to event
1318 for(std::map<v3s16, MapBlock*>::iterator
1319 i = modified_blocks.begin();
1320 i != modified_blocks.end(); ++i)
1322 event.modified_blocks.insert(i->first);
1325 catch(InvalidPositionException &e){
1329 dispatchEvent(&event);
1334 bool Map::removeNodeWithEvent(v3s16 p)
1337 event.type = MEET_REMOVENODE;
1340 bool succeeded = true;
1342 std::map<v3s16, MapBlock*> modified_blocks;
1343 removeNodeAndUpdate(p, modified_blocks);
1345 // Copy modified_blocks to event
1346 for(std::map<v3s16, MapBlock*>::iterator
1347 i = modified_blocks.begin();
1348 i != modified_blocks.end(); ++i)
1350 event.modified_blocks.insert(i->first);
1353 catch(InvalidPositionException &e){
1357 dispatchEvent(&event);
1362 bool Map::getDayNightDiff(v3s16 blockpos)
1365 v3s16 p = blockpos + v3s16(0,0,0);
1366 MapBlock *b = getBlockNoCreate(p);
1367 if(b->getDayNightDiff())
1370 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(-1,0,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->getDayNightDiff())
1378 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(0,-1,0);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->getDayNightDiff())
1385 catch(InvalidPositionException &e){}
1387 v3s16 p = blockpos + v3s16(0,0,-1);
1388 MapBlock *b = getBlockNoCreate(p);
1389 if(b->getDayNightDiff())
1392 catch(InvalidPositionException &e){}
1395 v3s16 p = blockpos + v3s16(1,0,0);
1396 MapBlock *b = getBlockNoCreate(p);
1397 if(b->getDayNightDiff())
1400 catch(InvalidPositionException &e){}
1402 v3s16 p = blockpos + v3s16(0,1,0);
1403 MapBlock *b = getBlockNoCreate(p);
1404 if(b->getDayNightDiff())
1407 catch(InvalidPositionException &e){}
1409 v3s16 p = blockpos + v3s16(0,0,1);
1410 MapBlock *b = getBlockNoCreate(p);
1411 if(b->getDayNightDiff())
1414 catch(InvalidPositionException &e){}
1420 Updates usage timers
1422 void Map::timerUpdate(float dtime, float unload_timeout,
1423 std::list<v3s16> *unloaded_blocks)
1425 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1427 // Profile modified reasons
1428 Profiler modprofiler;
1430 std::list<v2s16> sector_deletion_queue;
1431 u32 deleted_blocks_count = 0;
1432 u32 saved_blocks_count = 0;
1433 u32 block_count_all = 0;
1436 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1437 si != m_sectors.end(); ++si)
1439 MapSector *sector = si->second;
1441 bool all_blocks_deleted = true;
1443 std::list<MapBlock*> blocks;
1444 sector->getBlocks(blocks);
1446 for(std::list<MapBlock*>::iterator i = blocks.begin();
1447 i != blocks.end(); ++i)
1449 MapBlock *block = (*i);
1451 block->incrementUsageTimer(dtime);
1453 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1455 v3s16 p = block->getPos();
1458 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading)
1460 modprofiler.add(block->getModifiedReason(), 1);
1461 if (!saveBlock(block))
1463 saved_blocks_count++;
1466 // Delete from memory
1467 sector->deleteBlock(block);
1470 unloaded_blocks->push_back(p);
1472 deleted_blocks_count++;
1476 all_blocks_deleted = false;
1481 if(all_blocks_deleted)
1483 sector_deletion_queue.push_back(si->first);
1488 // Finally delete the empty sectors
1489 deleteSectors(sector_deletion_queue);
1491 if(deleted_blocks_count != 0)
1493 PrintInfo(infostream); // ServerMap/ClientMap:
1494 infostream<<"Unloaded "<<deleted_blocks_count
1495 <<" blocks from memory";
1496 if(save_before_unloading)
1497 infostream<<", of which "<<saved_blocks_count<<" were written";
1498 infostream<<", "<<block_count_all<<" blocks in memory";
1499 infostream<<"."<<std::endl;
1500 if(saved_blocks_count != 0){
1501 PrintInfo(infostream); // ServerMap/ClientMap:
1502 infostream<<"Blocks modified by: "<<std::endl;
1503 modprofiler.print(infostream);
1508 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1510 timerUpdate(0.0, -1.0, unloaded_blocks);
1513 void Map::deleteSectors(std::list<v2s16> &list)
1515 for(std::list<v2s16>::iterator j = list.begin();
1516 j != list.end(); ++j)
1518 MapSector *sector = m_sectors[*j];
1519 // If sector is in sector cache, remove it from there
1520 if(m_sector_cache == sector)
1521 m_sector_cache = NULL;
1522 // Remove from map and delete
1523 m_sectors.erase(*j);
1529 void Map::unloadUnusedData(float timeout,
1530 core::list<v3s16> *deleted_blocks)
1532 core::list<v2s16> sector_deletion_queue;
1533 u32 deleted_blocks_count = 0;
1534 u32 saved_blocks_count = 0;
1536 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1537 for(; si.atEnd() == false; si++)
1539 MapSector *sector = si.getNode()->getValue();
1541 bool all_blocks_deleted = true;
1543 core::list<MapBlock*> blocks;
1544 sector->getBlocks(blocks);
1545 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1546 i != blocks.end(); i++)
1548 MapBlock *block = (*i);
1550 if(block->getUsageTimer() > timeout)
1553 if(block->getModified() != MOD_STATE_CLEAN)
1556 saved_blocks_count++;
1558 // Delete from memory
1559 sector->deleteBlock(block);
1560 deleted_blocks_count++;
1564 all_blocks_deleted = false;
1568 if(all_blocks_deleted)
1570 sector_deletion_queue.push_back(si.getNode()->getKey());
1574 deleteSectors(sector_deletion_queue);
1576 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1577 <<", of which "<<saved_blocks_count<<" were wr."
1580 //return sector_deletion_queue.getSize();
1581 //return deleted_blocks_count;
1585 void Map::PrintInfo(std::ostream &out)
1590 #define WATER_DROP_BOOST 4
1594 NEIGHBOR_SAME_LEVEL,
1597 struct NodeNeighbor {
1601 bool l; //can liquid
1604 void Map::transforming_liquid_add(v3s16 p) {
1605 m_transforming_liquid.push_back(p);
1608 s32 Map::transforming_liquid_size() {
1609 return m_transforming_liquid.size();
1612 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1614 INodeDefManager *nodemgr = m_gamedef->ndef();
1616 DSTACK(__FUNCTION_NAME);
1617 //TimeTaker timer("transformLiquids()");
1620 u32 initial_size = m_transforming_liquid.size();
1622 /*if(initial_size != 0)
1623 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1625 // list of nodes that due to viscosity have not reached their max level height
1626 UniqueQueue<v3s16> must_reflow;
1628 // List of MapBlocks that will require a lighting update (due to lava)
1629 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1631 u16 loop_max = g_settings->getU16("liquid_loop_max");
1633 while(m_transforming_liquid.size() != 0)
1635 // This should be done here so that it is done when continue is used
1636 if(loopcount >= initial_size || loopcount >= loop_max)
1641 Get a queued transforming liquid node
1643 v3s16 p0 = m_transforming_liquid.pop_front();
1645 MapNode n0 = getNodeNoEx(p0);
1648 Collect information about current node
1650 s8 liquid_level = -1;
1651 content_t liquid_kind = CONTENT_IGNORE;
1652 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1653 switch (liquid_type) {
1655 liquid_level = LIQUID_LEVEL_SOURCE;
1656 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1658 case LIQUID_FLOWING:
1659 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1660 liquid_kind = n0.getContent();
1663 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1664 // continue with the next node.
1665 if (n0.getContent() != CONTENT_AIR)
1667 liquid_kind = CONTENT_AIR;
1672 Collect information about the environment
1674 const v3s16 *dirs = g_6dirs;
1675 NodeNeighbor sources[6]; // surrounding sources
1676 int num_sources = 0;
1677 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1679 NodeNeighbor airs[6]; // surrounding air
1681 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1682 int num_neutrals = 0;
1683 bool flowing_down = false;
1684 for (u16 i = 0; i < 6; i++) {
1685 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1688 nt = NEIGHBOR_UPPER;
1691 nt = NEIGHBOR_LOWER;
1694 v3s16 npos = p0 + dirs[i];
1695 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1696 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1698 if (nb.n.getContent() == CONTENT_AIR) {
1699 airs[num_airs++] = nb;
1700 // if the current node is a water source the neighbor
1701 // should be enqueded for transformation regardless of whether the
1702 // current node changes or not.
1703 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1704 m_transforming_liquid.push_back(npos);
1705 // if the current node happens to be a flowing node, it will start to flow down here.
1706 if (nb.t == NEIGHBOR_LOWER) {
1707 flowing_down = true;
1710 neutrals[num_neutrals++] = nb;
1714 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1715 if (liquid_kind == CONTENT_AIR)
1716 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1717 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1718 neutrals[num_neutrals++] = nb;
1720 // Do not count bottom source, it will screw things up
1722 sources[num_sources++] = nb;
1725 case LIQUID_FLOWING:
1726 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1727 if (liquid_kind == CONTENT_AIR)
1728 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1729 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1730 neutrals[num_neutrals++] = nb;
1732 flows[num_flows++] = nb;
1733 if (nb.t == NEIGHBOR_LOWER)
1734 flowing_down = true;
1741 decide on the type (and possibly level) of the current node
1743 content_t new_node_content;
1744 s8 new_node_level = -1;
1745 s8 max_node_level = -1;
1746 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
1747 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1748 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1749 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1750 // it's perfectly safe to use liquid_kind here to determine the new node content.
1751 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1752 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1753 // liquid_kind is set properly, see above
1754 new_node_content = liquid_kind;
1755 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1756 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1757 new_node_content = CONTENT_AIR;
1759 // no surrounding sources, so get the maximum level that can flow into this node
1760 for (u16 i = 0; i < num_flows; i++) {
1761 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1762 switch (flows[i].t) {
1763 case NEIGHBOR_UPPER:
1764 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1765 max_node_level = LIQUID_LEVEL_MAX;
1766 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1767 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1768 } else if (nb_liquid_level > max_node_level)
1769 max_node_level = nb_liquid_level;
1771 case NEIGHBOR_LOWER:
1773 case NEIGHBOR_SAME_LEVEL:
1774 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1775 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1776 max_node_level = nb_liquid_level - 1;
1782 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1783 if (viscosity > 1 && max_node_level != liquid_level) {
1784 // amount to gain, limited by viscosity
1785 // must be at least 1 in absolute value
1786 s8 level_inc = max_node_level - liquid_level;
1787 if (level_inc < -viscosity || level_inc > viscosity)
1788 new_node_level = liquid_level + level_inc/viscosity;
1789 else if (level_inc < 0)
1790 new_node_level = liquid_level - 1;
1791 else if (level_inc > 0)
1792 new_node_level = liquid_level + 1;
1793 if (new_node_level != max_node_level)
1794 must_reflow.push_back(p0);
1796 new_node_level = max_node_level;
1798 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1799 new_node_content = liquid_kind;
1801 new_node_content = CONTENT_AIR;
1806 check if anything has changed. if not, just continue with the next node.
1808 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1809 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1810 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1816 update the current node
1819 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1820 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1821 // set level to last 3 bits, flowing down bit to 4th bit
1822 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1824 // set the liquid level and flow bit to 0
1825 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1827 n0.setContent(new_node_content);
1829 // Find out whether there is a suspect for this action
1830 std::string suspect;
1831 if(m_gamedef->rollback()){
1832 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1835 if(!suspect.empty()){
1837 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1838 // Get old node for rollback
1839 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1843 RollbackNode rollback_newnode(this, p0, m_gamedef);
1844 RollbackAction action;
1845 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1846 m_gamedef->rollback()->reportAction(action);
1852 v3s16 blockpos = getNodeBlockPos(p0);
1853 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1855 modified_blocks[blockpos] = block;
1856 // If new or old node emits light, MapBlock requires lighting update
1857 if(nodemgr->get(n0).light_source != 0 ||
1858 nodemgr->get(n00).light_source != 0)
1859 lighting_modified_blocks[block->getPos()] = block;
1863 enqueue neighbors for update if neccessary
1865 switch (nodemgr->get(n0.getContent()).liquid_type) {
1867 case LIQUID_FLOWING:
1868 // make sure source flows into all neighboring nodes
1869 for (u16 i = 0; i < num_flows; i++)
1870 if (flows[i].t != NEIGHBOR_UPPER)
1871 m_transforming_liquid.push_back(flows[i].p);
1872 for (u16 i = 0; i < num_airs; i++)
1873 if (airs[i].t != NEIGHBOR_UPPER)
1874 m_transforming_liquid.push_back(airs[i].p);
1877 // this flow has turned to air; neighboring flows might need to do the same
1878 for (u16 i = 0; i < num_flows; i++)
1879 m_transforming_liquid.push_back(flows[i].p);
1883 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1884 while (must_reflow.size() > 0)
1885 m_transforming_liquid.push_back(must_reflow.pop_front());
1886 updateLighting(lighting_modified_blocks, modified_blocks);
1889 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1891 v3s16 blockpos = getNodeBlockPos(p);
1892 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1893 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1895 infostream<<"Map::getNodeMetadata(): Need to emerge "
1896 <<PP(blockpos)<<std::endl;
1897 block = emergeBlock(blockpos, false);
1900 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1904 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1908 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1910 v3s16 blockpos = getNodeBlockPos(p);
1911 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1912 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1914 infostream<<"Map::setNodeMetadata(): Need to emerge "
1915 <<PP(blockpos)<<std::endl;
1916 block = emergeBlock(blockpos, false);
1919 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1923 block->m_node_metadata.set(p_rel, meta);
1927 void Map::removeNodeMetadata(v3s16 p)
1929 v3s16 blockpos = getNodeBlockPos(p);
1930 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1931 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1934 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1938 block->m_node_metadata.remove(p_rel);
1941 NodeTimer Map::getNodeTimer(v3s16 p)
1943 v3s16 blockpos = getNodeBlockPos(p);
1944 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1945 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1947 infostream<<"Map::getNodeTimer(): Need to emerge "
1948 <<PP(blockpos)<<std::endl;
1949 block = emergeBlock(blockpos, false);
1952 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1956 NodeTimer t = block->m_node_timers.get(p_rel);
1960 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1962 v3s16 blockpos = getNodeBlockPos(p);
1963 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1964 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1966 infostream<<"Map::setNodeTimer(): Need to emerge "
1967 <<PP(blockpos)<<std::endl;
1968 block = emergeBlock(blockpos, false);
1971 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1975 block->m_node_timers.set(p_rel, t);
1978 void Map::removeNodeTimer(v3s16 p)
1980 v3s16 blockpos = getNodeBlockPos(p);
1981 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1982 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1985 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
1989 block->m_node_timers.remove(p_rel);
1995 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
1996 Map(dout_server, gamedef),
1998 m_map_metadata_changed(true)
2000 verbosestream<<__FUNCTION_NAME<<std::endl;
2003 Try to load map; if not found, create a new one.
2006 // Determine which database backend to use
2007 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2009 bool succeeded = conf.readConfigFile(conf_path.c_str());
2010 if (!succeeded || !conf.exists("backend")) {
2011 // fall back to sqlite3
2012 dbase = new Database_SQLite3(this, savedir);
2013 conf.set("backend", "sqlite3");
2015 std::string backend = conf.get("backend");
2016 if (backend == "dummy")
2017 dbase = new Database_Dummy(this);
2018 else if (backend == "sqlite3")
2019 dbase = new Database_SQLite3(this, savedir);
2021 else if (backend == "leveldb")
2022 dbase = new Database_LevelDB(this, savedir);
2025 else if (backend == "redis")
2026 dbase = new Database_Redis(this, savedir);
2029 throw BaseException("Unknown map backend");
2032 m_savedir = savedir;
2033 m_map_saving_enabled = false;
2037 // If directory exists, check contents and load if possible
2038 if(fs::PathExists(m_savedir))
2040 // If directory is empty, it is safe to save into it.
2041 if(fs::GetDirListing(m_savedir).size() == 0)
2043 infostream<<"ServerMap: Empty save directory is valid."
2045 m_map_saving_enabled = true;
2050 // Load map metadata (seed, chunksize)
2053 catch(SettingNotFoundException &e){
2054 infostream<<"ServerMap: Some metadata not found."
2055 <<" Using default settings."<<std::endl;
2057 catch(FileNotGoodException &e){
2058 infostream<<"WARNING: Could not load map metadata"
2059 //<<" Disabling chunk-based generator."
2064 infostream<<"ServerMap: Successfully loaded map "
2065 <<"metadata from "<<savedir
2066 <<", assuming valid save directory."
2067 <<" seed="<< m_emerge->params.seed <<"."
2070 m_map_saving_enabled = true;
2071 // Map loaded, not creating new one
2075 // If directory doesn't exist, it is safe to save to it
2077 m_map_saving_enabled = true;
2080 catch(std::exception &e)
2082 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2083 <<", exception: "<<e.what()<<std::endl;
2084 infostream<<"Please remove the map or fix it."<<std::endl;
2085 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2088 infostream<<"Initializing new map."<<std::endl;
2090 // Create zero sector
2091 emergeSector(v2s16(0,0));
2093 // Initially write whole map
2094 save(MOD_STATE_CLEAN);
2097 ServerMap::~ServerMap()
2099 verbosestream<<__FUNCTION_NAME<<std::endl;
2103 if(m_map_saving_enabled)
2105 // Save only changed parts
2106 save(MOD_STATE_WRITE_AT_UNLOAD);
2107 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2111 infostream<<"ServerMap: Map not saved"<<std::endl;
2114 catch(std::exception &e)
2116 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2117 <<", exception: "<<e.what()<<std::endl;
2121 Close database if it was opened
2129 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2130 for(; i.atEnd() == false; i++)
2132 MapChunk *chunk = i.getNode()->getValue();
2138 u64 ServerMap::getSeed()
2140 return m_emerge->params.seed;
2143 s16 ServerMap::getWaterLevel()
2145 return m_emerge->params.water_level;
2148 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2150 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2151 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2153 s16 chunksize = m_emerge->params.chunksize;
2154 s16 coffset = -chunksize / 2;
2155 v3s16 chunk_offset(coffset, coffset, coffset);
2156 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2157 v3s16 blockpos_min = blockpos_div * chunksize;
2158 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2159 blockpos_min += chunk_offset;
2160 blockpos_max += chunk_offset;
2162 v3s16 extra_borders(1,1,1);
2164 // Do nothing if not inside limits (+-1 because of neighbors)
2165 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2166 blockpos_over_limit(blockpos_max + extra_borders))
2169 data->seed = m_emerge->params.seed;
2170 data->blockpos_min = blockpos_min;
2171 data->blockpos_max = blockpos_max;
2172 data->blockpos_requested = blockpos;
2173 data->nodedef = m_gamedef->ndef();
2176 Create the whole area of this and the neighboring blocks
2179 //TimeTaker timer("initBlockMake() create area");
2181 for(s16 x=blockpos_min.X-extra_borders.X;
2182 x<=blockpos_max.X+extra_borders.X; x++)
2183 for(s16 z=blockpos_min.Z-extra_borders.Z;
2184 z<=blockpos_max.Z+extra_borders.Z; z++)
2186 v2s16 sectorpos(x, z);
2187 // Sector metadata is loaded from disk if not already loaded.
2188 ServerMapSector *sector = createSector(sectorpos);
2192 for(s16 y=blockpos_min.Y-extra_borders.Y;
2193 y<=blockpos_max.Y+extra_borders.Y; y++)
2196 //MapBlock *block = createBlock(p);
2197 // 1) get from memory, 2) load from disk
2198 MapBlock *block = emergeBlock(p, false);
2199 // 3) create a blank one
2202 block = createBlock(p);
2205 Block gets sunlight if this is true.
2207 Refer to the map generator heuristics.
2209 bool ug = m_emerge->isBlockUnderground(p);
2210 block->setIsUnderground(ug);
2213 // Lighting will not be valid after make_chunk is called
2214 block->setLightingExpired(true);
2215 // Lighting will be calculated
2216 //block->setLightingExpired(false);
2222 Now we have a big empty area.
2224 Make a ManualMapVoxelManipulator that contains this and the
2228 // The area that contains this block and it's neighbors
2229 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2230 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2232 data->vmanip = new ManualMapVoxelManipulator(this);
2233 //data->vmanip->setMap(this);
2237 //TimeTaker timer("initBlockMake() initialEmerge");
2238 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2241 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2242 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2243 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2244 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2245 core::map<v3s16, u8>::Node *n;
2246 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2249 u8 flags = n->getValue();
2250 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2256 // Data is ready now.
2260 void ServerMap::finishBlockMake(BlockMakeData *data,
2261 std::map<v3s16, MapBlock*> &changed_blocks)
2263 v3s16 blockpos_min = data->blockpos_min;
2264 v3s16 blockpos_max = data->blockpos_max;
2265 v3s16 blockpos_requested = data->blockpos_requested;
2266 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2267 <<blockpos_requested.Y<<","
2268 <<blockpos_requested.Z<<")"<<std::endl;*/
2270 v3s16 extra_borders(1,1,1);
2272 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2274 /*infostream<<"Resulting vmanip:"<<std::endl;
2275 data->vmanip.print(infostream);*/
2277 // Make sure affected blocks are loaded
2278 for(s16 x=blockpos_min.X-extra_borders.X;
2279 x<=blockpos_max.X+extra_borders.X; x++)
2280 for(s16 z=blockpos_min.Z-extra_borders.Z;
2281 z<=blockpos_max.Z+extra_borders.Z; z++)
2282 for(s16 y=blockpos_min.Y-extra_borders.Y;
2283 y<=blockpos_max.Y+extra_borders.Y; y++)
2286 // Load from disk if not already in memory
2287 emergeBlock(p, false);
2291 Blit generated stuff to map
2292 NOTE: blitBackAll adds nearly everything to changed_blocks
2296 //TimeTaker timer("finishBlockMake() blitBackAll");
2297 data->vmanip->blitBackAll(&changed_blocks);
2300 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2303 Copy transforming liquid information
2305 while(data->transforming_liquid.size() > 0)
2307 v3s16 p = data->transforming_liquid.pop_front();
2308 m_transforming_liquid.push_back(p);
2312 Do stuff in central blocks
2320 TimeTaker t("finishBlockMake lighting update");
2322 core::map<v3s16, MapBlock*> lighting_update_blocks;
2325 for(s16 x=blockpos_min.X-extra_borders.X;
2326 x<=blockpos_max.X+extra_borders.X; x++)
2327 for(s16 z=blockpos_min.Z-extra_borders.Z;
2328 z<=blockpos_max.Z+extra_borders.Z; z++)
2329 for(s16 y=blockpos_min.Y-extra_borders.Y;
2330 y<=blockpos_max.Y+extra_borders.Y; y++)
2333 MapBlock *block = getBlockNoCreateNoEx(p);
2335 lighting_update_blocks.insert(block->getPos(), block);
2338 updateLighting(lighting_update_blocks, changed_blocks);
2342 Set lighting to non-expired state in all of them.
2343 This is cheating, but it is not fast enough if all of them
2344 would actually be updated.
2346 for(s16 x=blockpos_min.X-extra_borders.X;
2347 x<=blockpos_max.X+extra_borders.X; x++)
2348 for(s16 z=blockpos_min.Z-extra_borders.Z;
2349 z<=blockpos_max.Z+extra_borders.Z; z++)
2350 for(s16 y=blockpos_min.Y-extra_borders.Y;
2351 y<=blockpos_max.Y+extra_borders.Y; y++)
2354 MapBlock * block = getBlockNoCreateNoEx(p);
2356 block->setLightingExpired(false);
2360 if(enable_mapgen_debug_info == false)
2361 t.stop(true); // Hide output
2366 Go through changed blocks
2368 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2369 i != changed_blocks.end(); ++i)
2371 MapBlock *block = i->second;
2375 Update day/night difference cache of the MapBlocks
2377 block->expireDayNightDiff();
2379 Set block as modified
2381 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2382 "finishBlockMake expireDayNightDiff");
2386 Set central blocks as generated
2388 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2389 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2390 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2393 MapBlock *block = getBlockNoCreateNoEx(p);
2396 block->setGenerated(true);
2400 Save changed parts of map
2401 NOTE: Will be saved later.
2403 //save(MOD_STATE_WRITE_AT_UNLOAD);
2405 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2406 <<","<<blockpos_requested.Y<<","
2407 <<blockpos_requested.Z<<")"<<std::endl;*/
2411 if(enable_mapgen_debug_info)
2414 Analyze resulting blocks
2416 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2417 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2418 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2419 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2420 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2421 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2423 v3s16 p = v3s16(x,y,z);
2424 MapBlock *block = getBlockNoCreateNoEx(p);
2426 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2427 infostream<<"Generated "<<spos<<": "
2428 <<analyze_block(block)<<std::endl;
2433 getBlockNoCreateNoEx(blockpos_requested);
2436 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2438 DSTACKF("%s: p2d=(%d,%d)",
2443 Check if it exists already in memory
2445 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2450 Try to load it from disk (with blocks)
2452 //if(loadSectorFull(p2d) == true)
2455 Try to load metadata from disk
2458 if(loadSectorMeta(p2d) == true)
2460 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2463 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2464 throw InvalidPositionException("");
2470 Do not create over-limit
2472 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2473 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2474 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2475 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2476 throw InvalidPositionException("createSector(): pos. over limit");
2479 Generate blank sector
2482 sector = new ServerMapSector(this, p2d, m_gamedef);
2484 // Sector position on map in nodes
2485 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2490 m_sectors[p2d] = sector;
2497 This is a quick-hand function for calling makeBlock().
2499 MapBlock * ServerMap::generateBlock(
2501 std::map<v3s16, MapBlock*> &modified_blocks
2504 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2506 /*infostream<<"generateBlock(): "
2507 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2510 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2512 TimeTaker timer("generateBlock");
2514 //MapBlock *block = original_dummy;
2516 v2s16 p2d(p.X, p.Z);
2517 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2520 Do not generate over-limit
2522 if(blockpos_over_limit(p))
2524 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2525 throw InvalidPositionException("generateBlock(): pos. over limit");
2529 Create block make data
2532 initBlockMake(&data, p);
2538 TimeTaker t("mapgen::make_block()");
2539 mapgen->makeChunk(&data);
2540 //mapgen::make_block(&data);
2542 if(enable_mapgen_debug_info == false)
2543 t.stop(true); // Hide output
2547 Blit data back on map, update lighting, add mobs and whatever this does
2549 finishBlockMake(&data, modified_blocks);
2554 MapBlock *block = getBlockNoCreateNoEx(p);
2562 bool erroneus_content = false;
2563 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2564 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2565 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2568 MapNode n = block->getNode(p);
2569 if(n.getContent() == CONTENT_IGNORE)
2571 infostream<<"CONTENT_IGNORE at "
2572 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2574 erroneus_content = true;
2578 if(erroneus_content)
2587 Generate a completely empty block
2591 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2592 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2594 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2597 n.setContent(CONTENT_AIR);
2598 block->setNode(v3s16(x0,y0,z0), n);
2604 if(enable_mapgen_debug_info == false)
2605 timer.stop(true); // Hide output
2611 MapBlock * ServerMap::createBlock(v3s16 p)
2613 DSTACKF("%s: p=(%d,%d,%d)",
2614 __FUNCTION_NAME, p.X, p.Y, p.Z);
2617 Do not create over-limit
2619 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2620 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2621 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2622 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2623 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2624 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2625 throw InvalidPositionException("createBlock(): pos. over limit");
2627 v2s16 p2d(p.X, p.Z);
2630 This will create or load a sector if not found in memory.
2631 If block exists on disk, it will be loaded.
2633 NOTE: On old save formats, this will be slow, as it generates
2634 lighting on blocks for them.
2636 ServerMapSector *sector;
2638 sector = (ServerMapSector*)createSector(p2d);
2639 assert(sector->getId() == MAPSECTOR_SERVER);
2641 catch(InvalidPositionException &e)
2643 infostream<<"createBlock: createSector() failed"<<std::endl;
2647 NOTE: This should not be done, or at least the exception
2648 should not be passed on as std::exception, because it
2649 won't be catched at all.
2651 /*catch(std::exception &e)
2653 infostream<<"createBlock: createSector() failed: "
2654 <<e.what()<<std::endl;
2659 Try to get a block from the sector
2662 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2665 if(block->isDummy())
2670 block = sector->createBlankBlock(block_y);
2675 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2677 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2679 p.X, p.Y, p.Z, create_blank);
2682 MapBlock *block = getBlockNoCreateNoEx(p);
2683 if(block && block->isDummy() == false)
2688 MapBlock *block = loadBlock(p);
2694 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2695 MapBlock *block = sector->createBlankBlock(p.Y);
2703 std::map<v3s16, MapBlock*> modified_blocks;
2704 MapBlock *block = generateBlock(p, modified_blocks);
2708 event.type = MEET_OTHER;
2711 // Copy modified_blocks to event
2712 for(std::map<v3s16, MapBlock*>::iterator
2713 i = modified_blocks.begin();
2714 i != modified_blocks.end(); ++i)
2716 event.modified_blocks.insert(i->first);
2720 dispatchEvent(&event);
2730 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2732 MapBlock *block = getBlockNoCreateNoEx(p3d);
2734 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2739 void ServerMap::prepareBlock(MapBlock *block) {
2742 // N.B. This requires no synchronization, since data will not be modified unless
2743 // the VoxelManipulator being updated belongs to the same thread.
2744 void ServerMap::updateVManip(v3s16 pos)
2746 Mapgen *mg = m_emerge->getCurrentMapgen();
2750 ManualMapVoxelManipulator *vm = mg->vm;
2754 if (!vm->m_area.contains(pos))
2757 s32 idx = vm->m_area.index(pos);
2758 vm->m_data[idx] = getNodeNoEx(pos);
2759 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2761 vm->m_is_dirty = true;
2764 s16 ServerMap::findGroundLevel(v2s16 p2d)
2768 Uh, just do something random...
2770 // Find existing map from top to down
2773 v3s16 p(p2d.X, max, p2d.Y);
2774 for(; p.Y>min; p.Y--)
2776 MapNode n = getNodeNoEx(p);
2777 if(n.getContent() != CONTENT_IGNORE)
2782 // If this node is not air, go to plan b
2783 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2785 // Search existing walkable and return it
2786 for(; p.Y>min; p.Y--)
2788 MapNode n = getNodeNoEx(p);
2789 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2798 Determine from map generator noise functions
2801 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2804 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2805 //return (s16)level;
2808 bool ServerMap::loadFromFolders() {
2809 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2814 void ServerMap::createDirs(std::string path)
2816 if(fs::CreateAllDirs(path) == false)
2818 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2819 <<"\""<<path<<"\""<<std::endl;
2820 throw BaseException("ServerMap failed to create directory");
2824 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2830 snprintf(cc, 9, "%.4x%.4x",
2831 (unsigned int)pos.X&0xffff,
2832 (unsigned int)pos.Y&0xffff);
2834 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2836 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2837 (unsigned int)pos.X&0xfff,
2838 (unsigned int)pos.Y&0xfff);
2840 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2847 v2s16 ServerMap::getSectorPos(std::string dirname)
2849 unsigned int x = 0, y = 0;
2851 std::string component;
2852 fs::RemoveLastPathComponent(dirname, &component, 1);
2853 if(component.size() == 8)
2856 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2858 else if(component.size() == 3)
2861 fs::RemoveLastPathComponent(dirname, &component, 2);
2862 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2863 // Sign-extend the 12 bit values up to 16 bits...
2864 if(x&0x800) x|=0xF000;
2865 if(y&0x800) y|=0xF000;
2872 v2s16 pos((s16)x, (s16)y);
2876 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2878 v2s16 p2d = getSectorPos(sectordir);
2880 if(blockfile.size() != 4){
2881 throw InvalidFilenameException("Invalid block filename");
2884 int r = sscanf(blockfile.c_str(), "%4x", &y);
2886 throw InvalidFilenameException("Invalid block filename");
2887 return v3s16(p2d.X, y, p2d.Y);
2890 std::string ServerMap::getBlockFilename(v3s16 p)
2893 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2897 void ServerMap::save(ModifiedState save_level)
2899 DSTACK(__FUNCTION_NAME);
2900 if(m_map_saving_enabled == false)
2902 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2906 if(save_level == MOD_STATE_CLEAN)
2907 infostream<<"ServerMap: Saving whole map, this can take time."
2910 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2915 // Profile modified reasons
2916 Profiler modprofiler;
2918 u32 sector_meta_count = 0;
2919 u32 block_count = 0;
2920 u32 block_count_all = 0; // Number of blocks in memory
2922 // Don't do anything with sqlite unless something is really saved
2923 bool save_started = false;
2925 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2926 i != m_sectors.end(); ++i)
2928 ServerMapSector *sector = (ServerMapSector*)i->second;
2929 assert(sector->getId() == MAPSECTOR_SERVER);
2931 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2933 saveSectorMeta(sector);
2934 sector_meta_count++;
2936 std::list<MapBlock*> blocks;
2937 sector->getBlocks(blocks);
2939 for(std::list<MapBlock*>::iterator j = blocks.begin();
2940 j != blocks.end(); ++j)
2942 MapBlock *block = *j;
2946 if(block->getModified() >= (u32)save_level)
2951 save_started = true;
2954 modprofiler.add(block->getModifiedReason(), 1);
2959 /*infostream<<"ServerMap: Written block ("
2960 <<block->getPos().X<<","
2961 <<block->getPos().Y<<","
2962 <<block->getPos().Z<<")"
2971 Only print if something happened or saved whole map
2973 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2974 || block_count != 0)
2976 infostream<<"ServerMap: Written: "
2977 <<sector_meta_count<<" sector metadata files, "
2978 <<block_count<<" block files"
2979 <<", "<<block_count_all<<" blocks in memory."
2981 PrintInfo(infostream); // ServerMap/ClientMap:
2982 infostream<<"Blocks modified by: "<<std::endl;
2983 modprofiler.print(infostream);
2987 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
2989 if(loadFromFolders()){
2990 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2991 <<"all blocks that are stored in flat files"<<std::endl;
2993 dbase->listAllLoadableBlocks(dst);
2996 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
2998 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2999 si != m_sectors.end(); ++si)
3001 MapSector *sector = si->second;
3003 std::list<MapBlock*> blocks;
3004 sector->getBlocks(blocks);
3006 for(std::list<MapBlock*>::iterator i = blocks.begin();
3007 i != blocks.end(); ++i)
3009 MapBlock *block = (*i);
3010 v3s16 p = block->getPos();
3016 void ServerMap::saveMapMeta()
3018 DSTACK(__FUNCTION_NAME);
3020 /*infostream<<"ServerMap::saveMapMeta(): "
3024 createDirs(m_savedir);
3026 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3027 std::ostringstream ss(std::ios_base::binary);
3031 m_emerge->saveParamsToSettings(¶ms);
3032 params.writeLines(ss);
3034 ss<<"[end_of_params]\n";
3036 if(!fs::safeWriteToFile(fullpath, ss.str()))
3038 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3039 <<"could not write "<<fullpath<<std::endl;
3040 throw FileNotGoodException("Cannot save chunk metadata");
3043 m_map_metadata_changed = false;
3046 void ServerMap::loadMapMeta()
3048 DSTACK(__FUNCTION_NAME);
3050 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3051 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3053 errorstream << "ServerMap::loadMapMeta(): "
3054 << "could not open" << fullpath << std::endl;
3055 throw FileNotGoodException("Cannot open map metadata");
3060 if (!params.parseConfigLines(is, "[end_of_params]")) {
3061 throw SerializationError("ServerMap::loadMapMeta(): "
3062 "[end_of_params] not found!");
3065 m_emerge->loadParamsFromSettings(¶ms);
3067 verbosestream << "ServerMap::loadMapMeta(): seed="
3068 << m_emerge->params.seed << std::endl;
3071 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3073 DSTACK(__FUNCTION_NAME);
3074 // Format used for writing
3075 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3077 v2s16 pos = sector->getPos();
3078 std::string dir = getSectorDir(pos);
3081 std::string fullpath = dir + DIR_DELIM + "meta";
3082 std::ostringstream ss(std::ios_base::binary);
3084 sector->serialize(ss, version);
3086 if(!fs::safeWriteToFile(fullpath, ss.str()))
3087 throw FileNotGoodException("Cannot write sector metafile");
3089 sector->differs_from_disk = false;
3092 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3094 DSTACK(__FUNCTION_NAME);
3096 v2s16 p2d = getSectorPos(sectordir);
3098 ServerMapSector *sector = NULL;
3100 std::string fullpath = sectordir + DIR_DELIM + "meta";
3101 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3102 if(is.good() == false)
3104 // If the directory exists anyway, it probably is in some old
3105 // format. Just go ahead and create the sector.
3106 if(fs::PathExists(sectordir))
3108 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3109 <<fullpath<<" doesn't exist but directory does."
3110 <<" Continuing with a sector with no metadata."
3112 sector = new ServerMapSector(this, p2d, m_gamedef);
3113 m_sectors[p2d] = sector;
3117 throw FileNotGoodException("Cannot open sector metafile");
3122 sector = ServerMapSector::deSerialize
3123 (is, this, p2d, m_sectors, m_gamedef);
3125 saveSectorMeta(sector);
3128 sector->differs_from_disk = false;
3133 bool ServerMap::loadSectorMeta(v2s16 p2d)
3135 DSTACK(__FUNCTION_NAME);
3137 MapSector *sector = NULL;
3139 // The directory layout we're going to load from.
3140 // 1 - original sectors/xxxxzzzz/
3141 // 2 - new sectors2/xxx/zzz/
3142 // If we load from anything but the latest structure, we will
3143 // immediately save to the new one, and remove the old.
3145 std::string sectordir1 = getSectorDir(p2d, 1);
3146 std::string sectordir;
3147 if(fs::PathExists(sectordir1))
3149 sectordir = sectordir1;
3154 sectordir = getSectorDir(p2d, 2);
3158 sector = loadSectorMeta(sectordir, loadlayout != 2);
3160 catch(InvalidFilenameException &e)
3164 catch(FileNotGoodException &e)
3168 catch(std::exception &e)
3177 bool ServerMap::loadSectorFull(v2s16 p2d)
3179 DSTACK(__FUNCTION_NAME);
3181 MapSector *sector = NULL;
3183 // The directory layout we're going to load from.
3184 // 1 - original sectors/xxxxzzzz/
3185 // 2 - new sectors2/xxx/zzz/
3186 // If we load from anything but the latest structure, we will
3187 // immediately save to the new one, and remove the old.
3189 std::string sectordir1 = getSectorDir(p2d, 1);
3190 std::string sectordir;
3191 if(fs::PathExists(sectordir1))
3193 sectordir = sectordir1;
3198 sectordir = getSectorDir(p2d, 2);
3202 sector = loadSectorMeta(sectordir, loadlayout != 2);
3204 catch(InvalidFilenameException &e)
3208 catch(FileNotGoodException &e)
3212 catch(std::exception &e)
3220 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3222 std::vector<fs::DirListNode>::iterator i2;
3223 for(i2=list2.begin(); i2!=list2.end(); i2++)
3229 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3231 catch(InvalidFilenameException &e)
3233 // This catches unknown crap in directory
3239 infostream<<"Sector converted to new layout - deleting "<<
3240 sectordir1<<std::endl;
3241 fs::RecursiveDelete(sectordir1);
3248 void ServerMap::beginSave()
3253 void ServerMap::endSave()
3258 bool ServerMap::saveBlock(MapBlock *block)
3260 return saveBlock(block, dbase);
3263 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3265 v3s16 p3d = block->getPos();
3267 // Dummy blocks are not written
3268 if (block->isDummy()) {
3269 errorstream << "WARNING: saveBlock: Not writing dummy block "
3270 << PP(p3d) << std::endl;
3274 // Format used for writing
3275 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3278 [0] u8 serialization version
3281 std::ostringstream o(std::ios_base::binary);
3282 o.write((char*) &version, 1);
3283 block->serialize(o, version, true);
3285 std::string data = o.str();
3286 bool ret = db->saveBlock(p3d, data);
3288 // We just wrote it to the disk so clear modified flag
3289 block->resetModified();
3294 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3295 MapSector *sector, bool save_after_load)
3297 DSTACK(__FUNCTION_NAME);
3299 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3302 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3303 if(is.good() == false)
3304 throw FileNotGoodException("Cannot open block file");
3306 v3s16 p3d = getBlockPos(sectordir, blockfile);
3307 v2s16 p2d(p3d.X, p3d.Z);
3309 assert(sector->getPos() == p2d);
3311 u8 version = SER_FMT_VER_INVALID;
3312 is.read((char*)&version, 1);
3315 throw SerializationError("ServerMap::loadBlock(): Failed"
3316 " to read MapBlock version");
3318 /*u32 block_size = MapBlock::serializedLength(version);
3319 SharedBuffer<u8> data(block_size);
3320 is.read((char*)*data, block_size);*/
3322 // This will always return a sector because we're the server
3323 //MapSector *sector = emergeSector(p2d);
3325 MapBlock *block = NULL;
3326 bool created_new = false;
3327 block = sector->getBlockNoCreateNoEx(p3d.Y);
3330 block = sector->createBlankBlockNoInsert(p3d.Y);
3335 block->deSerialize(is, version, true);
3337 // If it's a new block, insert it to the map
3339 sector->insertBlock(block);
3342 Save blocks loaded in old format in new format
3345 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3349 // Should be in database now, so delete the old file
3350 fs::RecursiveDelete(fullpath);
3353 // We just loaded it from the disk, so it's up-to-date.
3354 block->resetModified();
3357 catch(SerializationError &e)
3359 infostream<<"WARNING: Invalid block data on disk "
3360 <<"fullpath="<<fullpath
3361 <<" (SerializationError). "
3362 <<"what()="<<e.what()
3364 // Ignoring. A new one will be generated.
3367 // TODO: Backup file; name is in fullpath.
3371 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3373 DSTACK(__FUNCTION_NAME);
3376 std::istringstream is(*blob, std::ios_base::binary);
3378 u8 version = SER_FMT_VER_INVALID;
3379 is.read((char*)&version, 1);
3382 throw SerializationError("ServerMap::loadBlock(): Failed"
3383 " to read MapBlock version");
3385 /*u32 block_size = MapBlock::serializedLength(version);
3386 SharedBuffer<u8> data(block_size);
3387 is.read((char*)*data, block_size);*/
3389 // This will always return a sector because we're the server
3390 //MapSector *sector = emergeSector(p2d);
3392 MapBlock *block = NULL;
3393 bool created_new = false;
3394 block = sector->getBlockNoCreateNoEx(p3d.Y);
3397 block = sector->createBlankBlockNoInsert(p3d.Y);
3402 block->deSerialize(is, version, true);
3404 // If it's a new block, insert it to the map
3406 sector->insertBlock(block);
3409 Save blocks loaded in old format in new format
3412 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3413 // Only save if asked to; no need to update version
3417 // We just loaded it from, so it's up-to-date.
3418 block->resetModified();
3421 catch(SerializationError &e)
3423 errorstream<<"Invalid block data in database"
3424 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3425 <<" (SerializationError): "<<e.what()<<std::endl;
3427 // TODO: Block should be marked as invalid in memory so that it is
3428 // not touched but the game can run
3430 if(g_settings->getBool("ignore_world_load_errors")){
3431 errorstream<<"Ignoring block load error. Duck and cover! "
3432 <<"(ignore_world_load_errors)"<<std::endl;
3434 throw SerializationError("Invalid block data in database");
3440 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3442 DSTACK(__FUNCTION_NAME);
3444 v2s16 p2d(blockpos.X, blockpos.Z);
3448 ret = dbase->loadBlock(blockpos);
3450 loadBlock(&ret, blockpos, createSector(p2d), false);
3451 return getBlockNoCreateNoEx(blockpos);
3453 // Not found in database, try the files
3455 // The directory layout we're going to load from.
3456 // 1 - original sectors/xxxxzzzz/
3457 // 2 - new sectors2/xxx/zzz/
3458 // If we load from anything but the latest structure, we will
3459 // immediately save to the new one, and remove the old.
3461 std::string sectordir1 = getSectorDir(p2d, 1);
3462 std::string sectordir;
3463 if(fs::PathExists(sectordir1))
3465 sectordir = sectordir1;
3470 sectordir = getSectorDir(p2d, 2);
3474 Make sure sector is loaded
3476 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3480 sector = loadSectorMeta(sectordir, loadlayout != 2);
3482 catch(InvalidFilenameException &e)
3486 catch(FileNotGoodException &e)
3490 catch(std::exception &e)
3497 Make sure file exists
3500 std::string blockfilename = getBlockFilename(blockpos);
3501 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3505 Load block and save it to the database
3507 loadBlock(sectordir, blockfilename, sector, true);
3508 return getBlockNoCreateNoEx(blockpos);
3511 void ServerMap::PrintInfo(std::ostream &out)
3516 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3519 m_create_area(false),
3524 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3528 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
3529 v3s16 blockpos_max, bool load_if_inexistent)
3531 TimeTaker timer1("initialEmerge", &emerge_time);
3533 // Units of these are MapBlocks
3534 v3s16 p_min = blockpos_min;
3535 v3s16 p_max = blockpos_max;
3537 VoxelArea block_area_nodes
3538 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3540 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3543 infostream<<"initialEmerge: area: ";
3544 block_area_nodes.print(infostream);
3545 infostream<<" ("<<size_MB<<"MB)";
3546 infostream<<std::endl;
3549 addArea(block_area_nodes);
3551 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3552 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3553 for(s32 x=p_min.X; x<=p_max.X; x++)
3558 std::map<v3s16, u8>::iterator n;
3559 n = m_loaded_blocks.find(p);
3560 if(n != m_loaded_blocks.end())
3563 bool block_data_inexistent = false;
3566 TimeTaker timer1("emerge load", &emerge_load_time);
3568 block = m_map->getBlockNoCreate(p);
3569 if(block->isDummy())
3570 block_data_inexistent = true;
3572 block->copyTo(*this);
3574 catch(InvalidPositionException &e)
3576 block_data_inexistent = true;
3579 if(block_data_inexistent)
3582 if (load_if_inexistent) {
3583 ServerMap *svrmap = (ServerMap *)m_map;
3584 block = svrmap->emergeBlock(p, false);
3586 block = svrmap->createBlock(p);
3588 block->copyTo(*this);
3590 flags |= VMANIP_BLOCK_DATA_INEXIST;
3593 Mark area inexistent
3595 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3596 // Fill with VOXELFLAG_NO_DATA
3597 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3598 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3600 s32 i = m_area.index(a.MinEdge.X,y,z);
3601 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3605 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3607 // Mark that block was loaded as blank
3608 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3611 m_loaded_blocks[p] = flags;
3617 void ManualMapVoxelManipulator::blitBackAll(
3618 std::map<v3s16, MapBlock*> *modified_blocks,
3619 bool overwrite_generated)
3621 if(m_area.getExtent() == v3s16(0,0,0))
3625 Copy data of all blocks
3627 for(std::map<v3s16, u8>::iterator
3628 i = m_loaded_blocks.begin();
3629 i != m_loaded_blocks.end(); ++i)
3632 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3633 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3634 if ((existed == false) || (block == NULL) ||
3635 (overwrite_generated == false && block->isGenerated() == true))
3638 block->copyFrom(*this);
3641 (*modified_blocks)[p] = block;