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"
26 #include "serialization.h"
27 #include "nodemetadata.h"
33 #include "util/directiontables.h"
34 #include "util/mathconstants.h"
35 #include "rollback_interface.h"
36 #include "environment.h"
38 #include "mapgen_v6.h"
43 #include "database-dummy.h"
44 #include "database-sqlite3.h"
47 #include "database-leveldb.h"
50 #include "database-redis.h"
53 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
60 Map::Map(std::ostream &dout, IGameDef *gamedef):
64 m_transforming_liquid_loop_count_multiplier(1.0f),
65 m_unprocessed_count(0),
66 m_inc_trending_up_start_time(0),
67 m_queue_size_timer_started(false)
76 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
77 i != m_sectors.end(); ++i)
83 void Map::addEventReceiver(MapEventReceiver *event_receiver)
85 m_event_receivers.insert(event_receiver);
88 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
90 m_event_receivers.erase(event_receiver);
93 void Map::dispatchEvent(MapEditEvent *event)
95 for(std::set<MapEventReceiver*>::iterator
96 i = m_event_receivers.begin();
97 i != m_event_receivers.end(); ++i)
99 (*i)->onMapEditEvent(event);
103 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
105 if(m_sector_cache != NULL && p == m_sector_cache_p){
106 MapSector * sector = m_sector_cache;
110 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
112 if(n == m_sectors.end())
115 MapSector *sector = n->second;
117 // Cache the last result
118 m_sector_cache_p = p;
119 m_sector_cache = sector;
124 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
126 return getSectorNoGenerateNoExNoLock(p);
129 MapSector * Map::getSectorNoGenerate(v2s16 p)
131 MapSector *sector = getSectorNoGenerateNoEx(p);
133 throw InvalidPositionException();
138 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
140 v2s16 p2d(p3d.X, p3d.Z);
141 MapSector * sector = getSectorNoGenerateNoEx(p2d);
144 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
148 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
150 MapBlock *block = getBlockNoCreateNoEx(p3d);
152 throw InvalidPositionException();
156 bool Map::isNodeUnderground(v3s16 p)
158 v3s16 blockpos = getNodeBlockPos(p);
160 MapBlock * block = getBlockNoCreate(blockpos);
161 return block->getIsUnderground();
163 catch(InvalidPositionException &e)
169 bool Map::isValidPosition(v3s16 p)
171 v3s16 blockpos = getNodeBlockPos(p);
172 MapBlock *block = getBlockNoCreate(blockpos);
173 return (block != NULL);
176 // Returns a CONTENT_IGNORE node if not found
177 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
179 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock *block = getBlockNoCreateNoEx(blockpos);
182 if (is_valid_position != NULL)
183 *is_valid_position = false;
184 return MapNode(CONTENT_IGNORE);
187 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
190 if (is_valid_position != NULL)
191 *is_valid_position = is_valid_p;
197 // throws InvalidPositionException if not found
198 // TODO: Now this is deprecated, getNodeNoEx should be renamed
199 MapNode Map::getNode(v3s16 p)
201 v3s16 blockpos = getNodeBlockPos(p);
202 MapBlock *block = getBlockNoCreateNoEx(blockpos);
204 throw InvalidPositionException();
205 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 bool is_valid_position;
207 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
208 if (!is_valid_position)
209 throw InvalidPositionException();
214 // throws InvalidPositionException if not found
215 void Map::setNode(v3s16 p, MapNode & n)
217 v3s16 blockpos = getNodeBlockPos(p);
218 MapBlock *block = getBlockNoCreate(blockpos);
219 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
220 // Never allow placing CONTENT_IGNORE, it fucks up stuff
221 if(n.getContent() == CONTENT_IGNORE){
223 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
224 <<" while trying to replace \""
225 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
226 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
227 debug_stacks_print_to(infostream);
230 block->setNodeNoCheck(relpos, n);
235 Goes recursively through the neighbours of the node.
237 Alters only transparent nodes.
239 If the lighting of the neighbour is lower than the lighting of
240 the node was (before changing it to 0 at the step before), the
241 lighting of the neighbour is set to 0 and then the same stuff
242 repeats for the neighbour.
244 The ending nodes of the routine are stored in light_sources.
245 This is useful when a light is removed. In such case, this
246 routine can be called for the light node and then again for
247 light_sources to re-light the area without the removed light.
249 values of from_nodes are lighting values.
251 void Map::unspreadLight(enum LightBank bank,
252 std::map<v3s16, u8> & from_nodes,
253 std::set<v3s16> & light_sources,
254 std::map<v3s16, MapBlock*> & modified_blocks)
256 INodeDefManager *nodemgr = m_gamedef->ndef();
259 v3s16(0,0,1), // back
261 v3s16(1,0,0), // right
262 v3s16(0,0,-1), // front
263 v3s16(0,-1,0), // bottom
264 v3s16(-1,0,0), // left
267 if(from_nodes.empty())
270 u32 blockchangecount = 0;
272 std::map<v3s16, u8> unlighted_nodes;
275 Initialize block cache
278 MapBlock *block = NULL;
279 // Cache this a bit, too
280 bool block_checked_in_modified = false;
282 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
283 j != from_nodes.end(); ++j)
285 v3s16 pos = j->first;
286 v3s16 blockpos = getNodeBlockPos(pos);
288 // Only fetch a new block if the block position has changed
290 if(block == NULL || blockpos != blockpos_last){
291 block = getBlockNoCreate(blockpos);
292 blockpos_last = blockpos;
294 block_checked_in_modified = false;
298 catch(InvalidPositionException &e)
306 // Calculate relative position in block
307 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
309 // Get node straight from the block
310 //MapNode n = block->getNode(relpos);
312 u8 oldlight = j->second;
314 // Loop through 6 neighbors
315 for(u16 i=0; i<6; i++)
317 // Get the position of the neighbor node
318 v3s16 n2pos = pos + dirs[i];
320 // Get the block where the node is located
321 v3s16 blockpos, relpos;
322 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
324 // Only fetch a new block if the block position has changed
326 if(block == NULL || blockpos != blockpos_last){
327 block = getBlockNoCreate(blockpos);
328 blockpos_last = blockpos;
330 block_checked_in_modified = false;
334 catch(InvalidPositionException &e) {
338 // Get node straight from the block
339 bool is_valid_position;
340 MapNode n2 = block->getNode(relpos, &is_valid_position);
341 if (!is_valid_position)
344 bool changed = false;
346 //TODO: Optimize output by optimizing light_sources?
349 If the neighbor is dimmer than what was specified
350 as oldlight (the light of the previous node)
352 if(n2.getLight(bank, nodemgr) < oldlight)
355 And the neighbor is transparent and it has some light
357 if(nodemgr->get(n2).light_propagates
358 && n2.getLight(bank, nodemgr) != 0)
361 Set light to 0 and add to queue
364 u8 current_light = n2.getLight(bank, nodemgr);
365 n2.setLight(bank, 0, nodemgr);
366 block->setNode(relpos, n2);
368 unlighted_nodes[n2pos] = current_light;
372 Remove from light_sources if it is there
373 NOTE: This doesn't happen nearly at all
375 /*if(light_sources.find(n2pos))
377 infostream<<"Removed from light_sources"<<std::endl;
378 light_sources.remove(n2pos);
383 if(light_sources.find(n2pos) != NULL)
384 light_sources.remove(n2pos);*/
387 light_sources.insert(n2pos);
390 // Add to modified_blocks
391 if(changed == true && block_checked_in_modified == false)
393 // If the block is not found in modified_blocks, add.
394 if(modified_blocks.find(blockpos) == modified_blocks.end())
396 modified_blocks[blockpos] = block;
398 block_checked_in_modified = true;
403 /*infostream<<"unspreadLight(): Changed block "
404 <<blockchangecount<<" times"
405 <<" for "<<from_nodes.size()<<" nodes"
408 if(!unlighted_nodes.empty())
409 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
413 A single-node wrapper of the above
415 void Map::unLightNeighbors(enum LightBank bank,
416 v3s16 pos, u8 lightwas,
417 std::set<v3s16> & light_sources,
418 std::map<v3s16, MapBlock*> & modified_blocks)
420 std::map<v3s16, u8> from_nodes;
421 from_nodes[pos] = lightwas;
423 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
427 Lights neighbors of from_nodes, collects all them and then
430 void Map::spreadLight(enum LightBank bank,
431 std::set<v3s16> & from_nodes,
432 std::map<v3s16, MapBlock*> & modified_blocks)
434 INodeDefManager *nodemgr = m_gamedef->ndef();
436 const v3s16 dirs[6] = {
437 v3s16(0,0,1), // back
439 v3s16(1,0,0), // right
440 v3s16(0,0,-1), // front
441 v3s16(0,-1,0), // bottom
442 v3s16(-1,0,0), // left
445 if(from_nodes.empty())
448 u32 blockchangecount = 0;
450 std::set<v3s16> lighted_nodes;
453 Initialize block cache
456 MapBlock *block = NULL;
457 // Cache this a bit, too
458 bool block_checked_in_modified = false;
460 for(std::set<v3s16>::iterator j = from_nodes.begin();
461 j != from_nodes.end(); ++j)
464 v3s16 blockpos, relpos;
466 getNodeBlockPosWithOffset(pos, blockpos, relpos);
468 // Only fetch a new block if the block position has changed
470 if(block == NULL || blockpos != blockpos_last){
471 block = getBlockNoCreate(blockpos);
472 blockpos_last = blockpos;
474 block_checked_in_modified = false;
478 catch(InvalidPositionException &e) {
485 // Get node straight from the block
486 bool is_valid_position;
487 MapNode n = block->getNode(relpos, &is_valid_position);
489 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
490 u8 newlight = diminish_light(oldlight);
492 // Loop through 6 neighbors
493 for(u16 i=0; i<6; i++){
494 // Get the position of the neighbor node
495 v3s16 n2pos = pos + dirs[i];
497 // Get the block where the node is located
498 v3s16 blockpos, relpos;
499 getNodeBlockPosWithOffset(n2pos, blockpos, relpos);
501 // Only fetch a new block if the block position has changed
503 if(block == NULL || blockpos != blockpos_last){
504 block = getBlockNoCreate(blockpos);
505 blockpos_last = blockpos;
507 block_checked_in_modified = false;
511 catch(InvalidPositionException &e) {
515 // Get node straight from the block
516 MapNode n2 = block->getNode(relpos, &is_valid_position);
517 if (!is_valid_position)
520 bool changed = false;
522 If the neighbor is brighter than the current node,
523 add to list (it will light up this node on its turn)
525 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
527 lighted_nodes.insert(n2pos);
531 If the neighbor is dimmer than how much light this node
532 would spread on it, add to list
534 if(n2.getLight(bank, nodemgr) < newlight)
536 if(nodemgr->get(n2).light_propagates)
538 n2.setLight(bank, newlight, nodemgr);
539 block->setNode(relpos, n2);
540 lighted_nodes.insert(n2pos);
545 // Add to modified_blocks
546 if(changed == true && block_checked_in_modified == false)
548 // If the block is not found in modified_blocks, add.
549 if(modified_blocks.find(blockpos) == modified_blocks.end())
551 modified_blocks[blockpos] = block;
553 block_checked_in_modified = true;
558 /*infostream<<"spreadLight(): Changed block "
559 <<blockchangecount<<" times"
560 <<" for "<<from_nodes.size()<<" nodes"
563 if(!lighted_nodes.empty())
564 spreadLight(bank, lighted_nodes, modified_blocks);
568 A single-node source variation of the above.
570 void Map::lightNeighbors(enum LightBank bank,
572 std::map<v3s16, MapBlock*> & modified_blocks)
574 std::set<v3s16> from_nodes;
575 from_nodes.insert(pos);
576 spreadLight(bank, from_nodes, modified_blocks);
579 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
581 INodeDefManager *nodemgr = m_gamedef->ndef();
584 v3s16(0,0,1), // back
586 v3s16(1,0,0), // right
587 v3s16(0,0,-1), // front
588 v3s16(0,-1,0), // bottom
589 v3s16(-1,0,0), // left
592 u8 brightest_light = 0;
593 v3s16 brightest_pos(0,0,0);
594 bool found_something = false;
596 // Loop through 6 neighbors
597 for(u16 i=0; i<6; i++){
598 // Get the position of the neighbor node
599 v3s16 n2pos = p + dirs[i];
601 bool is_valid_position;
602 n2 = getNodeNoEx(n2pos, &is_valid_position);
603 if (!is_valid_position)
606 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
607 brightest_light = n2.getLight(bank, nodemgr);
608 brightest_pos = n2pos;
609 found_something = true;
613 if(found_something == false)
614 throw InvalidPositionException();
616 return brightest_pos;
620 Propagates sunlight down from a node.
621 Starting point gets sunlight.
623 Returns the lowest y value of where the sunlight went.
625 Mud is turned into grass in where the sunlight stops.
627 s16 Map::propagateSunlight(v3s16 start,
628 std::map<v3s16, MapBlock*> & modified_blocks)
630 INodeDefManager *nodemgr = m_gamedef->ndef();
635 v3s16 pos(start.X, y, start.Z);
637 v3s16 blockpos = getNodeBlockPos(pos);
640 block = getBlockNoCreate(blockpos);
642 catch(InvalidPositionException &e)
647 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
648 bool is_valid_position;
649 MapNode n = block->getNode(relpos, &is_valid_position);
650 if (!is_valid_position)
653 if(nodemgr->get(n).sunlight_propagates)
655 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
656 block->setNode(relpos, n);
658 modified_blocks[blockpos] = block;
662 // Sunlight goes no further
669 void Map::updateLighting(enum LightBank bank,
670 std::map<v3s16, MapBlock*> & a_blocks,
671 std::map<v3s16, MapBlock*> & modified_blocks)
673 INodeDefManager *nodemgr = m_gamedef->ndef();
675 /*m_dout<<DTIME<<"Map::updateLighting(): "
676 <<a_blocks.size()<<" blocks."<<std::endl;*/
678 //TimeTaker timer("updateLighting");
682 //u32 count_was = modified_blocks.size();
684 //std::map<v3s16, MapBlock*> blocks_to_update;
686 std::set<v3s16> light_sources;
688 std::map<v3s16, u8> unlight_from;
690 int num_bottom_invalid = 0;
693 //TimeTaker t("first stuff");
695 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
696 i != a_blocks.end(); ++i)
698 MapBlock *block = i->second;
702 // Don't bother with dummy blocks.
706 v3s16 pos = block->getPos();
707 v3s16 posnodes = block->getPosRelative();
708 modified_blocks[pos] = block;
709 //blocks_to_update[pos] = block;
712 Clear all light from block
714 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
715 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
716 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
719 bool is_valid_position;
720 MapNode n = block->getNode(p, &is_valid_position);
721 if (!is_valid_position) {
722 /* This would happen when dealing with a
725 infostream<<"updateLighting(): InvalidPositionException"
729 u8 oldlight = n.getLight(bank, nodemgr);
730 n.setLight(bank, 0, nodemgr);
731 block->setNode(p, n);
733 // If node sources light, add to list
734 u8 source = nodemgr->get(n).light_source;
736 light_sources.insert(p + posnodes);
738 // Collect borders for unlighting
739 if((x==0 || x == MAP_BLOCKSIZE-1
740 || y==0 || y == MAP_BLOCKSIZE-1
741 || z==0 || z == MAP_BLOCKSIZE-1)
744 v3s16 p_map = p + posnodes;
745 unlight_from[p_map] = oldlight;
751 if(bank == LIGHTBANK_DAY)
753 bool bottom_valid = block->propagateSunlight(light_sources);
756 num_bottom_invalid++;
758 // If bottom is valid, we're done.
762 else if(bank == LIGHTBANK_NIGHT)
764 // For night lighting, sunlight is not propagated
769 assert("Invalid lighting bank" == NULL);
772 /*infostream<<"Bottom for sunlight-propagated block ("
773 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
776 // Bottom sunlight is not valid; get the block and loop to it
780 block = getBlockNoCreate(pos);
782 catch(InvalidPositionException &e)
784 FATAL_ERROR("Invalid position");
793 Enable this to disable proper lighting for speeding up map
794 generation for testing or whatever
797 //if(g_settings->get(""))
799 core::map<v3s16, MapBlock*>::Iterator i;
800 i = blocks_to_update.getIterator();
801 for(; i.atEnd() == false; i++)
803 MapBlock *block = i.getNode()->getValue();
804 v3s16 p = block->getPos();
805 block->setLightingExpired(false);
813 //TimeTaker timer("unspreadLight");
814 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
819 u32 diff = modified_blocks.size() - count_was;
820 count_was = modified_blocks.size();
821 infostream<<"unspreadLight modified "<<diff<<std::endl;
825 //TimeTaker timer("spreadLight");
826 spreadLight(bank, light_sources, modified_blocks);
831 u32 diff = modified_blocks.size() - count_was;
832 count_was = modified_blocks.size();
833 infostream<<"spreadLight modified "<<diff<<std::endl;
839 //MapVoxelManipulator vmanip(this);
841 // Make a manual voxel manipulator and load all the blocks
842 // that touch the requested blocks
843 ManualMapVoxelManipulator vmanip(this);
846 //TimeTaker timer("initialEmerge");
848 core::map<v3s16, MapBlock*>::Iterator i;
849 i = blocks_to_update.getIterator();
850 for(; i.atEnd() == false; i++)
852 MapBlock *block = i.getNode()->getValue();
853 v3s16 p = block->getPos();
855 // Add all surrounding blocks
856 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
859 Add all surrounding blocks that have up-to-date lighting
860 NOTE: This doesn't quite do the job (not everything
861 appropriate is lighted)
863 /*for(s16 z=-1; z<=1; z++)
864 for(s16 y=-1; y<=1; y++)
865 for(s16 x=-1; x<=1; x++)
867 v3s16 p2 = p + v3s16(x,y,z);
868 MapBlock *block = getBlockNoCreateNoEx(p2);
873 if(block->getLightingExpired())
875 vmanip.initialEmerge(p2, p2);
878 // Lighting of block will be updated completely
879 block->setLightingExpired(false);
884 //TimeTaker timer("unSpreadLight");
885 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
888 //TimeTaker timer("spreadLight");
889 vmanip.spreadLight(bank, light_sources, nodemgr);
892 //TimeTaker timer("blitBack");
893 vmanip.blitBack(modified_blocks);
895 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
900 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
903 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
904 std::map<v3s16, MapBlock*> & modified_blocks)
906 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
907 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
910 Update information about whether day and night light differ
912 for(std::map<v3s16, MapBlock*>::iterator
913 i = modified_blocks.begin();
914 i != modified_blocks.end(); ++i)
916 MapBlock *block = i->second;
917 block->expireDayNightDiff();
923 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
924 std::map<v3s16, MapBlock*> &modified_blocks,
925 bool remove_metadata)
927 INodeDefManager *ndef = m_gamedef->ndef();
930 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
931 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
934 From this node to nodes underneath:
935 If lighting is sunlight (1.0), unlight neighbours and
940 v3s16 toppos = p + v3s16(0,1,0);
941 //v3s16 bottompos = p + v3s16(0,-1,0);
943 bool node_under_sunlight = true;
944 std::set<v3s16> light_sources;
947 Collect old node for rollback
949 RollbackNode rollback_oldnode(this, p, m_gamedef);
952 If there is a node at top and it doesn't have sunlight,
953 there has not been any sunlight going down.
955 Otherwise there probably is.
958 bool is_valid_position;
959 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
961 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
962 node_under_sunlight = false;
965 Remove all light that has come out of this node
968 enum LightBank banks[] =
973 for(s32 i=0; i<2; i++)
975 enum LightBank bank = banks[i];
977 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
979 // Add the block of the added node to modified_blocks
980 v3s16 blockpos = getNodeBlockPos(p);
981 MapBlock * block = getBlockNoCreate(blockpos);
982 assert(block != NULL);
983 modified_blocks[blockpos] = block;
985 assert(isValidPosition(p));
987 // Unlight neighbours of node.
988 // This means setting light of all consequent dimmer nodes
990 // This also collects the nodes at the border which will spread
991 // light again into this.
992 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
994 n.setLight(bank, 0, ndef);
998 If node lets sunlight through and is under sunlight, it has
1001 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1003 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1007 Remove node metadata
1009 if (remove_metadata) {
1010 removeNodeMetadata(p);
1014 Set the node on the map
1020 If node is under sunlight and doesn't let sunlight through,
1021 take all sunlighted nodes under it and clear light from them
1022 and from where the light has been spread.
1023 TODO: This could be optimized by mass-unlighting instead
1026 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1030 //m_dout<<DTIME<<"y="<<y<<std::endl;
1031 v3s16 n2pos(p.X, y, p.Z);
1035 n2 = getNodeNoEx(n2pos, &is_valid_position);
1036 if (!is_valid_position)
1039 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1041 unLightNeighbors(LIGHTBANK_DAY,
1042 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1043 light_sources, modified_blocks);
1044 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1052 for(s32 i=0; i<2; i++)
1054 enum LightBank bank = banks[i];
1057 Spread light from all nodes that might be capable of doing so
1059 spreadLight(bank, light_sources, modified_blocks);
1063 Update information about whether day and night light differ
1065 for(std::map<v3s16, MapBlock*>::iterator
1066 i = modified_blocks.begin();
1067 i != modified_blocks.end(); ++i)
1069 i->second->expireDayNightDiff();
1075 if(m_gamedef->rollback())
1077 RollbackNode rollback_newnode(this, p, m_gamedef);
1078 RollbackAction action;
1079 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1080 m_gamedef->rollback()->reportAction(action);
1084 Add neighboring liquid nodes and the node itself if it is
1085 liquid (=water node was added) to transform queue.
1088 v3s16(0,0,0), // self
1089 v3s16(0,0,1), // back
1090 v3s16(0,1,0), // top
1091 v3s16(1,0,0), // right
1092 v3s16(0,0,-1), // front
1093 v3s16(0,-1,0), // bottom
1094 v3s16(-1,0,0), // left
1096 for(u16 i=0; i<7; i++)
1098 v3s16 p2 = p + dirs[i];
1100 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1101 if(is_valid_position
1102 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1104 m_transforming_liquid.push_back(p2);
1111 void Map::removeNodeAndUpdate(v3s16 p,
1112 std::map<v3s16, MapBlock*> &modified_blocks)
1114 INodeDefManager *ndef = m_gamedef->ndef();
1116 /*PrintInfo(m_dout);
1117 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1118 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1120 bool node_under_sunlight = true;
1122 v3s16 toppos = p + v3s16(0,1,0);
1124 // Node will be replaced with this
1125 content_t replace_material = CONTENT_AIR;
1128 Collect old node for rollback
1130 RollbackNode rollback_oldnode(this, p, m_gamedef);
1133 If there is a node at top and it doesn't have sunlight,
1134 there will be no sunlight going down.
1136 bool is_valid_position;
1137 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1139 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1140 node_under_sunlight = false;
1142 std::set<v3s16> light_sources;
1144 enum LightBank banks[] =
1149 for(s32 i=0; i<2; i++)
1151 enum LightBank bank = banks[i];
1154 Unlight neighbors (in case the node is a light source)
1156 unLightNeighbors(bank, p,
1157 getNodeNoEx(p).getLight(bank, ndef),
1158 light_sources, modified_blocks);
1162 Remove node metadata
1165 removeNodeMetadata(p);
1169 This also clears the lighting.
1172 MapNode n(replace_material);
1175 for(s32 i=0; i<2; i++)
1177 enum LightBank bank = banks[i];
1180 Recalculate lighting
1182 spreadLight(bank, light_sources, modified_blocks);
1185 // Add the block of the removed node to modified_blocks
1186 v3s16 blockpos = getNodeBlockPos(p);
1187 MapBlock * block = getBlockNoCreate(blockpos);
1188 assert(block != NULL);
1189 modified_blocks[blockpos] = block;
1192 If the removed node was under sunlight, propagate the
1193 sunlight down from it and then light all neighbors
1194 of the propagated blocks.
1196 if(node_under_sunlight)
1198 s16 ybottom = propagateSunlight(p, modified_blocks);
1199 /*m_dout<<DTIME<<"Node was under sunlight. "
1200 "Propagating sunlight";
1201 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1203 for(; y >= ybottom; y--)
1205 v3s16 p2(p.X, y, p.Z);
1206 /*m_dout<<DTIME<<"lighting neighbors of node ("
1207 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1209 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1214 // Set the lighting of this node to 0
1215 // TODO: Is this needed? Lighting is cleared up there already.
1216 MapNode n = getNodeNoEx(p, &is_valid_position);
1217 if (is_valid_position) {
1218 n.setLight(LIGHTBANK_DAY, 0, ndef);
1221 FATAL_ERROR("Invalid position");
1225 for(s32 i=0; i<2; i++)
1227 enum LightBank bank = banks[i];
1229 // Get the brightest neighbour node and propagate light from it
1230 v3s16 n2p = getBrightestNeighbour(bank, p);
1232 //MapNode n2 = getNode(n2p);
1233 lightNeighbors(bank, n2p, modified_blocks);
1235 catch(InvalidPositionException &e)
1241 Update information about whether day and night light differ
1243 for(std::map<v3s16, MapBlock*>::iterator
1244 i = modified_blocks.begin();
1245 i != modified_blocks.end(); ++i)
1247 i->second->expireDayNightDiff();
1253 if(m_gamedef->rollback())
1255 RollbackNode rollback_newnode(this, p, m_gamedef);
1256 RollbackAction action;
1257 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1258 m_gamedef->rollback()->reportAction(action);
1262 Add neighboring liquid nodes and this node to transform queue.
1263 (it's vital for the node itself to get updated last.)
1266 v3s16(0,0,1), // back
1267 v3s16(0,1,0), // top
1268 v3s16(1,0,0), // right
1269 v3s16(0,0,-1), // front
1270 v3s16(0,-1,0), // bottom
1271 v3s16(-1,0,0), // left
1272 v3s16(0,0,0), // self
1274 for(u16 i=0; i<7; i++)
1276 v3s16 p2 = p + dirs[i];
1278 bool is_position_valid;
1279 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1280 if (is_position_valid
1281 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1283 m_transforming_liquid.push_back(p2);
1288 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1291 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1295 bool succeeded = true;
1297 std::map<v3s16, MapBlock*> modified_blocks;
1298 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1300 // Copy modified_blocks to event
1301 for(std::map<v3s16, MapBlock*>::iterator
1302 i = modified_blocks.begin();
1303 i != modified_blocks.end(); ++i)
1305 event.modified_blocks.insert(i->first);
1308 catch(InvalidPositionException &e){
1312 dispatchEvent(&event);
1317 bool Map::removeNodeWithEvent(v3s16 p)
1320 event.type = MEET_REMOVENODE;
1323 bool succeeded = true;
1325 std::map<v3s16, MapBlock*> modified_blocks;
1326 removeNodeAndUpdate(p, modified_blocks);
1328 // Copy modified_blocks to event
1329 for(std::map<v3s16, MapBlock*>::iterator
1330 i = modified_blocks.begin();
1331 i != modified_blocks.end(); ++i)
1333 event.modified_blocks.insert(i->first);
1336 catch(InvalidPositionException &e){
1340 dispatchEvent(&event);
1345 bool Map::getDayNightDiff(v3s16 blockpos)
1348 v3s16 p = blockpos + v3s16(0,0,0);
1349 MapBlock *b = getBlockNoCreate(p);
1350 if(b->getDayNightDiff())
1353 catch(InvalidPositionException &e){}
1356 v3s16 p = blockpos + v3s16(-1,0,0);
1357 MapBlock *b = getBlockNoCreate(p);
1358 if(b->getDayNightDiff())
1361 catch(InvalidPositionException &e){}
1363 v3s16 p = blockpos + v3s16(0,-1,0);
1364 MapBlock *b = getBlockNoCreate(p);
1365 if(b->getDayNightDiff())
1368 catch(InvalidPositionException &e){}
1370 v3s16 p = blockpos + v3s16(0,0,-1);
1371 MapBlock *b = getBlockNoCreate(p);
1372 if(b->getDayNightDiff())
1375 catch(InvalidPositionException &e){}
1378 v3s16 p = blockpos + v3s16(1,0,0);
1379 MapBlock *b = getBlockNoCreate(p);
1380 if(b->getDayNightDiff())
1383 catch(InvalidPositionException &e){}
1385 v3s16 p = blockpos + v3s16(0,1,0);
1386 MapBlock *b = getBlockNoCreate(p);
1387 if(b->getDayNightDiff())
1390 catch(InvalidPositionException &e){}
1392 v3s16 p = blockpos + v3s16(0,0,1);
1393 MapBlock *b = getBlockNoCreate(p);
1394 if(b->getDayNightDiff())
1397 catch(InvalidPositionException &e){}
1403 Updates usage timers
1405 void Map::timerUpdate(float dtime, float unload_timeout,
1406 std::vector<v3s16> *unloaded_blocks)
1408 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1410 // Profile modified reasons
1411 Profiler modprofiler;
1413 std::vector<v2s16> sector_deletion_queue;
1414 u32 deleted_blocks_count = 0;
1415 u32 saved_blocks_count = 0;
1416 u32 block_count_all = 0;
1419 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1420 si != m_sectors.end(); ++si) {
1421 MapSector *sector = si->second;
1423 bool all_blocks_deleted = true;
1425 MapBlockVect blocks;
1426 sector->getBlocks(blocks);
1428 for(MapBlockVect::iterator i = blocks.begin();
1429 i != blocks.end(); ++i) {
1430 MapBlock *block = (*i);
1432 block->incrementUsageTimer(dtime);
1434 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout) {
1435 v3s16 p = block->getPos();
1438 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
1439 modprofiler.add(block->getModifiedReason(), 1);
1440 if (!saveBlock(block))
1442 saved_blocks_count++;
1445 // Delete from memory
1446 sector->deleteBlock(block);
1449 unloaded_blocks->push_back(p);
1451 deleted_blocks_count++;
1454 all_blocks_deleted = false;
1459 if(all_blocks_deleted) {
1460 sector_deletion_queue.push_back(si->first);
1465 // Finally delete the empty sectors
1466 deleteSectors(sector_deletion_queue);
1468 if(deleted_blocks_count != 0)
1470 PrintInfo(infostream); // ServerMap/ClientMap:
1471 infostream<<"Unloaded "<<deleted_blocks_count
1472 <<" blocks from memory";
1473 if(save_before_unloading)
1474 infostream<<", of which "<<saved_blocks_count<<" were written";
1475 infostream<<", "<<block_count_all<<" blocks in memory";
1476 infostream<<"."<<std::endl;
1477 if(saved_blocks_count != 0){
1478 PrintInfo(infostream); // ServerMap/ClientMap:
1479 infostream<<"Blocks modified by: "<<std::endl;
1480 modprofiler.print(infostream);
1485 void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
1487 timerUpdate(0.0, -1.0, unloaded_blocks);
1490 void Map::deleteSectors(std::vector<v2s16> §orList)
1492 for(std::vector<v2s16>::iterator j = sectorList.begin();
1493 j != sectorList.end(); ++j) {
1494 MapSector *sector = m_sectors[*j];
1495 // If sector is in sector cache, remove it from there
1496 if(m_sector_cache == sector)
1497 m_sector_cache = NULL;
1498 // Remove from map and delete
1499 m_sectors.erase(*j);
1504 void Map::PrintInfo(std::ostream &out)
1509 #define WATER_DROP_BOOST 4
1513 NEIGHBOR_SAME_LEVEL,
1516 struct NodeNeighbor {
1520 bool l; //can liquid
1526 NodeNeighbor(const MapNode &node, NeighborType n_type, v3s16 pos)
1533 void Map::transforming_liquid_add(v3s16 p) {
1534 m_transforming_liquid.push_back(p);
1537 s32 Map::transforming_liquid_size() {
1538 return m_transforming_liquid.size();
1541 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1544 INodeDefManager *nodemgr = m_gamedef->ndef();
1546 DSTACK(__FUNCTION_NAME);
1547 //TimeTaker timer("transformLiquids()");
1550 u32 initial_size = m_transforming_liquid.size();
1552 /*if(initial_size != 0)
1553 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1555 // list of nodes that due to viscosity have not reached their max level height
1556 std::deque<v3s16> must_reflow;
1558 // List of MapBlocks that will require a lighting update (due to lava)
1559 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1561 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1562 u32 loop_max = liquid_loop_max;
1566 /* If liquid_loop_max is not keeping up with the queue size increase
1567 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1569 if (m_transforming_liquid.size() > loop_max * 2) {
1571 float server_step = g_settings->getFloat("dedicated_server_step");
1572 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1573 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1575 m_transforming_liquid_loop_count_multiplier = 1.0;
1578 loop_max *= m_transforming_liquid_loop_count_multiplier;
1581 while(m_transforming_liquid.size() != 0)
1583 // This should be done here so that it is done when continue is used
1584 if(loopcount >= initial_size || loopcount >= loop_max)
1589 Get a queued transforming liquid node
1591 v3s16 p0 = m_transforming_liquid.front();
1592 m_transforming_liquid.pop_front();
1594 MapNode n0 = getNodeNoEx(p0);
1597 Collect information about current node
1599 s8 liquid_level = -1;
1600 content_t liquid_kind = CONTENT_IGNORE;
1601 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1602 switch (liquid_type) {
1604 liquid_level = LIQUID_LEVEL_SOURCE;
1605 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1607 case LIQUID_FLOWING:
1608 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1609 liquid_kind = n0.getContent();
1612 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1613 // continue with the next node.
1614 if (n0.getContent() != CONTENT_AIR)
1616 liquid_kind = CONTENT_AIR;
1621 Collect information about the environment
1623 const v3s16 *dirs = g_6dirs;
1624 NodeNeighbor sources[6]; // surrounding sources
1625 int num_sources = 0;
1626 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1628 NodeNeighbor airs[6]; // surrounding air
1630 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1631 int num_neutrals = 0;
1632 bool flowing_down = false;
1633 for (u16 i = 0; i < 6; i++) {
1634 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1637 nt = NEIGHBOR_UPPER;
1640 nt = NEIGHBOR_LOWER;
1643 v3s16 npos = p0 + dirs[i];
1644 NodeNeighbor nb(getNodeNoEx(npos), nt, npos);
1645 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1647 if (nb.n.getContent() == CONTENT_AIR) {
1648 airs[num_airs++] = nb;
1649 // if the current node is a water source the neighbor
1650 // should be enqueded for transformation regardless of whether the
1651 // current node changes or not.
1652 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1653 m_transforming_liquid.push_back(npos);
1654 // if the current node happens to be a flowing node, it will start to flow down here.
1655 if (nb.t == NEIGHBOR_LOWER) {
1656 flowing_down = true;
1659 neutrals[num_neutrals++] = nb;
1663 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1664 if (liquid_kind == CONTENT_AIR)
1665 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1666 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1667 neutrals[num_neutrals++] = nb;
1669 // Do not count bottom source, it will screw things up
1671 sources[num_sources++] = nb;
1674 case LIQUID_FLOWING:
1675 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1676 if (liquid_kind == CONTENT_AIR)
1677 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1678 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1679 neutrals[num_neutrals++] = nb;
1681 flows[num_flows++] = nb;
1682 if (nb.t == NEIGHBOR_LOWER)
1683 flowing_down = true;
1690 decide on the type (and possibly level) of the current node
1692 content_t new_node_content;
1693 s8 new_node_level = -1;
1694 s8 max_node_level = -1;
1696 u8 range = nodemgr->get(liquid_kind).liquid_range;
1697 if (range > LIQUID_LEVEL_MAX+1)
1698 range = LIQUID_LEVEL_MAX+1;
1700 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1701 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1702 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1703 // it's perfectly safe to use liquid_kind here to determine the new node content.
1704 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1705 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1706 // liquid_kind is set properly, see above
1707 new_node_content = liquid_kind;
1708 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1709 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1710 new_node_content = CONTENT_AIR;
1712 // no surrounding sources, so get the maximum level that can flow into this node
1713 for (u16 i = 0; i < num_flows; i++) {
1714 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1715 switch (flows[i].t) {
1716 case NEIGHBOR_UPPER:
1717 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1718 max_node_level = LIQUID_LEVEL_MAX;
1719 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1720 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1721 } else if (nb_liquid_level > max_node_level)
1722 max_node_level = nb_liquid_level;
1724 case NEIGHBOR_LOWER:
1726 case NEIGHBOR_SAME_LEVEL:
1727 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1728 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1729 max_node_level = nb_liquid_level - 1;
1735 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1736 if (viscosity > 1 && max_node_level != liquid_level) {
1737 // amount to gain, limited by viscosity
1738 // must be at least 1 in absolute value
1739 s8 level_inc = max_node_level - liquid_level;
1740 if (level_inc < -viscosity || level_inc > viscosity)
1741 new_node_level = liquid_level + level_inc/viscosity;
1742 else if (level_inc < 0)
1743 new_node_level = liquid_level - 1;
1744 else if (level_inc > 0)
1745 new_node_level = liquid_level + 1;
1746 if (new_node_level != max_node_level)
1747 must_reflow.push_back(p0);
1749 new_node_level = max_node_level;
1751 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1752 new_node_content = liquid_kind;
1754 new_node_content = CONTENT_AIR;
1759 check if anything has changed. if not, just continue with the next node.
1761 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1762 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1763 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1769 update the current node
1772 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1773 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1774 // set level to last 3 bits, flowing down bit to 4th bit
1775 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1777 // set the liquid level and flow bit to 0
1778 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1780 n0.setContent(new_node_content);
1782 // Find out whether there is a suspect for this action
1783 std::string suspect;
1784 if(m_gamedef->rollback()) {
1785 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1788 if(m_gamedef->rollback() && !suspect.empty()){
1790 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1791 // Get old node for rollback
1792 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1796 RollbackNode rollback_newnode(this, p0, m_gamedef);
1797 RollbackAction action;
1798 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1799 m_gamedef->rollback()->reportAction(action);
1805 v3s16 blockpos = getNodeBlockPos(p0);
1806 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1808 modified_blocks[blockpos] = block;
1809 // If new or old node emits light, MapBlock requires lighting update
1810 if(nodemgr->get(n0).light_source != 0 ||
1811 nodemgr->get(n00).light_source != 0)
1812 lighting_modified_blocks[block->getPos()] = block;
1816 enqueue neighbors for update if neccessary
1818 switch (nodemgr->get(n0.getContent()).liquid_type) {
1820 case LIQUID_FLOWING:
1821 // make sure source flows into all neighboring nodes
1822 for (u16 i = 0; i < num_flows; i++)
1823 if (flows[i].t != NEIGHBOR_UPPER)
1824 m_transforming_liquid.push_back(flows[i].p);
1825 for (u16 i = 0; i < num_airs; i++)
1826 if (airs[i].t != NEIGHBOR_UPPER)
1827 m_transforming_liquid.push_back(airs[i].p);
1830 // this flow has turned to air; neighboring flows might need to do the same
1831 for (u16 i = 0; i < num_flows; i++)
1832 m_transforming_liquid.push_back(flows[i].p);
1836 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1838 for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
1839 m_transforming_liquid.push_back(*iter);
1841 updateLighting(lighting_modified_blocks, modified_blocks);
1844 /* ----------------------------------------------------------------------
1845 * Manage the queue so that it does not grow indefinately
1847 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1849 if (time_until_purge == 0)
1850 return; // Feature disabled
1852 time_until_purge *= 1000; // seconds -> milliseconds
1854 u32 curr_time = getTime(PRECISION_MILLI);
1855 u32 prev_unprocessed = m_unprocessed_count;
1856 m_unprocessed_count = m_transforming_liquid.size();
1858 // if unprocessed block count is decreasing or stable
1859 if (m_unprocessed_count <= prev_unprocessed) {
1860 m_queue_size_timer_started = false;
1862 if (!m_queue_size_timer_started)
1863 m_inc_trending_up_start_time = curr_time;
1864 m_queue_size_timer_started = true;
1867 // Account for curr_time overflowing
1868 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1869 m_queue_size_timer_started = false;
1871 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1872 * and the number of unprocessed blocks is still > liquid_loop_max then we
1873 * cannot keep up; dump the oldest blocks from the queue so that the queue
1874 * has liquid_loop_max items in it
1876 if (m_queue_size_timer_started
1877 && curr_time - m_inc_trending_up_start_time > time_until_purge
1878 && m_unprocessed_count > liquid_loop_max) {
1880 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1882 infostream << "transformLiquids(): DUMPING " << dump_qty
1883 << " blocks from the queue" << std::endl;
1886 m_transforming_liquid.pop_front();
1888 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1889 m_unprocessed_count = m_transforming_liquid.size();
1893 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1895 v3s16 blockpos = getNodeBlockPos(p);
1896 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1897 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1899 infostream<<"Map::getNodeMetadata(): Need to emerge "
1900 <<PP(blockpos)<<std::endl;
1901 block = emergeBlock(blockpos, false);
1904 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1908 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1912 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1914 v3s16 blockpos = getNodeBlockPos(p);
1915 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1916 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1918 infostream<<"Map::setNodeMetadata(): Need to emerge "
1919 <<PP(blockpos)<<std::endl;
1920 block = emergeBlock(blockpos, false);
1923 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1927 block->m_node_metadata.set(p_rel, meta);
1931 void Map::removeNodeMetadata(v3s16 p)
1933 v3s16 blockpos = getNodeBlockPos(p);
1934 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1935 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1938 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1942 block->m_node_metadata.remove(p_rel);
1945 NodeTimer Map::getNodeTimer(v3s16 p)
1947 v3s16 blockpos = getNodeBlockPos(p);
1948 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1949 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1951 infostream<<"Map::getNodeTimer(): Need to emerge "
1952 <<PP(blockpos)<<std::endl;
1953 block = emergeBlock(blockpos, false);
1956 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1960 NodeTimer t = block->m_node_timers.get(p_rel);
1964 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1966 v3s16 blockpos = getNodeBlockPos(p);
1967 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1968 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1970 infostream<<"Map::setNodeTimer(): Need to emerge "
1971 <<PP(blockpos)<<std::endl;
1972 block = emergeBlock(blockpos, false);
1975 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1979 block->m_node_timers.set(p_rel, t);
1982 void Map::removeNodeTimer(v3s16 p)
1984 v3s16 blockpos = getNodeBlockPos(p);
1985 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1986 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1989 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
1993 block->m_node_timers.remove(p_rel);
1999 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2000 Map(dout_server, gamedef),
2002 m_map_metadata_changed(true)
2004 verbosestream<<__FUNCTION_NAME<<std::endl;
2007 Try to load map; if not found, create a new one.
2010 // Determine which database backend to use
2011 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2013 bool succeeded = conf.readConfigFile(conf_path.c_str());
2014 if (!succeeded || !conf.exists("backend")) {
2015 // fall back to sqlite3
2016 conf.set("backend", "sqlite3");
2018 std::string backend = conf.get("backend");
2019 dbase = createDatabase(backend, savedir, conf);
2021 if (!conf.updateConfigFile(conf_path.c_str()))
2022 errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
2024 m_savedir = savedir;
2025 m_map_saving_enabled = false;
2029 // If directory exists, check contents and load if possible
2030 if(fs::PathExists(m_savedir))
2032 // If directory is empty, it is safe to save into it.
2033 if(fs::GetDirListing(m_savedir).size() == 0)
2035 infostream<<"ServerMap: Empty save directory is valid."
2037 m_map_saving_enabled = true;
2042 // Load map metadata (seed, chunksize)
2045 catch(SettingNotFoundException &e){
2046 infostream<<"ServerMap: Some metadata not found."
2047 <<" Using default settings."<<std::endl;
2049 catch(FileNotGoodException &e){
2050 infostream<<"WARNING: Could not load map metadata"
2051 //<<" Disabling chunk-based generator."
2056 infostream<<"ServerMap: Successfully loaded map "
2057 <<"metadata from "<<savedir
2058 <<", assuming valid save directory."
2059 <<" seed="<< m_emerge->params.seed <<"."
2062 m_map_saving_enabled = true;
2063 // Map loaded, not creating new one
2067 // If directory doesn't exist, it is safe to save to it
2069 m_map_saving_enabled = true;
2072 catch(std::exception &e)
2074 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2075 <<", exception: "<<e.what()<<std::endl;
2076 infostream<<"Please remove the map or fix it."<<std::endl;
2077 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2080 infostream<<"Initializing new map."<<std::endl;
2082 // Create zero sector
2083 emergeSector(v2s16(0,0));
2085 // Initially write whole map
2086 save(MOD_STATE_CLEAN);
2089 ServerMap::~ServerMap()
2091 verbosestream<<__FUNCTION_NAME<<std::endl;
2095 if(m_map_saving_enabled)
2097 // Save only changed parts
2098 save(MOD_STATE_WRITE_AT_UNLOAD);
2099 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2103 infostream<<"ServerMap: Map not saved"<<std::endl;
2106 catch(std::exception &e)
2108 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2109 <<", exception: "<<e.what()<<std::endl;
2113 Close database if it was opened
2121 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2122 for(; i.atEnd() == false; i++)
2124 MapChunk *chunk = i.getNode()->getValue();
2130 u64 ServerMap::getSeed()
2132 return m_emerge->params.seed;
2135 s16 ServerMap::getWaterLevel()
2137 return m_emerge->params.water_level;
2140 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2142 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2143 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2145 s16 chunksize = m_emerge->params.chunksize;
2146 s16 coffset = -chunksize / 2;
2147 v3s16 chunk_offset(coffset, coffset, coffset);
2148 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2149 v3s16 blockpos_min = blockpos_div * chunksize;
2150 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2151 blockpos_min += chunk_offset;
2152 blockpos_max += chunk_offset;
2154 v3s16 extra_borders(1,1,1);
2156 // Do nothing if not inside limits (+-1 because of neighbors)
2157 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2158 blockpos_over_limit(blockpos_max + extra_borders))
2161 data->seed = m_emerge->params.seed;
2162 data->blockpos_min = blockpos_min;
2163 data->blockpos_max = blockpos_max;
2164 data->blockpos_requested = blockpos;
2165 data->nodedef = m_gamedef->ndef();
2168 Create the whole area of this and the neighboring blocks
2171 //TimeTaker timer("initBlockMake() create area");
2173 for(s16 x=blockpos_min.X-extra_borders.X;
2174 x<=blockpos_max.X+extra_borders.X; x++)
2175 for(s16 z=blockpos_min.Z-extra_borders.Z;
2176 z<=blockpos_max.Z+extra_borders.Z; z++)
2178 v2s16 sectorpos(x, z);
2179 // Sector metadata is loaded from disk if not already loaded.
2180 ServerMapSector *sector = createSector(sectorpos);
2181 FATAL_ERROR_IF(sector == NULL, "createSector() failed");
2184 for(s16 y=blockpos_min.Y-extra_borders.Y;
2185 y<=blockpos_max.Y+extra_borders.Y; y++)
2188 //MapBlock *block = createBlock(p);
2189 // 1) get from memory, 2) load from disk
2190 MapBlock *block = emergeBlock(p, false);
2191 // 3) create a blank one
2194 block = createBlock(p);
2197 Block gets sunlight if this is true.
2199 Refer to the map generator heuristics.
2201 bool ug = m_emerge->isBlockUnderground(p);
2202 block->setIsUnderground(ug);
2205 // Lighting will not be valid after make_chunk is called
2206 block->setLightingExpired(true);
2207 // Lighting will be calculated
2208 //block->setLightingExpired(false);
2214 Now we have a big empty area.
2216 Make a ManualMapVoxelManipulator that contains this and the
2220 // The area that contains this block and it's neighbors
2221 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2222 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2224 data->vmanip = new MMVManip(this);
2225 //data->vmanip->setMap(this);
2229 //TimeTaker timer("initBlockMake() initialEmerge");
2230 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2233 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2234 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2235 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2236 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2237 core::map<v3s16, u8>::Node *n;
2238 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2241 u8 flags = n->getValue();
2242 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2248 // Data is ready now.
2252 void ServerMap::finishBlockMake(BlockMakeData *data,
2253 std::map<v3s16, MapBlock*> &changed_blocks)
2255 v3s16 blockpos_min = data->blockpos_min;
2256 v3s16 blockpos_max = data->blockpos_max;
2257 v3s16 blockpos_requested = data->blockpos_requested;
2258 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2259 <<blockpos_requested.Y<<","
2260 <<blockpos_requested.Z<<")"<<std::endl;*/
2262 v3s16 extra_borders(1,1,1);
2264 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2266 /*infostream<<"Resulting vmanip:"<<std::endl;
2267 data->vmanip.print(infostream);*/
2269 // Make sure affected blocks are loaded
2270 for(s16 x=blockpos_min.X-extra_borders.X;
2271 x<=blockpos_max.X+extra_borders.X; x++)
2272 for(s16 z=blockpos_min.Z-extra_borders.Z;
2273 z<=blockpos_max.Z+extra_borders.Z; z++)
2274 for(s16 y=blockpos_min.Y-extra_borders.Y;
2275 y<=blockpos_max.Y+extra_borders.Y; y++)
2278 // Load from disk if not already in memory
2279 emergeBlock(p, false);
2283 Blit generated stuff to map
2284 NOTE: blitBackAll adds nearly everything to changed_blocks
2288 //TimeTaker timer("finishBlockMake() blitBackAll");
2289 data->vmanip->blitBackAll(&changed_blocks);
2292 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2295 Copy transforming liquid information
2297 while(data->transforming_liquid.size() > 0)
2299 m_transforming_liquid.push_back(data->transforming_liquid.front());
2300 data->transforming_liquid.pop_front();
2304 Do stuff in central blocks
2312 TimeTaker t("finishBlockMake lighting update");
2314 core::map<v3s16, MapBlock*> lighting_update_blocks;
2317 for(s16 x=blockpos_min.X-extra_borders.X;
2318 x<=blockpos_max.X+extra_borders.X; x++)
2319 for(s16 z=blockpos_min.Z-extra_borders.Z;
2320 z<=blockpos_max.Z+extra_borders.Z; z++)
2321 for(s16 y=blockpos_min.Y-extra_borders.Y;
2322 y<=blockpos_max.Y+extra_borders.Y; y++)
2325 MapBlock *block = getBlockNoCreateNoEx(p);
2327 lighting_update_blocks.insert(block->getPos(), block);
2330 updateLighting(lighting_update_blocks, changed_blocks);
2334 Set lighting to non-expired state in all of them.
2335 This is cheating, but it is not fast enough if all of them
2336 would actually be updated.
2338 for(s16 x=blockpos_min.X-extra_borders.X;
2339 x<=blockpos_max.X+extra_borders.X; x++)
2340 for(s16 z=blockpos_min.Z-extra_borders.Z;
2341 z<=blockpos_max.Z+extra_borders.Z; z++)
2342 for(s16 y=blockpos_min.Y-extra_borders.Y;
2343 y<=blockpos_max.Y+extra_borders.Y; y++)
2346 MapBlock * block = getBlockNoCreateNoEx(p);
2348 block->setLightingExpired(false);
2352 if(enable_mapgen_debug_info == false)
2353 t.stop(true); // Hide output
2358 Go through changed blocks
2360 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2361 i != changed_blocks.end(); ++i)
2363 MapBlock *block = i->second;
2367 Update day/night difference cache of the MapBlocks
2369 block->expireDayNightDiff();
2371 Set block as modified
2373 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2374 "finishBlockMake expireDayNightDiff");
2378 Set central blocks as generated
2380 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2381 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2382 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2385 MapBlock *block = getBlockNoCreateNoEx(p);
2388 block->setGenerated(true);
2392 Save changed parts of map
2393 NOTE: Will be saved later.
2395 //save(MOD_STATE_WRITE_AT_UNLOAD);
2397 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2398 <<","<<blockpos_requested.Y<<","
2399 <<blockpos_requested.Z<<")"<<std::endl;*/
2403 if(enable_mapgen_debug_info)
2406 Analyze resulting blocks
2408 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2409 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2410 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2411 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2412 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2413 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2415 v3s16 p = v3s16(x,y,z);
2416 MapBlock *block = getBlockNoCreateNoEx(p);
2418 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2419 infostream<<"Generated "<<spos<<": "
2420 <<analyze_block(block)<<std::endl;
2425 getBlockNoCreateNoEx(blockpos_requested);
2428 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2430 DSTACKF("%s: p2d=(%d,%d)",
2435 Check if it exists already in memory
2437 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2442 Try to load it from disk (with blocks)
2444 //if(loadSectorFull(p2d) == true)
2447 Try to load metadata from disk
2450 if(loadSectorMeta(p2d) == true)
2452 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2455 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2456 throw InvalidPositionException("");
2462 Do not create over-limit
2464 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2465 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2466 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2467 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2468 throw InvalidPositionException("createSector(): pos. over limit");
2471 Generate blank sector
2474 sector = new ServerMapSector(this, p2d, m_gamedef);
2476 // Sector position on map in nodes
2477 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2482 m_sectors[p2d] = sector;
2489 This is a quick-hand function for calling makeBlock().
2491 MapBlock * ServerMap::generateBlock(
2493 std::map<v3s16, MapBlock*> &modified_blocks
2496 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2498 /*infostream<<"generateBlock(): "
2499 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2502 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2504 TimeTaker timer("generateBlock");
2506 //MapBlock *block = original_dummy;
2508 v2s16 p2d(p.X, p.Z);
2509 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2512 Do not generate over-limit
2514 if(blockpos_over_limit(p))
2516 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2517 throw InvalidPositionException("generateBlock(): pos. over limit");
2521 Create block make data
2524 initBlockMake(&data, p);
2530 TimeTaker t("mapgen::make_block()");
2531 mapgen->makeChunk(&data);
2532 //mapgen::make_block(&data);
2534 if(enable_mapgen_debug_info == false)
2535 t.stop(true); // Hide output
2539 Blit data back on map, update lighting, add mobs and whatever this does
2541 finishBlockMake(&data, modified_blocks);
2546 MapBlock *block = getBlockNoCreateNoEx(p);
2554 bool erroneus_content = false;
2555 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2556 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2557 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2560 MapNode n = block->getNode(p);
2561 if(n.getContent() == CONTENT_IGNORE)
2563 infostream<<"CONTENT_IGNORE at "
2564 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2566 erroneus_content = true;
2570 if(erroneus_content)
2579 Generate a completely empty block
2583 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2584 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2586 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2589 n.setContent(CONTENT_AIR);
2590 block->setNode(v3s16(x0,y0,z0), n);
2596 if(enable_mapgen_debug_info == false)
2597 timer.stop(true); // Hide output
2603 MapBlock * ServerMap::createBlock(v3s16 p)
2605 DSTACKF("%s: p=(%d,%d,%d)",
2606 __FUNCTION_NAME, p.X, p.Y, p.Z);
2609 Do not create over-limit
2611 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2612 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2613 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2614 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2615 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2616 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2617 throw InvalidPositionException("createBlock(): pos. over limit");
2619 v2s16 p2d(p.X, p.Z);
2622 This will create or load a sector if not found in memory.
2623 If block exists on disk, it will be loaded.
2625 NOTE: On old save formats, this will be slow, as it generates
2626 lighting on blocks for them.
2628 ServerMapSector *sector;
2630 sector = (ServerMapSector*)createSector(p2d);
2631 assert(sector->getId() == MAPSECTOR_SERVER);
2633 catch(InvalidPositionException &e)
2635 infostream<<"createBlock: createSector() failed"<<std::endl;
2639 NOTE: This should not be done, or at least the exception
2640 should not be passed on as std::exception, because it
2641 won't be catched at all.
2643 /*catch(std::exception &e)
2645 infostream<<"createBlock: createSector() failed: "
2646 <<e.what()<<std::endl;
2651 Try to get a block from the sector
2654 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2657 if(block->isDummy())
2662 block = sector->createBlankBlock(block_y);
2667 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2669 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2671 p.X, p.Y, p.Z, create_blank);
2674 MapBlock *block = getBlockNoCreateNoEx(p);
2675 if(block && block->isDummy() == false)
2680 MapBlock *block = loadBlock(p);
2686 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2687 MapBlock *block = sector->createBlankBlock(p.Y);
2695 std::map<v3s16, MapBlock*> modified_blocks;
2696 MapBlock *block = generateBlock(p, modified_blocks);
2700 event.type = MEET_OTHER;
2703 // Copy modified_blocks to event
2704 for(std::map<v3s16, MapBlock*>::iterator
2705 i = modified_blocks.begin();
2706 i != modified_blocks.end(); ++i)
2708 event.modified_blocks.insert(i->first);
2712 dispatchEvent(&event);
2722 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2724 MapBlock *block = getBlockNoCreateNoEx(p3d);
2726 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2731 void ServerMap::prepareBlock(MapBlock *block) {
2734 // N.B. This requires no synchronization, since data will not be modified unless
2735 // the VoxelManipulator being updated belongs to the same thread.
2736 void ServerMap::updateVManip(v3s16 pos)
2738 Mapgen *mg = m_emerge->getCurrentMapgen();
2742 MMVManip *vm = mg->vm;
2746 if (!vm->m_area.contains(pos))
2749 s32 idx = vm->m_area.index(pos);
2750 vm->m_data[idx] = getNodeNoEx(pos);
2751 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2753 vm->m_is_dirty = true;
2756 s16 ServerMap::findGroundLevel(v2s16 p2d)
2760 Uh, just do something random...
2762 // Find existing map from top to down
2765 v3s16 p(p2d.X, max, p2d.Y);
2766 for(; p.Y>min; p.Y--)
2768 MapNode n = getNodeNoEx(p);
2769 if(n.getContent() != CONTENT_IGNORE)
2774 // If this node is not air, go to plan b
2775 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2777 // Search existing walkable and return it
2778 for(; p.Y>min; p.Y--)
2780 MapNode n = getNodeNoEx(p);
2781 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2790 Determine from map generator noise functions
2793 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2796 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2797 //return (s16)level;
2800 bool ServerMap::loadFromFolders() {
2801 if (!dbase->initialized() &&
2802 !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2807 void ServerMap::createDirs(std::string path)
2809 if(fs::CreateAllDirs(path) == false)
2811 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2812 <<"\""<<path<<"\""<<std::endl;
2813 throw BaseException("ServerMap failed to create directory");
2817 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2823 snprintf(cc, 9, "%.4x%.4x",
2824 (unsigned int) pos.X & 0xffff,
2825 (unsigned int) pos.Y & 0xffff);
2827 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2829 snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
2830 (unsigned int) pos.X & 0xfff,
2831 (unsigned int) pos.Y & 0xfff);
2833 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2840 v2s16 ServerMap::getSectorPos(std::string dirname)
2842 unsigned int x = 0, y = 0;
2844 std::string component;
2845 fs::RemoveLastPathComponent(dirname, &component, 1);
2846 if(component.size() == 8)
2849 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2851 else if(component.size() == 3)
2854 fs::RemoveLastPathComponent(dirname, &component, 2);
2855 r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
2856 // Sign-extend the 12 bit values up to 16 bits...
2857 if(x & 0x800) x |= 0xF000;
2858 if(y & 0x800) y |= 0xF000;
2865 FATAL_ERROR_IF(r != 2, "getSectorPos()");
2866 v2s16 pos((s16)x, (s16)y);
2870 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2872 v2s16 p2d = getSectorPos(sectordir);
2874 if(blockfile.size() != 4){
2875 throw InvalidFilenameException("Invalid block filename");
2878 int r = sscanf(blockfile.c_str(), "%4x", &y);
2880 throw InvalidFilenameException("Invalid block filename");
2881 return v3s16(p2d.X, y, p2d.Y);
2884 std::string ServerMap::getBlockFilename(v3s16 p)
2887 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2891 void ServerMap::save(ModifiedState save_level)
2893 DSTACK(__FUNCTION_NAME);
2894 if(m_map_saving_enabled == false) {
2895 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2899 if(save_level == MOD_STATE_CLEAN)
2900 infostream<<"ServerMap: Saving whole map, this can take time."
2903 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
2907 // Profile modified reasons
2908 Profiler modprofiler;
2910 u32 sector_meta_count = 0;
2911 u32 block_count = 0;
2912 u32 block_count_all = 0; // Number of blocks in memory
2914 // Don't do anything with sqlite unless something is really saved
2915 bool save_started = false;
2917 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2918 i != m_sectors.end(); ++i) {
2919 ServerMapSector *sector = (ServerMapSector*)i->second;
2920 assert(sector->getId() == MAPSECTOR_SERVER);
2922 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
2923 saveSectorMeta(sector);
2924 sector_meta_count++;
2927 MapBlockVect blocks;
2928 sector->getBlocks(blocks);
2930 for(MapBlockVect::iterator j = blocks.begin();
2931 j != blocks.end(); ++j) {
2932 MapBlock *block = *j;
2936 if(block->getModified() >= (u32)save_level) {
2940 save_started = true;
2943 modprofiler.add(block->getModifiedReason(), 1);
2948 /*infostream<<"ServerMap: Written block ("
2949 <<block->getPos().X<<","
2950 <<block->getPos().Y<<","
2951 <<block->getPos().Z<<")"
2961 Only print if something happened or saved whole map
2963 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2964 || block_count != 0) {
2965 infostream<<"ServerMap: Written: "
2966 <<sector_meta_count<<" sector metadata files, "
2967 <<block_count<<" block files"
2968 <<", "<<block_count_all<<" blocks in memory."
2970 PrintInfo(infostream); // ServerMap/ClientMap:
2971 infostream<<"Blocks modified by: "<<std::endl;
2972 modprofiler.print(infostream);
2976 void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
2978 if (loadFromFolders()) {
2979 errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
2980 << "all blocks that are stored in flat files." << std::endl;
2982 dbase->listAllLoadableBlocks(dst);
2985 void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
2987 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
2988 si != m_sectors.end(); ++si)
2990 MapSector *sector = si->second;
2992 MapBlockVect blocks;
2993 sector->getBlocks(blocks);
2995 for(MapBlockVect::iterator i = blocks.begin();
2996 i != blocks.end(); ++i) {
2997 v3s16 p = (*i)->getPos();
3003 void ServerMap::saveMapMeta()
3005 DSTACK(__FUNCTION_NAME);
3007 createDirs(m_savedir);
3009 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3010 std::ostringstream oss(std::ios_base::binary);
3013 m_emerge->params.save(conf);
3014 conf.writeLines(oss);
3016 oss << "[end_of_params]\n";
3018 if(!fs::safeWriteToFile(fullpath, oss.str())) {
3019 errorstream << "ServerMap::saveMapMeta(): "
3020 << "could not write " << fullpath << std::endl;
3021 throw FileNotGoodException("Cannot save chunk metadata");
3024 m_map_metadata_changed = false;
3027 void ServerMap::loadMapMeta()
3029 DSTACK(__FUNCTION_NAME);
3032 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3034 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3036 errorstream << "ServerMap::loadMapMeta(): "
3037 "could not open " << fullpath << std::endl;
3038 throw FileNotGoodException("Cannot open map metadata");
3041 if (!conf.parseConfigLines(is, "[end_of_params]")) {
3042 throw SerializationError("ServerMap::loadMapMeta(): "
3043 "[end_of_params] not found!");
3046 m_emerge->params.load(conf);
3048 verbosestream << "ServerMap::loadMapMeta(): seed="
3049 << m_emerge->params.seed << std::endl;
3052 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3054 DSTACK(__FUNCTION_NAME);
3055 // Format used for writing
3056 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3058 v2s16 pos = sector->getPos();
3059 std::string dir = getSectorDir(pos);
3062 std::string fullpath = dir + DIR_DELIM + "meta";
3063 std::ostringstream ss(std::ios_base::binary);
3065 sector->serialize(ss, version);
3067 if(!fs::safeWriteToFile(fullpath, ss.str()))
3068 throw FileNotGoodException("Cannot write sector metafile");
3070 sector->differs_from_disk = false;
3073 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3075 DSTACK(__FUNCTION_NAME);
3077 v2s16 p2d = getSectorPos(sectordir);
3079 ServerMapSector *sector = NULL;
3081 std::string fullpath = sectordir + DIR_DELIM + "meta";
3082 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3083 if(is.good() == false)
3085 // If the directory exists anyway, it probably is in some old
3086 // format. Just go ahead and create the sector.
3087 if(fs::PathExists(sectordir))
3089 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3090 <<fullpath<<" doesn't exist but directory does."
3091 <<" Continuing with a sector with no metadata."
3093 sector = new ServerMapSector(this, p2d, m_gamedef);
3094 m_sectors[p2d] = sector;
3098 throw FileNotGoodException("Cannot open sector metafile");
3103 sector = ServerMapSector::deSerialize
3104 (is, this, p2d, m_sectors, m_gamedef);
3106 saveSectorMeta(sector);
3109 sector->differs_from_disk = false;
3114 bool ServerMap::loadSectorMeta(v2s16 p2d)
3116 DSTACK(__FUNCTION_NAME);
3118 // The directory layout we're going to load from.
3119 // 1 - original sectors/xxxxzzzz/
3120 // 2 - new sectors2/xxx/zzz/
3121 // If we load from anything but the latest structure, we will
3122 // immediately save to the new one, and remove the old.
3124 std::string sectordir1 = getSectorDir(p2d, 1);
3125 std::string sectordir;
3126 if(fs::PathExists(sectordir1))
3128 sectordir = sectordir1;
3133 sectordir = getSectorDir(p2d, 2);
3137 loadSectorMeta(sectordir, loadlayout != 2);
3139 catch(InvalidFilenameException &e)
3143 catch(FileNotGoodException &e)
3147 catch(std::exception &e)
3156 bool ServerMap::loadSectorFull(v2s16 p2d)
3158 DSTACK(__FUNCTION_NAME);
3160 MapSector *sector = NULL;
3162 // The directory layout we're going to load from.
3163 // 1 - original sectors/xxxxzzzz/
3164 // 2 - new sectors2/xxx/zzz/
3165 // If we load from anything but the latest structure, we will
3166 // immediately save to the new one, and remove the old.
3168 std::string sectordir1 = getSectorDir(p2d, 1);
3169 std::string sectordir;
3170 if(fs::PathExists(sectordir1))
3172 sectordir = sectordir1;
3177 sectordir = getSectorDir(p2d, 2);
3181 sector = loadSectorMeta(sectordir, loadlayout != 2);
3183 catch(InvalidFilenameException &e)
3187 catch(FileNotGoodException &e)
3191 catch(std::exception &e)
3199 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3201 std::vector<fs::DirListNode>::iterator i2;
3202 for(i2=list2.begin(); i2!=list2.end(); i2++)
3208 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3210 catch(InvalidFilenameException &e)
3212 // This catches unknown crap in directory
3218 infostream<<"Sector converted to new layout - deleting "<<
3219 sectordir1<<std::endl;
3220 fs::RecursiveDelete(sectordir1);
3227 Database *ServerMap::createDatabase(const std::string &name, const std::string &savedir, Settings &conf)
3229 if (name == "sqlite3")
3230 return new Database_SQLite3(savedir);
3231 if (name == "dummy")
3232 return new Database_Dummy();
3234 else if (name == "leveldb")
3235 return new Database_LevelDB(savedir);
3238 else if (name == "redis")
3239 return new Database_Redis(conf);
3242 throw BaseException(std::string("Database backend ") + name + " not supported.");
3245 void ServerMap::beginSave()
3250 void ServerMap::endSave()
3255 bool ServerMap::saveBlock(MapBlock *block)
3257 return saveBlock(block, dbase);
3260 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3262 v3s16 p3d = block->getPos();
3264 // Dummy blocks are not written
3265 if (block->isDummy()) {
3266 errorstream << "WARNING: saveBlock: Not writing dummy block "
3267 << PP(p3d) << std::endl;
3271 // Format used for writing
3272 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3275 [0] u8 serialization version
3278 std::ostringstream o(std::ios_base::binary);
3279 o.write((char*) &version, 1);
3280 block->serialize(o, version, true);
3282 std::string data = o.str();
3283 bool ret = db->saveBlock(p3d, data);
3285 // We just wrote it to the disk so clear modified flag
3286 block->resetModified();
3291 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3292 MapSector *sector, bool save_after_load)
3294 DSTACK(__FUNCTION_NAME);
3296 std::string fullpath = sectordir + DIR_DELIM + blockfile;
3299 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3300 if(is.good() == false)
3301 throw FileNotGoodException("Cannot open block file");
3303 v3s16 p3d = getBlockPos(sectordir, blockfile);
3304 v2s16 p2d(p3d.X, p3d.Z);
3306 assert(sector->getPos() == p2d);
3308 u8 version = SER_FMT_VER_INVALID;
3309 is.read((char*)&version, 1);
3312 throw SerializationError("ServerMap::loadBlock(): Failed"
3313 " to read MapBlock version");
3315 /*u32 block_size = MapBlock::serializedLength(version);
3316 SharedBuffer<u8> data(block_size);
3317 is.read((char*)*data, block_size);*/
3319 // This will always return a sector because we're the server
3320 //MapSector *sector = emergeSector(p2d);
3322 MapBlock *block = NULL;
3323 bool created_new = false;
3324 block = sector->getBlockNoCreateNoEx(p3d.Y);
3327 block = sector->createBlankBlockNoInsert(p3d.Y);
3332 block->deSerialize(is, version, true);
3334 // If it's a new block, insert it to the map
3336 sector->insertBlock(block);
3339 Save blocks loaded in old format in new format
3342 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3346 // Should be in database now, so delete the old file
3347 fs::RecursiveDelete(fullpath);
3350 // We just loaded it from the disk, so it's up-to-date.
3351 block->resetModified();
3354 catch(SerializationError &e)
3356 infostream<<"WARNING: Invalid block data on disk "
3357 <<"fullpath="<<fullpath
3358 <<" (SerializationError). "
3359 <<"what()="<<e.what()
3361 // Ignoring. A new one will be generated.
3364 // TODO: Backup file; name is in fullpath.
3368 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3370 DSTACK(__FUNCTION_NAME);
3373 std::istringstream is(*blob, std::ios_base::binary);
3375 u8 version = SER_FMT_VER_INVALID;
3376 is.read((char*)&version, 1);
3379 throw SerializationError("ServerMap::loadBlock(): Failed"
3380 " to read MapBlock version");
3382 /*u32 block_size = MapBlock::serializedLength(version);
3383 SharedBuffer<u8> data(block_size);
3384 is.read((char*)*data, block_size);*/
3386 // This will always return a sector because we're the server
3387 //MapSector *sector = emergeSector(p2d);
3389 MapBlock *block = NULL;
3390 bool created_new = false;
3391 block = sector->getBlockNoCreateNoEx(p3d.Y);
3394 block = sector->createBlankBlockNoInsert(p3d.Y);
3399 block->deSerialize(is, version, true);
3401 // If it's a new block, insert it to the map
3403 sector->insertBlock(block);
3406 Save blocks loaded in old format in new format
3409 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3410 // Only save if asked to; no need to update version
3414 // We just loaded it from, so it's up-to-date.
3415 block->resetModified();
3418 catch(SerializationError &e)
3420 errorstream<<"Invalid block data in database"
3421 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3422 <<" (SerializationError): "<<e.what()<<std::endl;
3424 // TODO: Block should be marked as invalid in memory so that it is
3425 // not touched but the game can run
3427 if(g_settings->getBool("ignore_world_load_errors")){
3428 errorstream<<"Ignoring block load error. Duck and cover! "
3429 <<"(ignore_world_load_errors)"<<std::endl;
3431 throw SerializationError("Invalid block data in database");
3436 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3438 DSTACK(__FUNCTION_NAME);
3440 v2s16 p2d(blockpos.X, blockpos.Z);
3444 ret = dbase->loadBlock(blockpos);
3446 loadBlock(&ret, blockpos, createSector(p2d), false);
3447 return getBlockNoCreateNoEx(blockpos);
3449 // Not found in database, try the files
3451 // The directory layout we're going to load from.
3452 // 1 - original sectors/xxxxzzzz/
3453 // 2 - new sectors2/xxx/zzz/
3454 // If we load from anything but the latest structure, we will
3455 // immediately save to the new one, and remove the old.
3457 std::string sectordir1 = getSectorDir(p2d, 1);
3458 std::string sectordir;
3459 if(fs::PathExists(sectordir1))
3461 sectordir = sectordir1;
3466 sectordir = getSectorDir(p2d, 2);
3470 Make sure sector is loaded
3472 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3476 sector = loadSectorMeta(sectordir, loadlayout != 2);
3478 catch(InvalidFilenameException &e)
3482 catch(FileNotGoodException &e)
3486 catch(std::exception &e)
3493 Make sure file exists
3496 std::string blockfilename = getBlockFilename(blockpos);
3497 if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
3501 Load block and save it to the database
3503 loadBlock(sectordir, blockfilename, sector, true);
3504 return getBlockNoCreateNoEx(blockpos);
3507 bool ServerMap::deleteBlock(v3s16 blockpos)
3509 if (!dbase->deleteBlock(blockpos))
3512 MapBlock *block = getBlockNoCreateNoEx(blockpos);
3514 v2s16 p2d(blockpos.X, blockpos.Z);
3515 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3518 sector->deleteBlock(block);
3524 void ServerMap::PrintInfo(std::ostream &out)
3529 MMVManip::MMVManip(Map *map):
3532 m_create_area(false),
3537 MMVManip::~MMVManip()
3541 void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
3542 bool load_if_inexistent)
3544 TimeTaker timer1("initialEmerge", &emerge_time);
3546 // Units of these are MapBlocks
3547 v3s16 p_min = blockpos_min;
3548 v3s16 p_max = blockpos_max;
3550 VoxelArea block_area_nodes
3551 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3553 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3556 infostream<<"initialEmerge: area: ";
3557 block_area_nodes.print(infostream);
3558 infostream<<" ("<<size_MB<<"MB)";
3559 infostream<<std::endl;
3562 addArea(block_area_nodes);
3564 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3565 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3566 for(s32 x=p_min.X; x<=p_max.X; x++)
3571 std::map<v3s16, u8>::iterator n;
3572 n = m_loaded_blocks.find(p);
3573 if(n != m_loaded_blocks.end())
3576 bool block_data_inexistent = false;
3579 TimeTaker timer1("emerge load", &emerge_load_time);
3581 block = m_map->getBlockNoCreate(p);
3582 if(block->isDummy())
3583 block_data_inexistent = true;
3585 block->copyTo(*this);
3587 catch(InvalidPositionException &e)
3589 block_data_inexistent = true;
3592 if(block_data_inexistent)
3595 if (load_if_inexistent) {
3596 ServerMap *svrmap = (ServerMap *)m_map;
3597 block = svrmap->emergeBlock(p, false);
3599 block = svrmap->createBlock(p);
3600 block->copyTo(*this);
3602 flags |= VMANIP_BLOCK_DATA_INEXIST;
3605 Mark area inexistent
3607 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3608 // Fill with VOXELFLAG_NO_DATA
3609 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3610 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3612 s32 i = m_area.index(a.MinEdge.X,y,z);
3613 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3617 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3619 // Mark that block was loaded as blank
3620 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3623 m_loaded_blocks[p] = flags;
3629 void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
3630 bool overwrite_generated)
3632 if(m_area.getExtent() == v3s16(0,0,0))
3636 Copy data of all blocks
3638 for(std::map<v3s16, u8>::iterator
3639 i = m_loaded_blocks.begin();
3640 i != m_loaded_blocks.end(); ++i)
3643 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3644 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3645 if ((existed == false) || (block == NULL) ||
3646 (overwrite_generated == false && block->isGenerated() == true))
3649 block->copyFrom(*this);
3652 (*modified_blocks)[p] = block;