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):
80 m_transforming_liquid_loop_count_multiplier(1.0f),
81 m_unprocessed_count(0),
82 m_inc_trending_up_start_time(0),
83 m_queue_size_timer_started(false)
92 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
93 i != m_sectors.end(); ++i)
99 void Map::addEventReceiver(MapEventReceiver *event_receiver)
101 m_event_receivers.insert(event_receiver);
104 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
106 m_event_receivers.erase(event_receiver);
109 void Map::dispatchEvent(MapEditEvent *event)
111 for(std::set<MapEventReceiver*>::iterator
112 i = m_event_receivers.begin();
113 i != m_event_receivers.end(); ++i)
115 (*i)->onMapEditEvent(event);
119 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
121 if(m_sector_cache != NULL && p == m_sector_cache_p){
122 MapSector * sector = m_sector_cache;
126 std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
128 if(n == m_sectors.end())
131 MapSector *sector = n->second;
133 // Cache the last result
134 m_sector_cache_p = p;
135 m_sector_cache = sector;
140 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
142 return getSectorNoGenerateNoExNoLock(p);
145 MapSector * Map::getSectorNoGenerate(v2s16 p)
147 MapSector *sector = getSectorNoGenerateNoEx(p);
149 throw InvalidPositionException();
154 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
156 v2s16 p2d(p3d.X, p3d.Z);
157 MapSector * sector = getSectorNoGenerateNoEx(p2d);
160 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
164 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
166 MapBlock *block = getBlockNoCreateNoEx(p3d);
168 throw InvalidPositionException();
172 bool Map::isNodeUnderground(v3s16 p)
174 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock * block = getBlockNoCreate(blockpos);
177 return block->getIsUnderground();
179 catch(InvalidPositionException &e)
185 bool Map::isValidPosition(v3s16 p)
187 v3s16 blockpos = getNodeBlockPos(p);
188 MapBlock *block = getBlockNoCreate(blockpos);
189 return (block != NULL);
192 // Returns a CONTENT_IGNORE node if not found
193 MapNode Map::getNodeNoEx(v3s16 p, bool *is_valid_position)
195 v3s16 blockpos = getNodeBlockPos(p);
196 MapBlock *block = getBlockNoCreateNoEx(blockpos);
198 if (is_valid_position != NULL)
199 *is_valid_position = false;
200 return MapNode(CONTENT_IGNORE);
203 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
206 if (is_valid_position != NULL)
207 *is_valid_position = is_valid_p;
213 // throws InvalidPositionException if not found
214 // TODO: Now this is deprecated, getNodeNoEx should be renamed
215 MapNode Map::getNode(v3s16 p)
217 v3s16 blockpos = getNodeBlockPos(p);
218 MapBlock *block = getBlockNoCreateNoEx(blockpos);
220 throw InvalidPositionException();
221 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
222 bool is_valid_position;
223 MapNode node = block->getNodeNoCheck(relpos, &is_valid_position);
224 if (!is_valid_position)
225 throw InvalidPositionException();
230 // throws InvalidPositionException if not found
231 void Map::setNode(v3s16 p, MapNode & n)
233 v3s16 blockpos = getNodeBlockPos(p);
234 MapBlock *block = getBlockNoCreate(blockpos);
235 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
236 // Never allow placing CONTENT_IGNORE, it fucks up stuff
237 if(n.getContent() == CONTENT_IGNORE){
239 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
240 <<" while trying to replace \""
241 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name
242 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
243 debug_stacks_print_to(infostream);
246 block->setNodeNoCheck(relpos, n);
251 Goes recursively through the neighbours of the node.
253 Alters only transparent nodes.
255 If the lighting of the neighbour is lower than the lighting of
256 the node was (before changing it to 0 at the step before), the
257 lighting of the neighbour is set to 0 and then the same stuff
258 repeats for the neighbour.
260 The ending nodes of the routine are stored in light_sources.
261 This is useful when a light is removed. In such case, this
262 routine can be called for the light node and then again for
263 light_sources to re-light the area without the removed light.
265 values of from_nodes are lighting values.
267 void Map::unspreadLight(enum LightBank bank,
268 std::map<v3s16, u8> & from_nodes,
269 std::set<v3s16> & light_sources,
270 std::map<v3s16, MapBlock*> & modified_blocks)
272 INodeDefManager *nodemgr = m_gamedef->ndef();
275 v3s16(0,0,1), // back
277 v3s16(1,0,0), // right
278 v3s16(0,0,-1), // front
279 v3s16(0,-1,0), // bottom
280 v3s16(-1,0,0), // left
283 if(from_nodes.empty())
286 u32 blockchangecount = 0;
288 std::map<v3s16, u8> unlighted_nodes;
291 Initialize block cache
294 MapBlock *block = NULL;
295 // Cache this a bit, too
296 bool block_checked_in_modified = false;
298 for(std::map<v3s16, u8>::iterator j = from_nodes.begin();
299 j != from_nodes.end(); ++j)
301 v3s16 pos = j->first;
302 v3s16 blockpos = getNodeBlockPos(pos);
304 // Only fetch a new block if the block position has changed
306 if(block == NULL || blockpos != blockpos_last){
307 block = getBlockNoCreate(blockpos);
308 blockpos_last = blockpos;
310 block_checked_in_modified = false;
314 catch(InvalidPositionException &e)
322 // Calculate relative position in block
323 //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
325 // Get node straight from the block
326 //MapNode n = block->getNode(relpos);
328 u8 oldlight = j->second;
330 // Loop through 6 neighbors
331 for(u16 i=0; i<6; i++)
333 // Get the position of the neighbor node
334 v3s16 n2pos = pos + dirs[i];
336 // Get the block where the node is located
337 v3s16 blockpos = getNodeBlockPos(n2pos);
339 // Only fetch a new block if the block position has changed
341 if(block == NULL || blockpos != blockpos_last){
342 block = getBlockNoCreate(blockpos);
343 blockpos_last = blockpos;
345 block_checked_in_modified = false;
349 catch(InvalidPositionException &e) {
353 // Calculate relative position in block
354 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
355 // Get node straight from the block
356 bool is_valid_position;
357 MapNode n2 = block->getNode(relpos, &is_valid_position);
358 if (!is_valid_position)
361 bool changed = false;
363 //TODO: Optimize output by optimizing light_sources?
366 If the neighbor is dimmer than what was specified
367 as oldlight (the light of the previous node)
369 if(n2.getLight(bank, nodemgr) < oldlight)
372 And the neighbor is transparent and it has some light
374 if(nodemgr->get(n2).light_propagates
375 && n2.getLight(bank, nodemgr) != 0)
378 Set light to 0 and add to queue
381 u8 current_light = n2.getLight(bank, nodemgr);
382 n2.setLight(bank, 0, nodemgr);
383 block->setNode(relpos, n2);
385 unlighted_nodes[n2pos] = current_light;
389 Remove from light_sources if it is there
390 NOTE: This doesn't happen nearly at all
392 /*if(light_sources.find(n2pos))
394 infostream<<"Removed from light_sources"<<std::endl;
395 light_sources.remove(n2pos);
400 if(light_sources.find(n2pos) != NULL)
401 light_sources.remove(n2pos);*/
404 light_sources.insert(n2pos);
407 // Add to modified_blocks
408 if(changed == true && block_checked_in_modified == false)
410 // If the block is not found in modified_blocks, add.
411 if(modified_blocks.find(blockpos) == modified_blocks.end())
413 modified_blocks[blockpos] = block;
415 block_checked_in_modified = true;
420 /*infostream<<"unspreadLight(): Changed block "
421 <<blockchangecount<<" times"
422 <<" for "<<from_nodes.size()<<" nodes"
425 if(!unlighted_nodes.empty())
426 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
430 A single-node wrapper of the above
432 void Map::unLightNeighbors(enum LightBank bank,
433 v3s16 pos, u8 lightwas,
434 std::set<v3s16> & light_sources,
435 std::map<v3s16, MapBlock*> & modified_blocks)
437 std::map<v3s16, u8> from_nodes;
438 from_nodes[pos] = lightwas;
440 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
444 Lights neighbors of from_nodes, collects all them and then
447 void Map::spreadLight(enum LightBank bank,
448 std::set<v3s16> & from_nodes,
449 std::map<v3s16, MapBlock*> & modified_blocks)
451 INodeDefManager *nodemgr = m_gamedef->ndef();
453 const v3s16 dirs[6] = {
454 v3s16(0,0,1), // back
456 v3s16(1,0,0), // right
457 v3s16(0,0,-1), // front
458 v3s16(0,-1,0), // bottom
459 v3s16(-1,0,0), // left
462 if(from_nodes.empty())
465 u32 blockchangecount = 0;
467 std::set<v3s16> lighted_nodes;
470 Initialize block cache
473 MapBlock *block = NULL;
474 // Cache this a bit, too
475 bool block_checked_in_modified = false;
477 for(std::set<v3s16>::iterator j = from_nodes.begin();
478 j != from_nodes.end(); ++j)
481 v3s16 blockpos = getNodeBlockPos(pos);
483 // Only fetch a new block if the block position has changed
485 if(block == NULL || blockpos != blockpos_last){
486 block = getBlockNoCreate(blockpos);
487 blockpos_last = blockpos;
489 block_checked_in_modified = false;
493 catch(InvalidPositionException &e) {
500 // Calculate relative position in block
501 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
503 // Get node straight from the block
504 bool is_valid_position;
505 MapNode n = block->getNode(relpos, &is_valid_position);
507 u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0;
508 u8 newlight = diminish_light(oldlight);
510 // Loop through 6 neighbors
511 for(u16 i=0; i<6; i++){
512 // Get the position of the neighbor node
513 v3s16 n2pos = pos + dirs[i];
515 // Get the block where the node is located
516 v3s16 blockpos = getNodeBlockPos(n2pos);
518 // Only fetch a new block if the block position has changed
520 if(block == NULL || blockpos != blockpos_last){
521 block = getBlockNoCreate(blockpos);
522 blockpos_last = blockpos;
524 block_checked_in_modified = false;
528 catch(InvalidPositionException &e) {
532 // Calculate relative position in block
533 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
534 // Get node straight from the block
535 MapNode n2 = block->getNode(relpos, &is_valid_position);
536 if (!is_valid_position)
539 bool changed = false;
541 If the neighbor is brighter than the current node,
542 add to list (it will light up this node on its turn)
544 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
546 lighted_nodes.insert(n2pos);
550 If the neighbor is dimmer than how much light this node
551 would spread on it, add to list
553 if(n2.getLight(bank, nodemgr) < newlight)
555 if(nodemgr->get(n2).light_propagates)
557 n2.setLight(bank, newlight, nodemgr);
558 block->setNode(relpos, n2);
559 lighted_nodes.insert(n2pos);
564 // Add to modified_blocks
565 if(changed == true && block_checked_in_modified == false)
567 // If the block is not found in modified_blocks, add.
568 if(modified_blocks.find(blockpos) == modified_blocks.end())
570 modified_blocks[blockpos] = block;
572 block_checked_in_modified = true;
577 /*infostream<<"spreadLight(): Changed block "
578 <<blockchangecount<<" times"
579 <<" for "<<from_nodes.size()<<" nodes"
582 if(!lighted_nodes.empty())
583 spreadLight(bank, lighted_nodes, modified_blocks);
587 A single-node source variation of the above.
589 void Map::lightNeighbors(enum LightBank bank,
591 std::map<v3s16, MapBlock*> & modified_blocks)
593 std::set<v3s16> from_nodes;
594 from_nodes.insert(pos);
595 spreadLight(bank, from_nodes, modified_blocks);
598 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
600 INodeDefManager *nodemgr = m_gamedef->ndef();
603 v3s16(0,0,1), // back
605 v3s16(1,0,0), // right
606 v3s16(0,0,-1), // front
607 v3s16(0,-1,0), // bottom
608 v3s16(-1,0,0), // left
611 u8 brightest_light = 0;
612 v3s16 brightest_pos(0,0,0);
613 bool found_something = false;
615 // Loop through 6 neighbors
616 for(u16 i=0; i<6; i++){
617 // Get the position of the neighbor node
618 v3s16 n2pos = p + dirs[i];
620 bool is_valid_position;
621 n2 = getNodeNoEx(n2pos, &is_valid_position);
622 if (!is_valid_position)
625 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
626 brightest_light = n2.getLight(bank, nodemgr);
627 brightest_pos = n2pos;
628 found_something = true;
632 if(found_something == false)
633 throw InvalidPositionException();
635 return brightest_pos;
639 Propagates sunlight down from a node.
640 Starting point gets sunlight.
642 Returns the lowest y value of where the sunlight went.
644 Mud is turned into grass in where the sunlight stops.
646 s16 Map::propagateSunlight(v3s16 start,
647 std::map<v3s16, MapBlock*> & modified_blocks)
649 INodeDefManager *nodemgr = m_gamedef->ndef();
654 v3s16 pos(start.X, y, start.Z);
656 v3s16 blockpos = getNodeBlockPos(pos);
659 block = getBlockNoCreate(blockpos);
661 catch(InvalidPositionException &e)
666 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
667 bool is_valid_position;
668 MapNode n = block->getNode(relpos, &is_valid_position);
669 if (!is_valid_position)
672 if(nodemgr->get(n).sunlight_propagates)
674 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
675 block->setNode(relpos, n);
677 modified_blocks[blockpos] = block;
681 // Sunlight goes no further
688 void Map::updateLighting(enum LightBank bank,
689 std::map<v3s16, MapBlock*> & a_blocks,
690 std::map<v3s16, MapBlock*> & modified_blocks)
692 INodeDefManager *nodemgr = m_gamedef->ndef();
694 /*m_dout<<DTIME<<"Map::updateLighting(): "
695 <<a_blocks.size()<<" blocks."<<std::endl;*/
697 //TimeTaker timer("updateLighting");
701 //u32 count_was = modified_blocks.size();
703 std::map<v3s16, MapBlock*> blocks_to_update;
705 std::set<v3s16> light_sources;
707 std::map<v3s16, u8> unlight_from;
709 int num_bottom_invalid = 0;
712 //TimeTaker t("first stuff");
714 for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin();
715 i != a_blocks.end(); ++i)
717 MapBlock *block = i->second;
721 // Don't bother with dummy blocks.
725 v3s16 pos = block->getPos();
726 v3s16 posnodes = block->getPosRelative();
727 modified_blocks[pos] = block;
728 blocks_to_update[pos] = block;
731 Clear all light from block
733 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
734 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
735 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
738 bool is_valid_position;
739 MapNode n = block->getNode(p, &is_valid_position);
740 if (!is_valid_position) {
741 /* This would happen when dealing with a
744 infostream<<"updateLighting(): InvalidPositionException"
748 u8 oldlight = n.getLight(bank, nodemgr);
749 n.setLight(bank, 0, nodemgr);
750 block->setNode(p, n);
752 // If node sources light, add to list
753 u8 source = nodemgr->get(n).light_source;
755 light_sources.insert(p + posnodes);
757 // Collect borders for unlighting
758 if((x==0 || x == MAP_BLOCKSIZE-1
759 || y==0 || y == MAP_BLOCKSIZE-1
760 || z==0 || z == MAP_BLOCKSIZE-1)
763 v3s16 p_map = p + posnodes;
764 unlight_from[p_map] = oldlight;
770 if(bank == LIGHTBANK_DAY)
772 bool bottom_valid = block->propagateSunlight(light_sources);
775 num_bottom_invalid++;
777 // If bottom is valid, we're done.
781 else if(bank == LIGHTBANK_NIGHT)
783 // For night lighting, sunlight is not propagated
788 // Invalid lighting bank
792 /*infostream<<"Bottom for sunlight-propagated block ("
793 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
796 // Bottom sunlight is not valid; get the block and loop to it
800 block = getBlockNoCreate(pos);
802 catch(InvalidPositionException &e)
813 Enable this to disable proper lighting for speeding up map
814 generation for testing or whatever
817 //if(g_settings->get(""))
819 core::map<v3s16, MapBlock*>::Iterator i;
820 i = blocks_to_update.getIterator();
821 for(; i.atEnd() == false; i++)
823 MapBlock *block = i.getNode()->getValue();
824 v3s16 p = block->getPos();
825 block->setLightingExpired(false);
833 //TimeTaker timer("unspreadLight");
834 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
839 u32 diff = modified_blocks.size() - count_was;
840 count_was = modified_blocks.size();
841 infostream<<"unspreadLight modified "<<diff<<std::endl;
845 //TimeTaker timer("spreadLight");
846 spreadLight(bank, light_sources, modified_blocks);
851 u32 diff = modified_blocks.size() - count_was;
852 count_was = modified_blocks.size();
853 infostream<<"spreadLight modified "<<diff<<std::endl;
859 //MapVoxelManipulator vmanip(this);
861 // Make a manual voxel manipulator and load all the blocks
862 // that touch the requested blocks
863 ManualMapVoxelManipulator vmanip(this);
866 //TimeTaker timer("initialEmerge");
868 core::map<v3s16, MapBlock*>::Iterator i;
869 i = blocks_to_update.getIterator();
870 for(; i.atEnd() == false; i++)
872 MapBlock *block = i.getNode()->getValue();
873 v3s16 p = block->getPos();
875 // Add all surrounding blocks
876 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
879 Add all surrounding blocks that have up-to-date lighting
880 NOTE: This doesn't quite do the job (not everything
881 appropriate is lighted)
883 /*for(s16 z=-1; z<=1; z++)
884 for(s16 y=-1; y<=1; y++)
885 for(s16 x=-1; x<=1; x++)
887 v3s16 p2 = p + v3s16(x,y,z);
888 MapBlock *block = getBlockNoCreateNoEx(p2);
893 if(block->getLightingExpired())
895 vmanip.initialEmerge(p2, p2);
898 // Lighting of block will be updated completely
899 block->setLightingExpired(false);
904 //TimeTaker timer("unSpreadLight");
905 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
908 //TimeTaker timer("spreadLight");
909 vmanip.spreadLight(bank, light_sources, nodemgr);
912 //TimeTaker timer("blitBack");
913 vmanip.blitBack(modified_blocks);
915 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
920 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
923 void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks,
924 std::map<v3s16, MapBlock*> & modified_blocks)
926 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
927 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
930 Update information about whether day and night light differ
932 for(std::map<v3s16, MapBlock*>::iterator
933 i = modified_blocks.begin();
934 i != modified_blocks.end(); ++i)
936 MapBlock *block = i->second;
937 block->expireDayNightDiff();
943 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
944 std::map<v3s16, MapBlock*> &modified_blocks,
945 bool remove_metadata)
947 INodeDefManager *ndef = m_gamedef->ndef();
950 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
951 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
954 From this node to nodes underneath:
955 If lighting is sunlight (1.0), unlight neighbours and
960 v3s16 toppos = p + v3s16(0,1,0);
961 //v3s16 bottompos = p + v3s16(0,-1,0);
963 bool node_under_sunlight = true;
964 std::set<v3s16> light_sources;
967 Collect old node for rollback
969 RollbackNode rollback_oldnode(this, p, m_gamedef);
972 If there is a node at top and it doesn't have sunlight,
973 there has not been any sunlight going down.
975 Otherwise there probably is.
978 bool is_valid_position;
979 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
981 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
982 node_under_sunlight = false;
985 Remove all light that has come out of this node
988 enum LightBank banks[] =
993 for(s32 i=0; i<2; i++)
995 enum LightBank bank = banks[i];
997 u8 lightwas = getNodeNoEx(p).getLight(bank, ndef);
999 // Add the block of the added node to modified_blocks
1000 v3s16 blockpos = getNodeBlockPos(p);
1001 MapBlock * block = getBlockNoCreate(blockpos);
1002 assert(block != NULL);
1003 modified_blocks[blockpos] = block;
1005 assert(isValidPosition(p));
1007 // Unlight neighbours of node.
1008 // This means setting light of all consequent dimmer nodes
1010 // This also collects the nodes at the border which will spread
1011 // light again into this.
1012 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1014 n.setLight(bank, 0, ndef);
1018 If node lets sunlight through and is under sunlight, it has
1021 if(node_under_sunlight && ndef->get(n).sunlight_propagates)
1023 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
1027 Remove node metadata
1029 if (remove_metadata) {
1030 removeNodeMetadata(p);
1034 Set the node on the map
1040 If node is under sunlight and doesn't let sunlight through,
1041 take all sunlighted nodes under it and clear light from them
1042 and from where the light has been spread.
1043 TODO: This could be optimized by mass-unlighting instead
1046 if(node_under_sunlight && !ndef->get(n).sunlight_propagates)
1050 //m_dout<<DTIME<<"y="<<y<<std::endl;
1051 v3s16 n2pos(p.X, y, p.Z);
1055 n2 = getNodeNoEx(n2pos, &is_valid_position);
1056 if (!is_valid_position)
1059 if(n2.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN)
1061 unLightNeighbors(LIGHTBANK_DAY,
1062 n2pos, n2.getLight(LIGHTBANK_DAY, ndef),
1063 light_sources, modified_blocks);
1064 n2.setLight(LIGHTBANK_DAY, 0, ndef);
1072 for(s32 i=0; i<2; i++)
1074 enum LightBank bank = banks[i];
1077 Spread light from all nodes that might be capable of doing so
1079 spreadLight(bank, light_sources, modified_blocks);
1083 Update information about whether day and night light differ
1085 for(std::map<v3s16, MapBlock*>::iterator
1086 i = modified_blocks.begin();
1087 i != modified_blocks.end(); ++i)
1089 i->second->expireDayNightDiff();
1095 if(m_gamedef->rollback())
1097 RollbackNode rollback_newnode(this, p, m_gamedef);
1098 RollbackAction action;
1099 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1100 m_gamedef->rollback()->reportAction(action);
1104 Add neighboring liquid nodes and the node itself if it is
1105 liquid (=water node was added) to transform queue.
1108 v3s16(0,0,0), // self
1109 v3s16(0,0,1), // back
1110 v3s16(0,1,0), // top
1111 v3s16(1,0,0), // right
1112 v3s16(0,0,-1), // front
1113 v3s16(0,-1,0), // bottom
1114 v3s16(-1,0,0), // left
1116 for(u16 i=0; i<7; i++)
1118 v3s16 p2 = p + dirs[i];
1120 MapNode n2 = getNodeNoEx(p2, &is_valid_position);
1121 if(is_valid_position
1122 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1124 m_transforming_liquid.push_back(p2);
1131 void Map::removeNodeAndUpdate(v3s16 p,
1132 std::map<v3s16, MapBlock*> &modified_blocks)
1134 INodeDefManager *ndef = m_gamedef->ndef();
1136 /*PrintInfo(m_dout);
1137 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1138 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1140 bool node_under_sunlight = true;
1142 v3s16 toppos = p + v3s16(0,1,0);
1144 // Node will be replaced with this
1145 content_t replace_material = CONTENT_AIR;
1148 Collect old node for rollback
1150 RollbackNode rollback_oldnode(this, p, m_gamedef);
1153 If there is a node at top and it doesn't have sunlight,
1154 there will be no sunlight going down.
1156 bool is_valid_position;
1157 MapNode topnode = getNodeNoEx(toppos, &is_valid_position);
1159 if(is_valid_position && topnode.getLight(LIGHTBANK_DAY, ndef) != LIGHT_SUN)
1160 node_under_sunlight = false;
1162 std::set<v3s16> light_sources;
1164 enum LightBank banks[] =
1169 for(s32 i=0; i<2; i++)
1171 enum LightBank bank = banks[i];
1174 Unlight neighbors (in case the node is a light source)
1176 unLightNeighbors(bank, p,
1177 getNodeNoEx(p).getLight(bank, ndef),
1178 light_sources, modified_blocks);
1182 Remove node metadata
1185 removeNodeMetadata(p);
1189 This also clears the lighting.
1193 n.setContent(replace_material);
1196 for(s32 i=0; i<2; i++)
1198 enum LightBank bank = banks[i];
1201 Recalculate lighting
1203 spreadLight(bank, light_sources, modified_blocks);
1206 // Add the block of the removed node to modified_blocks
1207 v3s16 blockpos = getNodeBlockPos(p);
1208 MapBlock * block = getBlockNoCreate(blockpos);
1209 assert(block != NULL);
1210 modified_blocks[blockpos] = block;
1213 If the removed node was under sunlight, propagate the
1214 sunlight down from it and then light all neighbors
1215 of the propagated blocks.
1217 if(node_under_sunlight)
1219 s16 ybottom = propagateSunlight(p, modified_blocks);
1220 /*m_dout<<DTIME<<"Node was under sunlight. "
1221 "Propagating sunlight";
1222 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1224 for(; y >= ybottom; y--)
1226 v3s16 p2(p.X, y, p.Z);
1227 /*m_dout<<DTIME<<"lighting neighbors of node ("
1228 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1230 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1235 // Set the lighting of this node to 0
1236 // TODO: Is this needed? Lighting is cleared up there already.
1237 MapNode n = getNodeNoEx(p, &is_valid_position);
1238 if (is_valid_position) {
1239 n.setLight(LIGHTBANK_DAY, 0, ndef);
1246 for(s32 i=0; i<2; i++)
1248 enum LightBank bank = banks[i];
1250 // Get the brightest neighbour node and propagate light from it
1251 v3s16 n2p = getBrightestNeighbour(bank, p);
1253 //MapNode n2 = getNode(n2p);
1254 lightNeighbors(bank, n2p, modified_blocks);
1256 catch(InvalidPositionException &e)
1262 Update information about whether day and night light differ
1264 for(std::map<v3s16, MapBlock*>::iterator
1265 i = modified_blocks.begin();
1266 i != modified_blocks.end(); ++i)
1268 i->second->expireDayNightDiff();
1274 if(m_gamedef->rollback())
1276 RollbackNode rollback_newnode(this, p, m_gamedef);
1277 RollbackAction action;
1278 action.setSetNode(p, rollback_oldnode, rollback_newnode);
1279 m_gamedef->rollback()->reportAction(action);
1283 Add neighboring liquid nodes and this node to transform queue.
1284 (it's vital for the node itself to get updated last.)
1287 v3s16(0,0,1), // back
1288 v3s16(0,1,0), // top
1289 v3s16(1,0,0), // right
1290 v3s16(0,0,-1), // front
1291 v3s16(0,-1,0), // bottom
1292 v3s16(-1,0,0), // left
1293 v3s16(0,0,0), // self
1295 for(u16 i=0; i<7; i++)
1297 v3s16 p2 = p + dirs[i];
1299 bool is_position_valid;
1300 MapNode n2 = getNodeNoEx(p2, &is_position_valid);
1301 if (is_position_valid
1302 && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR))
1304 m_transforming_liquid.push_back(p2);
1309 bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
1312 event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
1316 bool succeeded = true;
1318 std::map<v3s16, MapBlock*> modified_blocks;
1319 addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
1321 // Copy modified_blocks to event
1322 for(std::map<v3s16, MapBlock*>::iterator
1323 i = modified_blocks.begin();
1324 i != modified_blocks.end(); ++i)
1326 event.modified_blocks.insert(i->first);
1329 catch(InvalidPositionException &e){
1333 dispatchEvent(&event);
1338 bool Map::removeNodeWithEvent(v3s16 p)
1341 event.type = MEET_REMOVENODE;
1344 bool succeeded = true;
1346 std::map<v3s16, MapBlock*> modified_blocks;
1347 removeNodeAndUpdate(p, modified_blocks);
1349 // Copy modified_blocks to event
1350 for(std::map<v3s16, MapBlock*>::iterator
1351 i = modified_blocks.begin();
1352 i != modified_blocks.end(); ++i)
1354 event.modified_blocks.insert(i->first);
1357 catch(InvalidPositionException &e){
1361 dispatchEvent(&event);
1366 bool Map::getDayNightDiff(v3s16 blockpos)
1369 v3s16 p = blockpos + v3s16(0,0,0);
1370 MapBlock *b = getBlockNoCreate(p);
1371 if(b->getDayNightDiff())
1374 catch(InvalidPositionException &e){}
1377 v3s16 p = blockpos + v3s16(-1,0,0);
1378 MapBlock *b = getBlockNoCreate(p);
1379 if(b->getDayNightDiff())
1382 catch(InvalidPositionException &e){}
1384 v3s16 p = blockpos + v3s16(0,-1,0);
1385 MapBlock *b = getBlockNoCreate(p);
1386 if(b->getDayNightDiff())
1389 catch(InvalidPositionException &e){}
1391 v3s16 p = blockpos + v3s16(0,0,-1);
1392 MapBlock *b = getBlockNoCreate(p);
1393 if(b->getDayNightDiff())
1396 catch(InvalidPositionException &e){}
1399 v3s16 p = blockpos + v3s16(1,0,0);
1400 MapBlock *b = getBlockNoCreate(p);
1401 if(b->getDayNightDiff())
1404 catch(InvalidPositionException &e){}
1406 v3s16 p = blockpos + v3s16(0,1,0);
1407 MapBlock *b = getBlockNoCreate(p);
1408 if(b->getDayNightDiff())
1411 catch(InvalidPositionException &e){}
1413 v3s16 p = blockpos + v3s16(0,0,1);
1414 MapBlock *b = getBlockNoCreate(p);
1415 if(b->getDayNightDiff())
1418 catch(InvalidPositionException &e){}
1424 Updates usage timers
1426 void Map::timerUpdate(float dtime, float unload_timeout,
1427 std::list<v3s16> *unloaded_blocks)
1429 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1431 // Profile modified reasons
1432 Profiler modprofiler;
1434 std::list<v2s16> sector_deletion_queue;
1435 u32 deleted_blocks_count = 0;
1436 u32 saved_blocks_count = 0;
1437 u32 block_count_all = 0;
1440 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
1441 si != m_sectors.end(); ++si)
1443 MapSector *sector = si->second;
1445 bool all_blocks_deleted = true;
1447 std::list<MapBlock*> blocks;
1448 sector->getBlocks(blocks);
1450 for(std::list<MapBlock*>::iterator i = blocks.begin();
1451 i != blocks.end(); ++i)
1453 MapBlock *block = (*i);
1455 block->incrementUsageTimer(dtime);
1457 if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
1459 v3s16 p = block->getPos();
1462 if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading)
1464 modprofiler.add(block->getModifiedReason(), 1);
1465 if (!saveBlock(block))
1467 saved_blocks_count++;
1470 // Delete from memory
1471 sector->deleteBlock(block);
1474 unloaded_blocks->push_back(p);
1476 deleted_blocks_count++;
1480 all_blocks_deleted = false;
1485 if(all_blocks_deleted)
1487 sector_deletion_queue.push_back(si->first);
1492 // Finally delete the empty sectors
1493 deleteSectors(sector_deletion_queue);
1495 if(deleted_blocks_count != 0)
1497 PrintInfo(infostream); // ServerMap/ClientMap:
1498 infostream<<"Unloaded "<<deleted_blocks_count
1499 <<" blocks from memory";
1500 if(save_before_unloading)
1501 infostream<<", of which "<<saved_blocks_count<<" were written";
1502 infostream<<", "<<block_count_all<<" blocks in memory";
1503 infostream<<"."<<std::endl;
1504 if(saved_blocks_count != 0){
1505 PrintInfo(infostream); // ServerMap/ClientMap:
1506 infostream<<"Blocks modified by: "<<std::endl;
1507 modprofiler.print(infostream);
1512 void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
1514 timerUpdate(0.0, -1.0, unloaded_blocks);
1517 void Map::deleteSectors(std::list<v2s16> &list)
1519 for(std::list<v2s16>::iterator j = list.begin();
1520 j != list.end(); ++j)
1522 MapSector *sector = m_sectors[*j];
1523 // If sector is in sector cache, remove it from there
1524 if(m_sector_cache == sector)
1525 m_sector_cache = NULL;
1526 // Remove from map and delete
1527 m_sectors.erase(*j);
1533 void Map::unloadUnusedData(float timeout,
1534 core::list<v3s16> *deleted_blocks)
1536 core::list<v2s16> sector_deletion_queue;
1537 u32 deleted_blocks_count = 0;
1538 u32 saved_blocks_count = 0;
1540 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1541 for(; si.atEnd() == false; si++)
1543 MapSector *sector = si.getNode()->getValue();
1545 bool all_blocks_deleted = true;
1547 core::list<MapBlock*> blocks;
1548 sector->getBlocks(blocks);
1549 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1550 i != blocks.end(); i++)
1552 MapBlock *block = (*i);
1554 if(block->getUsageTimer() > timeout)
1557 if(block->getModified() != MOD_STATE_CLEAN)
1560 saved_blocks_count++;
1562 // Delete from memory
1563 sector->deleteBlock(block);
1564 deleted_blocks_count++;
1568 all_blocks_deleted = false;
1572 if(all_blocks_deleted)
1574 sector_deletion_queue.push_back(si.getNode()->getKey());
1578 deleteSectors(sector_deletion_queue);
1580 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1581 <<", of which "<<saved_blocks_count<<" were wr."
1584 //return sector_deletion_queue.getSize();
1585 //return deleted_blocks_count;
1589 void Map::PrintInfo(std::ostream &out)
1594 #define WATER_DROP_BOOST 4
1598 NEIGHBOR_SAME_LEVEL,
1601 struct NodeNeighbor {
1605 bool l; //can liquid
1608 void Map::transforming_liquid_add(v3s16 p) {
1609 m_transforming_liquid.push_back(p);
1612 s32 Map::transforming_liquid_size() {
1613 return m_transforming_liquid.size();
1616 void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
1619 INodeDefManager *nodemgr = m_gamedef->ndef();
1621 DSTACK(__FUNCTION_NAME);
1622 //TimeTaker timer("transformLiquids()");
1625 u32 initial_size = m_transforming_liquid.size();
1627 /*if(initial_size != 0)
1628 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1630 // list of nodes that due to viscosity have not reached their max level height
1631 UniqueQueue<v3s16> must_reflow;
1633 // List of MapBlocks that will require a lighting update (due to lava)
1634 std::map<v3s16, MapBlock*> lighting_modified_blocks;
1636 u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
1637 u32 loop_max = liquid_loop_max;
1641 /* If liquid_loop_max is not keeping up with the queue size increase
1642 * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
1644 if (m_transforming_liquid.size() > loop_max * 2) {
1646 float server_step = g_settings->getFloat("dedicated_server_step");
1647 if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
1648 m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
1650 m_transforming_liquid_loop_count_multiplier = 1.0;
1653 loop_max *= m_transforming_liquid_loop_count_multiplier;
1656 while(m_transforming_liquid.size() != 0)
1658 // This should be done here so that it is done when continue is used
1659 if(loopcount >= initial_size || loopcount >= loop_max)
1664 Get a queued transforming liquid node
1666 v3s16 p0 = m_transforming_liquid.pop_front();
1668 MapNode n0 = getNodeNoEx(p0);
1671 Collect information about current node
1673 s8 liquid_level = -1;
1674 content_t liquid_kind = CONTENT_IGNORE;
1675 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1676 switch (liquid_type) {
1678 liquid_level = LIQUID_LEVEL_SOURCE;
1679 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1681 case LIQUID_FLOWING:
1682 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1683 liquid_kind = n0.getContent();
1686 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1687 // continue with the next node.
1688 if (n0.getContent() != CONTENT_AIR)
1690 liquid_kind = CONTENT_AIR;
1695 Collect information about the environment
1697 const v3s16 *dirs = g_6dirs;
1698 NodeNeighbor sources[6]; // surrounding sources
1699 int num_sources = 0;
1700 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1702 NodeNeighbor airs[6]; // surrounding air
1704 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1705 int num_neutrals = 0;
1706 bool flowing_down = false;
1707 for (u16 i = 0; i < 6; i++) {
1708 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1711 nt = NEIGHBOR_UPPER;
1714 nt = NEIGHBOR_LOWER;
1717 v3s16 npos = p0 + dirs[i];
1718 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1719 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1721 if (nb.n.getContent() == CONTENT_AIR) {
1722 airs[num_airs++] = nb;
1723 // if the current node is a water source the neighbor
1724 // should be enqueded for transformation regardless of whether the
1725 // current node changes or not.
1726 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1727 m_transforming_liquid.push_back(npos);
1728 // if the current node happens to be a flowing node, it will start to flow down here.
1729 if (nb.t == NEIGHBOR_LOWER) {
1730 flowing_down = true;
1733 neutrals[num_neutrals++] = nb;
1737 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1738 if (liquid_kind == CONTENT_AIR)
1739 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1740 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1741 neutrals[num_neutrals++] = nb;
1743 // Do not count bottom source, it will screw things up
1745 sources[num_sources++] = nb;
1748 case LIQUID_FLOWING:
1749 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1750 if (liquid_kind == CONTENT_AIR)
1751 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1752 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1753 neutrals[num_neutrals++] = nb;
1755 flows[num_flows++] = nb;
1756 if (nb.t == NEIGHBOR_LOWER)
1757 flowing_down = true;
1764 decide on the type (and possibly level) of the current node
1766 content_t new_node_content;
1767 s8 new_node_level = -1;
1768 s8 max_node_level = -1;
1769 u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
1770 if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
1771 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1772 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1773 // it's perfectly safe to use liquid_kind here to determine the new node content.
1774 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1775 } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
1776 // liquid_kind is set properly, see above
1777 new_node_content = liquid_kind;
1778 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1779 if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
1780 new_node_content = CONTENT_AIR;
1782 // no surrounding sources, so get the maximum level that can flow into this node
1783 for (u16 i = 0; i < num_flows; i++) {
1784 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1785 switch (flows[i].t) {
1786 case NEIGHBOR_UPPER:
1787 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1788 max_node_level = LIQUID_LEVEL_MAX;
1789 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1790 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1791 } else if (nb_liquid_level > max_node_level)
1792 max_node_level = nb_liquid_level;
1794 case NEIGHBOR_LOWER:
1796 case NEIGHBOR_SAME_LEVEL:
1797 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1798 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1799 max_node_level = nb_liquid_level - 1;
1805 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1806 if (viscosity > 1 && max_node_level != liquid_level) {
1807 // amount to gain, limited by viscosity
1808 // must be at least 1 in absolute value
1809 s8 level_inc = max_node_level - liquid_level;
1810 if (level_inc < -viscosity || level_inc > viscosity)
1811 new_node_level = liquid_level + level_inc/viscosity;
1812 else if (level_inc < 0)
1813 new_node_level = liquid_level - 1;
1814 else if (level_inc > 0)
1815 new_node_level = liquid_level + 1;
1816 if (new_node_level != max_node_level)
1817 must_reflow.push_back(p0);
1819 new_node_level = max_node_level;
1821 if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
1822 new_node_content = liquid_kind;
1824 new_node_content = CONTENT_AIR;
1829 check if anything has changed. if not, just continue with the next node.
1831 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1832 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1833 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1839 update the current node
1842 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1843 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1844 // set level to last 3 bits, flowing down bit to 4th bit
1845 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1847 // set the liquid level and flow bit to 0
1848 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1850 n0.setContent(new_node_content);
1852 // Find out whether there is a suspect for this action
1853 std::string suspect;
1854 if(m_gamedef->rollback()){
1855 suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
1858 if(!suspect.empty()){
1860 RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
1861 // Get old node for rollback
1862 RollbackNode rollback_oldnode(this, p0, m_gamedef);
1866 RollbackNode rollback_newnode(this, p0, m_gamedef);
1867 RollbackAction action;
1868 action.setSetNode(p0, rollback_oldnode, rollback_newnode);
1869 m_gamedef->rollback()->reportAction(action);
1875 v3s16 blockpos = getNodeBlockPos(p0);
1876 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1878 modified_blocks[blockpos] = block;
1879 // If new or old node emits light, MapBlock requires lighting update
1880 if(nodemgr->get(n0).light_source != 0 ||
1881 nodemgr->get(n00).light_source != 0)
1882 lighting_modified_blocks[block->getPos()] = block;
1886 enqueue neighbors for update if neccessary
1888 switch (nodemgr->get(n0.getContent()).liquid_type) {
1890 case LIQUID_FLOWING:
1891 // make sure source flows into all neighboring nodes
1892 for (u16 i = 0; i < num_flows; i++)
1893 if (flows[i].t != NEIGHBOR_UPPER)
1894 m_transforming_liquid.push_back(flows[i].p);
1895 for (u16 i = 0; i < num_airs; i++)
1896 if (airs[i].t != NEIGHBOR_UPPER)
1897 m_transforming_liquid.push_back(airs[i].p);
1900 // this flow has turned to air; neighboring flows might need to do the same
1901 for (u16 i = 0; i < num_flows; i++)
1902 m_transforming_liquid.push_back(flows[i].p);
1906 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1907 while (must_reflow.size() > 0)
1908 m_transforming_liquid.push_back(must_reflow.pop_front());
1909 updateLighting(lighting_modified_blocks, modified_blocks);
1912 /* ----------------------------------------------------------------------
1913 * Manage the queue so that it does not grow indefinately
1915 u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
1917 if (time_until_purge == 0)
1918 return; // Feature disabled
1920 time_until_purge *= 1000; // seconds -> milliseconds
1922 u32 curr_time = getTime(PRECISION_MILLI);
1923 u32 prev_unprocessed = m_unprocessed_count;
1924 m_unprocessed_count = m_transforming_liquid.size();
1926 // if unprocessed block count is decreasing or stable
1927 if (m_unprocessed_count <= prev_unprocessed) {
1928 m_queue_size_timer_started = false;
1930 if (!m_queue_size_timer_started)
1931 m_inc_trending_up_start_time = curr_time;
1932 m_queue_size_timer_started = true;
1935 // Account for curr_time overflowing
1936 if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
1937 m_queue_size_timer_started = false;
1939 /* If the queue has been growing for more than liquid_queue_purge_time seconds
1940 * and the number of unprocessed blocks is still > liquid_loop_max then we
1941 * cannot keep up; dump the oldest blocks from the queue so that the queue
1942 * has liquid_loop_max items in it
1944 if (m_queue_size_timer_started
1945 && curr_time - m_inc_trending_up_start_time > time_until_purge
1946 && m_unprocessed_count > liquid_loop_max) {
1948 size_t dump_qty = m_unprocessed_count - liquid_loop_max;
1950 infostream << "transformLiquids(): DUMPING " << dump_qty
1951 << " blocks from the queue" << std::endl;
1954 m_transforming_liquid.pop_front();
1956 m_queue_size_timer_started = false; // optimistically assume we can keep up now
1957 m_unprocessed_count = m_transforming_liquid.size();
1961 NodeMetadata *Map::getNodeMetadata(v3s16 p)
1963 v3s16 blockpos = getNodeBlockPos(p);
1964 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1965 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1967 infostream<<"Map::getNodeMetadata(): Need to emerge "
1968 <<PP(blockpos)<<std::endl;
1969 block = emergeBlock(blockpos, false);
1972 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1976 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1980 bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1982 v3s16 blockpos = getNodeBlockPos(p);
1983 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1984 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1986 infostream<<"Map::setNodeMetadata(): Need to emerge "
1987 <<PP(blockpos)<<std::endl;
1988 block = emergeBlock(blockpos, false);
1991 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1995 block->m_node_metadata.set(p_rel, meta);
1999 void Map::removeNodeMetadata(v3s16 p)
2001 v3s16 blockpos = getNodeBlockPos(p);
2002 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2003 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2006 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
2010 block->m_node_metadata.remove(p_rel);
2013 NodeTimer Map::getNodeTimer(v3s16 p)
2015 v3s16 blockpos = getNodeBlockPos(p);
2016 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2017 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2019 infostream<<"Map::getNodeTimer(): Need to emerge "
2020 <<PP(blockpos)<<std::endl;
2021 block = emergeBlock(blockpos, false);
2024 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
2028 NodeTimer t = block->m_node_timers.get(p_rel);
2032 void Map::setNodeTimer(v3s16 p, NodeTimer t)
2034 v3s16 blockpos = getNodeBlockPos(p);
2035 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2036 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2038 infostream<<"Map::setNodeTimer(): Need to emerge "
2039 <<PP(blockpos)<<std::endl;
2040 block = emergeBlock(blockpos, false);
2043 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
2047 block->m_node_timers.set(p_rel, t);
2050 void Map::removeNodeTimer(v3s16 p)
2052 v3s16 blockpos = getNodeBlockPos(p);
2053 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
2054 MapBlock *block = getBlockNoCreateNoEx(blockpos);
2057 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
2061 block->m_node_timers.remove(p_rel);
2067 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
2068 Map(dout_server, gamedef),
2070 m_map_metadata_changed(true)
2072 verbosestream<<__FUNCTION_NAME<<std::endl;
2075 Try to load map; if not found, create a new one.
2078 // Determine which database backend to use
2079 std::string conf_path = savedir + DIR_DELIM + "world.mt";
2081 bool succeeded = conf.readConfigFile(conf_path.c_str());
2082 if (!succeeded || !conf.exists("backend")) {
2083 // fall back to sqlite3
2084 dbase = new Database_SQLite3(this, savedir);
2085 conf.set("backend", "sqlite3");
2087 std::string backend = conf.get("backend");
2088 if (backend == "dummy")
2089 dbase = new Database_Dummy(this);
2090 else if (backend == "sqlite3")
2091 dbase = new Database_SQLite3(this, savedir);
2093 else if (backend == "leveldb")
2094 dbase = new Database_LevelDB(this, savedir);
2097 else if (backend == "redis")
2098 dbase = new Database_Redis(this, savedir);
2101 throw BaseException("Unknown map backend");
2104 m_savedir = savedir;
2105 m_map_saving_enabled = false;
2109 // If directory exists, check contents and load if possible
2110 if(fs::PathExists(m_savedir))
2112 // If directory is empty, it is safe to save into it.
2113 if(fs::GetDirListing(m_savedir).size() == 0)
2115 infostream<<"ServerMap: Empty save directory is valid."
2117 m_map_saving_enabled = true;
2122 // Load map metadata (seed, chunksize)
2125 catch(SettingNotFoundException &e){
2126 infostream<<"ServerMap: Some metadata not found."
2127 <<" Using default settings."<<std::endl;
2129 catch(FileNotGoodException &e){
2130 infostream<<"WARNING: Could not load map metadata"
2131 //<<" Disabling chunk-based generator."
2136 infostream<<"ServerMap: Successfully loaded map "
2137 <<"metadata from "<<savedir
2138 <<", assuming valid save directory."
2139 <<" seed="<< m_emerge->params.seed <<"."
2142 m_map_saving_enabled = true;
2143 // Map loaded, not creating new one
2147 // If directory doesn't exist, it is safe to save to it
2149 m_map_saving_enabled = true;
2152 catch(std::exception &e)
2154 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2155 <<", exception: "<<e.what()<<std::endl;
2156 infostream<<"Please remove the map or fix it."<<std::endl;
2157 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2160 infostream<<"Initializing new map."<<std::endl;
2162 // Create zero sector
2163 emergeSector(v2s16(0,0));
2165 // Initially write whole map
2166 save(MOD_STATE_CLEAN);
2169 ServerMap::~ServerMap()
2171 verbosestream<<__FUNCTION_NAME<<std::endl;
2175 if(m_map_saving_enabled)
2177 // Save only changed parts
2178 save(MOD_STATE_WRITE_AT_UNLOAD);
2179 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2183 infostream<<"ServerMap: Map not saved"<<std::endl;
2186 catch(std::exception &e)
2188 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2189 <<", exception: "<<e.what()<<std::endl;
2193 Close database if it was opened
2201 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2202 for(; i.atEnd() == false; i++)
2204 MapChunk *chunk = i.getNode()->getValue();
2210 u64 ServerMap::getSeed()
2212 return m_emerge->params.seed;
2215 s16 ServerMap::getWaterLevel()
2217 return m_emerge->params.water_level;
2220 bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
2222 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2223 EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos));
2225 s16 chunksize = m_emerge->params.chunksize;
2226 s16 coffset = -chunksize / 2;
2227 v3s16 chunk_offset(coffset, coffset, coffset);
2228 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2229 v3s16 blockpos_min = blockpos_div * chunksize;
2230 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2231 blockpos_min += chunk_offset;
2232 blockpos_max += chunk_offset;
2234 v3s16 extra_borders(1,1,1);
2236 // Do nothing if not inside limits (+-1 because of neighbors)
2237 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2238 blockpos_over_limit(blockpos_max + extra_borders))
2241 data->seed = m_emerge->params.seed;
2242 data->blockpos_min = blockpos_min;
2243 data->blockpos_max = blockpos_max;
2244 data->blockpos_requested = blockpos;
2245 data->nodedef = m_gamedef->ndef();
2248 Create the whole area of this and the neighboring blocks
2251 //TimeTaker timer("initBlockMake() create area");
2253 for(s16 x=blockpos_min.X-extra_borders.X;
2254 x<=blockpos_max.X+extra_borders.X; x++)
2255 for(s16 z=blockpos_min.Z-extra_borders.Z;
2256 z<=blockpos_max.Z+extra_borders.Z; z++)
2258 v2s16 sectorpos(x, z);
2259 // Sector metadata is loaded from disk if not already loaded.
2260 ServerMapSector *sector = createSector(sectorpos);
2264 for(s16 y=blockpos_min.Y-extra_borders.Y;
2265 y<=blockpos_max.Y+extra_borders.Y; y++)
2268 //MapBlock *block = createBlock(p);
2269 // 1) get from memory, 2) load from disk
2270 MapBlock *block = emergeBlock(p, false);
2271 // 3) create a blank one
2274 block = createBlock(p);
2277 Block gets sunlight if this is true.
2279 Refer to the map generator heuristics.
2281 bool ug = m_emerge->isBlockUnderground(p);
2282 block->setIsUnderground(ug);
2285 // Lighting will not be valid after make_chunk is called
2286 block->setLightingExpired(true);
2287 // Lighting will be calculated
2288 //block->setLightingExpired(false);
2294 Now we have a big empty area.
2296 Make a ManualMapVoxelManipulator that contains this and the
2300 // The area that contains this block and it's neighbors
2301 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2302 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2304 data->vmanip = new ManualMapVoxelManipulator(this);
2305 //data->vmanip->setMap(this);
2309 //TimeTaker timer("initBlockMake() initialEmerge");
2310 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max, false);
2313 // Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2314 /* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2315 for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
2316 for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
2317 core::map<v3s16, u8>::Node *n;
2318 n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
2321 u8 flags = n->getValue();
2322 flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
2328 // Data is ready now.
2332 void ServerMap::finishBlockMake(BlockMakeData *data,
2333 std::map<v3s16, MapBlock*> &changed_blocks)
2335 v3s16 blockpos_min = data->blockpos_min;
2336 v3s16 blockpos_max = data->blockpos_max;
2337 v3s16 blockpos_requested = data->blockpos_requested;
2338 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2339 <<blockpos_requested.Y<<","
2340 <<blockpos_requested.Z<<")"<<std::endl;*/
2342 v3s16 extra_borders(1,1,1);
2344 bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info;
2346 /*infostream<<"Resulting vmanip:"<<std::endl;
2347 data->vmanip.print(infostream);*/
2349 // Make sure affected blocks are loaded
2350 for(s16 x=blockpos_min.X-extra_borders.X;
2351 x<=blockpos_max.X+extra_borders.X; x++)
2352 for(s16 z=blockpos_min.Z-extra_borders.Z;
2353 z<=blockpos_max.Z+extra_borders.Z; z++)
2354 for(s16 y=blockpos_min.Y-extra_borders.Y;
2355 y<=blockpos_max.Y+extra_borders.Y; y++)
2358 // Load from disk if not already in memory
2359 emergeBlock(p, false);
2363 Blit generated stuff to map
2364 NOTE: blitBackAll adds nearly everything to changed_blocks
2368 //TimeTaker timer("finishBlockMake() blitBackAll");
2369 data->vmanip->blitBackAll(&changed_blocks);
2372 EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()=" << changed_blocks.size());
2375 Copy transforming liquid information
2377 while(data->transforming_liquid.size() > 0)
2379 v3s16 p = data->transforming_liquid.pop_front();
2380 m_transforming_liquid.push_back(p);
2384 Do stuff in central blocks
2392 TimeTaker t("finishBlockMake lighting update");
2394 core::map<v3s16, MapBlock*> lighting_update_blocks;
2397 for(s16 x=blockpos_min.X-extra_borders.X;
2398 x<=blockpos_max.X+extra_borders.X; x++)
2399 for(s16 z=blockpos_min.Z-extra_borders.Z;
2400 z<=blockpos_max.Z+extra_borders.Z; z++)
2401 for(s16 y=blockpos_min.Y-extra_borders.Y;
2402 y<=blockpos_max.Y+extra_borders.Y; y++)
2405 MapBlock *block = getBlockNoCreateNoEx(p);
2407 lighting_update_blocks.insert(block->getPos(), block);
2410 updateLighting(lighting_update_blocks, changed_blocks);
2414 Set lighting to non-expired state in all of them.
2415 This is cheating, but it is not fast enough if all of them
2416 would actually be updated.
2418 for(s16 x=blockpos_min.X-extra_borders.X;
2419 x<=blockpos_max.X+extra_borders.X; x++)
2420 for(s16 z=blockpos_min.Z-extra_borders.Z;
2421 z<=blockpos_max.Z+extra_borders.Z; z++)
2422 for(s16 y=blockpos_min.Y-extra_borders.Y;
2423 y<=blockpos_max.Y+extra_borders.Y; y++)
2426 MapBlock * block = getBlockNoCreateNoEx(p);
2428 block->setLightingExpired(false);
2432 if(enable_mapgen_debug_info == false)
2433 t.stop(true); // Hide output
2438 Go through changed blocks
2440 for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin();
2441 i != changed_blocks.end(); ++i)
2443 MapBlock *block = i->second;
2447 Update day/night difference cache of the MapBlocks
2449 block->expireDayNightDiff();
2451 Set block as modified
2453 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2454 "finishBlockMake expireDayNightDiff");
2458 Set central blocks as generated
2460 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2461 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2462 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2465 MapBlock *block = getBlockNoCreateNoEx(p);
2468 block->setGenerated(true);
2472 Save changed parts of map
2473 NOTE: Will be saved later.
2475 //save(MOD_STATE_WRITE_AT_UNLOAD);
2477 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2478 <<","<<blockpos_requested.Y<<","
2479 <<blockpos_requested.Z<<")"<<std::endl;*/
2483 if(enable_mapgen_debug_info)
2486 Analyze resulting blocks
2488 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2489 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2490 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2491 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2492 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2493 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2495 v3s16 p = v3s16(x,y,z);
2496 MapBlock *block = getBlockNoCreateNoEx(p);
2498 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2499 infostream<<"Generated "<<spos<<": "
2500 <<analyze_block(block)<<std::endl;
2505 getBlockNoCreateNoEx(blockpos_requested);
2508 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2510 DSTACKF("%s: p2d=(%d,%d)",
2515 Check if it exists already in memory
2517 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2522 Try to load it from disk (with blocks)
2524 //if(loadSectorFull(p2d) == true)
2527 Try to load metadata from disk
2530 if(loadSectorMeta(p2d) == true)
2532 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2535 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2536 throw InvalidPositionException("");
2542 Do not create over-limit
2544 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2545 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2546 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2547 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2548 throw InvalidPositionException("createSector(): pos. over limit");
2551 Generate blank sector
2554 sector = new ServerMapSector(this, p2d, m_gamedef);
2556 // Sector position on map in nodes
2557 //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2562 m_sectors[p2d] = sector;
2569 This is a quick-hand function for calling makeBlock().
2571 MapBlock * ServerMap::generateBlock(
2573 std::map<v3s16, MapBlock*> &modified_blocks
2576 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2578 /*infostream<<"generateBlock(): "
2579 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2582 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2584 TimeTaker timer("generateBlock");
2586 //MapBlock *block = original_dummy;
2588 v2s16 p2d(p.X, p.Z);
2589 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2592 Do not generate over-limit
2594 if(blockpos_over_limit(p))
2596 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2597 throw InvalidPositionException("generateBlock(): pos. over limit");
2601 Create block make data
2604 initBlockMake(&data, p);
2610 TimeTaker t("mapgen::make_block()");
2611 mapgen->makeChunk(&data);
2612 //mapgen::make_block(&data);
2614 if(enable_mapgen_debug_info == false)
2615 t.stop(true); // Hide output
2619 Blit data back on map, update lighting, add mobs and whatever this does
2621 finishBlockMake(&data, modified_blocks);
2626 MapBlock *block = getBlockNoCreateNoEx(p);
2634 bool erroneus_content = false;
2635 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2636 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2637 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2640 MapNode n = block->getNode(p);
2641 if(n.getContent() == CONTENT_IGNORE)
2643 infostream<<"CONTENT_IGNORE at "
2644 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2646 erroneus_content = true;
2650 if(erroneus_content)
2659 Generate a completely empty block
2663 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2664 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2666 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2669 n.setContent(CONTENT_AIR);
2670 block->setNode(v3s16(x0,y0,z0), n);
2676 if(enable_mapgen_debug_info == false)
2677 timer.stop(true); // Hide output
2683 MapBlock * ServerMap::createBlock(v3s16 p)
2685 DSTACKF("%s: p=(%d,%d,%d)",
2686 __FUNCTION_NAME, p.X, p.Y, p.Z);
2689 Do not create over-limit
2691 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2692 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2693 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2694 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2695 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2696 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2697 throw InvalidPositionException("createBlock(): pos. over limit");
2699 v2s16 p2d(p.X, p.Z);
2702 This will create or load a sector if not found in memory.
2703 If block exists on disk, it will be loaded.
2705 NOTE: On old save formats, this will be slow, as it generates
2706 lighting on blocks for them.
2708 ServerMapSector *sector;
2710 sector = (ServerMapSector*)createSector(p2d);
2711 assert(sector->getId() == MAPSECTOR_SERVER);
2713 catch(InvalidPositionException &e)
2715 infostream<<"createBlock: createSector() failed"<<std::endl;
2719 NOTE: This should not be done, or at least the exception
2720 should not be passed on as std::exception, because it
2721 won't be catched at all.
2723 /*catch(std::exception &e)
2725 infostream<<"createBlock: createSector() failed: "
2726 <<e.what()<<std::endl;
2731 Try to get a block from the sector
2734 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2737 if(block->isDummy())
2742 block = sector->createBlankBlock(block_y);
2747 MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
2749 DSTACKF("%s: p=(%d,%d,%d), create_blank=%d",
2751 p.X, p.Y, p.Z, create_blank);
2754 MapBlock *block = getBlockNoCreateNoEx(p);
2755 if(block && block->isDummy() == false)
2760 MapBlock *block = loadBlock(p);
2766 ServerMapSector *sector = createSector(v2s16(p.X, p.Z));
2767 MapBlock *block = sector->createBlankBlock(p.Y);
2775 std::map<v3s16, MapBlock*> modified_blocks;
2776 MapBlock *block = generateBlock(p, modified_blocks);
2780 event.type = MEET_OTHER;
2783 // Copy modified_blocks to event
2784 for(std::map<v3s16, MapBlock*>::iterator
2785 i = modified_blocks.begin();
2786 i != modified_blocks.end(); ++i)
2788 event.modified_blocks.insert(i->first);
2792 dispatchEvent(&event);
2802 MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
2804 MapBlock *block = getBlockNoCreateNoEx(p3d);
2806 m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
2811 void ServerMap::prepareBlock(MapBlock *block) {
2814 // N.B. This requires no synchronization, since data will not be modified unless
2815 // the VoxelManipulator being updated belongs to the same thread.
2816 void ServerMap::updateVManip(v3s16 pos)
2818 Mapgen *mg = m_emerge->getCurrentMapgen();
2822 ManualMapVoxelManipulator *vm = mg->vm;
2826 if (!vm->m_area.contains(pos))
2829 s32 idx = vm->m_area.index(pos);
2830 vm->m_data[idx] = getNodeNoEx(pos);
2831 vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
2833 vm->m_is_dirty = true;
2836 s16 ServerMap::findGroundLevel(v2s16 p2d)
2840 Uh, just do something random...
2842 // Find existing map from top to down
2845 v3s16 p(p2d.X, max, p2d.Y);
2846 for(; p.Y>min; p.Y--)
2848 MapNode n = getNodeNoEx(p);
2849 if(n.getContent() != CONTENT_IGNORE)
2854 // If this node is not air, go to plan b
2855 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2857 // Search existing walkable and return it
2858 for(; p.Y>min; p.Y--)
2860 MapNode n = getNodeNoEx(p);
2861 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2870 Determine from map generator noise functions
2873 s16 level = m_emerge->getGroundLevelAtPoint(p2d);
2876 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2877 //return (s16)level;
2880 bool ServerMap::loadFromFolders() {
2881 if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
2886 void ServerMap::createDirs(std::string path)
2888 if(fs::CreateAllDirs(path) == false)
2890 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2891 <<"\""<<path<<"\""<<std::endl;
2892 throw BaseException("ServerMap failed to create directory");
2896 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2902 snprintf(cc, 9, "%.4x%.4x",
2903 (unsigned int)pos.X&0xffff,
2904 (unsigned int)pos.Y&0xffff);
2906 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2908 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2909 (unsigned int)pos.X&0xfff,
2910 (unsigned int)pos.Y&0xfff);
2912 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2919 v2s16 ServerMap::getSectorPos(std::string dirname)
2921 unsigned int x = 0, y = 0;
2923 std::string component;
2924 fs::RemoveLastPathComponent(dirname, &component, 1);
2925 if(component.size() == 8)
2928 r = sscanf(component.c_str(), "%4x%4x", &x, &y);
2930 else if(component.size() == 3)
2933 fs::RemoveLastPathComponent(dirname, &component, 2);
2934 r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2935 // Sign-extend the 12 bit values up to 16 bits...
2936 if(x&0x800) x|=0xF000;
2937 if(y&0x800) y|=0xF000;
2944 v2s16 pos((s16)x, (s16)y);
2948 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2950 v2s16 p2d = getSectorPos(sectordir);
2952 if(blockfile.size() != 4){
2953 throw InvalidFilenameException("Invalid block filename");
2956 int r = sscanf(blockfile.c_str(), "%4x", &y);
2958 throw InvalidFilenameException("Invalid block filename");
2959 return v3s16(p2d.X, y, p2d.Y);
2962 std::string ServerMap::getBlockFilename(v3s16 p)
2965 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2969 void ServerMap::save(ModifiedState save_level)
2971 DSTACK(__FUNCTION_NAME);
2972 if(m_map_saving_enabled == false)
2974 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2978 if(save_level == MOD_STATE_CLEAN)
2979 infostream<<"ServerMap: Saving whole map, this can take time."
2982 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2987 // Profile modified reasons
2988 Profiler modprofiler;
2990 u32 sector_meta_count = 0;
2991 u32 block_count = 0;
2992 u32 block_count_all = 0; // Number of blocks in memory
2994 // Don't do anything with sqlite unless something is really saved
2995 bool save_started = false;
2997 for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
2998 i != m_sectors.end(); ++i)
3000 ServerMapSector *sector = (ServerMapSector*)i->second;
3001 assert(sector->getId() == MAPSECTOR_SERVER);
3003 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
3005 saveSectorMeta(sector);
3006 sector_meta_count++;
3008 std::list<MapBlock*> blocks;
3009 sector->getBlocks(blocks);
3011 for(std::list<MapBlock*>::iterator j = blocks.begin();
3012 j != blocks.end(); ++j)
3014 MapBlock *block = *j;
3018 if(block->getModified() >= (u32)save_level)
3023 save_started = true;
3026 modprofiler.add(block->getModifiedReason(), 1);
3031 /*infostream<<"ServerMap: Written block ("
3032 <<block->getPos().X<<","
3033 <<block->getPos().Y<<","
3034 <<block->getPos().Z<<")"
3043 Only print if something happened or saved whole map
3045 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
3046 || block_count != 0)
3048 infostream<<"ServerMap: Written: "
3049 <<sector_meta_count<<" sector metadata files, "
3050 <<block_count<<" block files"
3051 <<", "<<block_count_all<<" blocks in memory."
3053 PrintInfo(infostream); // ServerMap/ClientMap:
3054 infostream<<"Blocks modified by: "<<std::endl;
3055 modprofiler.print(infostream);
3059 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
3061 if(loadFromFolders()){
3062 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
3063 <<"all blocks that are stored in flat files"<<std::endl;
3065 dbase->listAllLoadableBlocks(dst);
3068 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
3070 for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
3071 si != m_sectors.end(); ++si)
3073 MapSector *sector = si->second;
3075 std::list<MapBlock*> blocks;
3076 sector->getBlocks(blocks);
3078 for(std::list<MapBlock*>::iterator i = blocks.begin();
3079 i != blocks.end(); ++i)
3081 MapBlock *block = (*i);
3082 v3s16 p = block->getPos();
3088 void ServerMap::saveMapMeta()
3090 DSTACK(__FUNCTION_NAME);
3092 /*infostream<<"ServerMap::saveMapMeta(): "
3096 createDirs(m_savedir);
3098 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3099 std::ostringstream ss(std::ios_base::binary);
3103 m_emerge->saveParamsToSettings(¶ms);
3104 params.writeLines(ss);
3106 ss<<"[end_of_params]\n";
3108 if(!fs::safeWriteToFile(fullpath, ss.str()))
3110 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3111 <<"could not write "<<fullpath<<std::endl;
3112 throw FileNotGoodException("Cannot save chunk metadata");
3115 m_map_metadata_changed = false;
3118 void ServerMap::loadMapMeta()
3120 DSTACK(__FUNCTION_NAME);
3122 std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
3123 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3125 errorstream << "ServerMap::loadMapMeta(): "
3126 << "could not open" << fullpath << std::endl;
3127 throw FileNotGoodException("Cannot open map metadata");
3132 if (!params.parseConfigLines(is, "[end_of_params]")) {
3133 throw SerializationError("ServerMap::loadMapMeta(): "
3134 "[end_of_params] not found!");
3137 m_emerge->loadParamsFromSettings(¶ms);
3139 verbosestream << "ServerMap::loadMapMeta(): seed="
3140 << m_emerge->params.seed << std::endl;
3143 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3145 DSTACK(__FUNCTION_NAME);
3146 // Format used for writing
3147 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3149 v2s16 pos = sector->getPos();
3150 std::string dir = getSectorDir(pos);
3153 std::string fullpath = dir + DIR_DELIM + "meta";
3154 std::ostringstream ss(std::ios_base::binary);
3156 sector->serialize(ss, version);
3158 if(!fs::safeWriteToFile(fullpath, ss.str()))
3159 throw FileNotGoodException("Cannot write sector metafile");
3161 sector->differs_from_disk = false;
3164 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3166 DSTACK(__FUNCTION_NAME);
3168 v2s16 p2d = getSectorPos(sectordir);
3170 ServerMapSector *sector = NULL;
3172 std::string fullpath = sectordir + DIR_DELIM + "meta";
3173 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3174 if(is.good() == false)
3176 // If the directory exists anyway, it probably is in some old
3177 // format. Just go ahead and create the sector.
3178 if(fs::PathExists(sectordir))
3180 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3181 <<fullpath<<" doesn't exist but directory does."
3182 <<" Continuing with a sector with no metadata."
3184 sector = new ServerMapSector(this, p2d, m_gamedef);
3185 m_sectors[p2d] = sector;
3189 throw FileNotGoodException("Cannot open sector metafile");
3194 sector = ServerMapSector::deSerialize
3195 (is, this, p2d, m_sectors, m_gamedef);
3197 saveSectorMeta(sector);
3200 sector->differs_from_disk = false;
3205 bool ServerMap::loadSectorMeta(v2s16 p2d)
3207 DSTACK(__FUNCTION_NAME);
3209 MapSector *sector = NULL;
3211 // The directory layout we're going to load from.
3212 // 1 - original sectors/xxxxzzzz/
3213 // 2 - new sectors2/xxx/zzz/
3214 // If we load from anything but the latest structure, we will
3215 // immediately save to the new one, and remove the old.
3217 std::string sectordir1 = getSectorDir(p2d, 1);
3218 std::string sectordir;
3219 if(fs::PathExists(sectordir1))
3221 sectordir = sectordir1;
3226 sectordir = getSectorDir(p2d, 2);
3230 sector = loadSectorMeta(sectordir, loadlayout != 2);
3232 catch(InvalidFilenameException &e)
3236 catch(FileNotGoodException &e)
3240 catch(std::exception &e)
3249 bool ServerMap::loadSectorFull(v2s16 p2d)
3251 DSTACK(__FUNCTION_NAME);
3253 MapSector *sector = NULL;
3255 // The directory layout we're going to load from.
3256 // 1 - original sectors/xxxxzzzz/
3257 // 2 - new sectors2/xxx/zzz/
3258 // If we load from anything but the latest structure, we will
3259 // immediately save to the new one, and remove the old.
3261 std::string sectordir1 = getSectorDir(p2d, 1);
3262 std::string sectordir;
3263 if(fs::PathExists(sectordir1))
3265 sectordir = sectordir1;
3270 sectordir = getSectorDir(p2d, 2);
3274 sector = loadSectorMeta(sectordir, loadlayout != 2);
3276 catch(InvalidFilenameException &e)
3280 catch(FileNotGoodException &e)
3284 catch(std::exception &e)
3292 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3294 std::vector<fs::DirListNode>::iterator i2;
3295 for(i2=list2.begin(); i2!=list2.end(); i2++)
3301 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3303 catch(InvalidFilenameException &e)
3305 // This catches unknown crap in directory
3311 infostream<<"Sector converted to new layout - deleting "<<
3312 sectordir1<<std::endl;
3313 fs::RecursiveDelete(sectordir1);
3320 void ServerMap::beginSave()
3325 void ServerMap::endSave()
3330 bool ServerMap::saveBlock(MapBlock *block)
3332 return saveBlock(block, dbase);
3335 bool ServerMap::saveBlock(MapBlock *block, Database *db)
3337 v3s16 p3d = block->getPos();
3339 // Dummy blocks are not written
3340 if (block->isDummy()) {
3341 errorstream << "WARNING: saveBlock: Not writing dummy block "
3342 << PP(p3d) << std::endl;
3346 // Format used for writing
3347 u8 version = SER_FMT_VER_HIGHEST_WRITE;
3350 [0] u8 serialization version
3353 std::ostringstream o(std::ios_base::binary);
3354 o.write((char*) &version, 1);
3355 block->serialize(o, version, true);
3357 std::string data = o.str();
3358 bool ret = db->saveBlock(p3d, data);
3360 // We just wrote it to the disk so clear modified flag
3361 block->resetModified();
3366 void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
3367 MapSector *sector, bool save_after_load)
3369 DSTACK(__FUNCTION_NAME);
3371 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3374 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3375 if(is.good() == false)
3376 throw FileNotGoodException("Cannot open block file");
3378 v3s16 p3d = getBlockPos(sectordir, blockfile);
3379 v2s16 p2d(p3d.X, p3d.Z);
3381 assert(sector->getPos() == p2d);
3383 u8 version = SER_FMT_VER_INVALID;
3384 is.read((char*)&version, 1);
3387 throw SerializationError("ServerMap::loadBlock(): Failed"
3388 " to read MapBlock version");
3390 /*u32 block_size = MapBlock::serializedLength(version);
3391 SharedBuffer<u8> data(block_size);
3392 is.read((char*)*data, block_size);*/
3394 // This will always return a sector because we're the server
3395 //MapSector *sector = emergeSector(p2d);
3397 MapBlock *block = NULL;
3398 bool created_new = false;
3399 block = sector->getBlockNoCreateNoEx(p3d.Y);
3402 block = sector->createBlankBlockNoInsert(p3d.Y);
3407 block->deSerialize(is, version, true);
3409 // If it's a new block, insert it to the map
3411 sector->insertBlock(block);
3414 Save blocks loaded in old format in new format
3417 if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
3421 // Should be in database now, so delete the old file
3422 fs::RecursiveDelete(fullpath);
3425 // We just loaded it from the disk, so it's up-to-date.
3426 block->resetModified();
3429 catch(SerializationError &e)
3431 infostream<<"WARNING: Invalid block data on disk "
3432 <<"fullpath="<<fullpath
3433 <<" (SerializationError). "
3434 <<"what()="<<e.what()
3436 // Ignoring. A new one will be generated.
3439 // TODO: Backup file; name is in fullpath.
3443 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3445 DSTACK(__FUNCTION_NAME);
3448 std::istringstream is(*blob, std::ios_base::binary);
3450 u8 version = SER_FMT_VER_INVALID;
3451 is.read((char*)&version, 1);
3454 throw SerializationError("ServerMap::loadBlock(): Failed"
3455 " to read MapBlock version");
3457 /*u32 block_size = MapBlock::serializedLength(version);
3458 SharedBuffer<u8> data(block_size);
3459 is.read((char*)*data, block_size);*/
3461 // This will always return a sector because we're the server
3462 //MapSector *sector = emergeSector(p2d);
3464 MapBlock *block = NULL;
3465 bool created_new = false;
3466 block = sector->getBlockNoCreateNoEx(p3d.Y);
3469 block = sector->createBlankBlockNoInsert(p3d.Y);
3474 block->deSerialize(is, version, true);
3476 // If it's a new block, insert it to the map
3478 sector->insertBlock(block);
3481 Save blocks loaded in old format in new format
3484 //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
3485 // Only save if asked to; no need to update version
3489 // We just loaded it from, so it's up-to-date.
3490 block->resetModified();
3493 catch(SerializationError &e)
3495 errorstream<<"Invalid block data in database"
3496 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3497 <<" (SerializationError): "<<e.what()<<std::endl;
3499 // TODO: Block should be marked as invalid in memory so that it is
3500 // not touched but the game can run
3502 if(g_settings->getBool("ignore_world_load_errors")){
3503 errorstream<<"Ignoring block load error. Duck and cover! "
3504 <<"(ignore_world_load_errors)"<<std::endl;
3506 throw SerializationError("Invalid block data in database");
3512 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3514 DSTACK(__FUNCTION_NAME);
3516 v2s16 p2d(blockpos.X, blockpos.Z);
3520 ret = dbase->loadBlock(blockpos);
3522 loadBlock(&ret, blockpos, createSector(p2d), false);
3523 return getBlockNoCreateNoEx(blockpos);
3525 // Not found in database, try the files
3527 // The directory layout we're going to load from.
3528 // 1 - original sectors/xxxxzzzz/
3529 // 2 - new sectors2/xxx/zzz/
3530 // If we load from anything but the latest structure, we will
3531 // immediately save to the new one, and remove the old.
3533 std::string sectordir1 = getSectorDir(p2d, 1);
3534 std::string sectordir;
3535 if(fs::PathExists(sectordir1))
3537 sectordir = sectordir1;
3542 sectordir = getSectorDir(p2d, 2);
3546 Make sure sector is loaded
3548 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3552 sector = loadSectorMeta(sectordir, loadlayout != 2);
3554 catch(InvalidFilenameException &e)
3558 catch(FileNotGoodException &e)
3562 catch(std::exception &e)
3569 Make sure file exists
3572 std::string blockfilename = getBlockFilename(blockpos);
3573 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3577 Load block and save it to the database
3579 loadBlock(sectordir, blockfilename, sector, true);
3580 return getBlockNoCreateNoEx(blockpos);
3583 void ServerMap::PrintInfo(std::ostream &out)
3588 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3591 m_create_area(false),
3596 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3600 void ManualMapVoxelManipulator::initializeBlank(v3s16 blockpos_min,
3603 // Units of these are MapBlocks
3604 v3s16 pmin = blockpos_min;
3605 v3s16 pmax = blockpos_max;
3607 VoxelArea block_area_nodes(pmin * MAP_BLOCKSIZE,
3608 (pmax + 1) * MAP_BLOCKSIZE - v3s16(1,1,1));
3610 addArea(block_area_nodes);
3611 u32 extent = m_area.getVolume();
3612 for (u32 i = 0; i != extent; i++)
3613 m_data[i] = MapNode(CONTENT_IGNORE);
3615 for (s32 z = pmin.Z; z <= pmax.Z; z++)
3616 for (s32 y = pmin.Y; y <= pmax.Y; y++)
3617 for (s32 x = pmin.X; x <= pmax.X; x++)
3618 m_loaded_blocks[v3s16(x, y, z)] = 0;
3623 void ManualMapVoxelManipulator::initialEmerge(v3s16 blockpos_min,
3624 v3s16 blockpos_max, bool load_if_inexistent)
3626 TimeTaker timer1("initialEmerge", &emerge_time);
3628 // Units of these are MapBlocks
3629 v3s16 p_min = blockpos_min;
3630 v3s16 p_max = blockpos_max;
3632 VoxelArea block_area_nodes
3633 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3635 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3638 infostream<<"initialEmerge: area: ";
3639 block_area_nodes.print(infostream);
3640 infostream<<" ("<<size_MB<<"MB)";
3641 infostream<<std::endl;
3644 addArea(block_area_nodes);
3646 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3647 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3648 for(s32 x=p_min.X; x<=p_max.X; x++)
3653 std::map<v3s16, u8>::iterator n;
3654 n = m_loaded_blocks.find(p);
3655 if(n != m_loaded_blocks.end())
3658 bool block_data_inexistent = false;
3661 TimeTaker timer1("emerge load", &emerge_load_time);
3663 block = m_map->getBlockNoCreate(p);
3664 if(block->isDummy())
3665 block_data_inexistent = true;
3667 block->copyTo(*this);
3669 catch(InvalidPositionException &e)
3671 block_data_inexistent = true;
3674 if(block_data_inexistent)
3677 if (load_if_inexistent) {
3678 ServerMap *svrmap = (ServerMap *)m_map;
3679 block = svrmap->emergeBlock(p, false);
3681 block = svrmap->createBlock(p);
3683 block->copyTo(*this);
3685 flags |= VMANIP_BLOCK_DATA_INEXIST;
3688 Mark area inexistent
3690 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3691 // Fill with VOXELFLAG_NO_DATA
3692 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3693 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3695 s32 i = m_area.index(a.MinEdge.X,y,z);
3696 memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
3700 /*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
3702 // Mark that block was loaded as blank
3703 flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
3706 m_loaded_blocks[p] = flags;
3712 void ManualMapVoxelManipulator::blitBackAll(
3713 std::map<v3s16, MapBlock*> *modified_blocks,
3714 bool overwrite_generated)
3716 if(m_area.getExtent() == v3s16(0,0,0))
3720 Copy data of all blocks
3722 for(std::map<v3s16, u8>::iterator
3723 i = m_loaded_blocks.begin();
3724 i != m_loaded_blocks.end(); ++i)
3727 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3728 bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST);
3729 if ((existed == false) || (block == NULL) ||
3730 (overwrite_generated == false && block->isGenerated() == true))
3733 block->copyFrom(*this);
3736 (*modified_blocks)[p] = block;