3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU 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"
28 #include "nodemetadata.h"
34 #include "util/directiontables.h"
36 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
39 SQLite format specification:
40 - Initially only replaces sectors/ and sectors2/
42 If map.sqlite does not exist in the save dir
43 or the block was not found in the database
44 the map will try to load from sectors folder.
45 In either case, map.sqlite will be created
46 and all future saves will save there.
48 Structure of map.sqlite:
59 Map::Map(std::ostream &dout, IGameDef *gamedef):
64 /*m_sector_mutex.Init();
65 assert(m_sector_mutex.IsInitialized());*/
73 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
74 for(; i.atEnd() == false; i++)
76 MapSector *sector = i.getNode()->getValue();
81 void Map::addEventReceiver(MapEventReceiver *event_receiver)
83 m_event_receivers.insert(event_receiver, false);
86 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
88 if(m_event_receivers.find(event_receiver) == NULL)
90 m_event_receivers.remove(event_receiver);
93 void Map::dispatchEvent(MapEditEvent *event)
95 for(core::map<MapEventReceiver*, bool>::Iterator
96 i = m_event_receivers.getIterator();
97 i.atEnd()==false; i++)
99 MapEventReceiver* event_receiver = i.getNode()->getKey();
100 event_receiver->onMapEditEvent(event);
104 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
106 if(m_sector_cache != NULL && p == m_sector_cache_p){
107 MapSector * sector = m_sector_cache;
111 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
116 MapSector *sector = n->getValue();
118 // Cache the last result
119 m_sector_cache_p = p;
120 m_sector_cache = sector;
125 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
127 return getSectorNoGenerateNoExNoLock(p);
130 MapSector * Map::getSectorNoGenerate(v2s16 p)
132 MapSector *sector = getSectorNoGenerateNoEx(p);
134 throw InvalidPositionException();
139 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
141 v2s16 p2d(p3d.X, p3d.Z);
142 MapSector * sector = getSectorNoGenerateNoEx(p2d);
145 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
149 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
151 MapBlock *block = getBlockNoCreateNoEx(p3d);
153 throw InvalidPositionException();
157 bool Map::isNodeUnderground(v3s16 p)
159 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock * block = getBlockNoCreate(blockpos);
162 return block->getIsUnderground();
164 catch(InvalidPositionException &e)
170 bool Map::isValidPosition(v3s16 p)
172 v3s16 blockpos = getNodeBlockPos(p);
173 MapBlock *block = getBlockNoCreate(blockpos);
174 return (block != NULL);
177 // Returns a CONTENT_IGNORE node if not found
178 MapNode Map::getNodeNoEx(v3s16 p)
180 v3s16 blockpos = getNodeBlockPos(p);
181 MapBlock *block = getBlockNoCreateNoEx(blockpos);
183 return MapNode(CONTENT_IGNORE);
184 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
185 return block->getNodeNoCheck(relpos);
188 // throws InvalidPositionException if not found
189 MapNode Map::getNode(v3s16 p)
191 v3s16 blockpos = getNodeBlockPos(p);
192 MapBlock *block = getBlockNoCreateNoEx(blockpos);
194 throw InvalidPositionException();
195 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196 return block->getNodeNoCheck(relpos);
199 // throws InvalidPositionException if not found
200 void Map::setNode(v3s16 p, MapNode & n)
202 v3s16 blockpos = getNodeBlockPos(p);
203 MapBlock *block = getBlockNoCreate(blockpos);
204 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
205 // Never allow placing CONTENT_IGNORE, it fucks up stuff
206 if(n.getContent() == CONTENT_IGNORE){
207 errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
208 <<" while trying to replace \""
209 <<m_gamedef->ndef()->get(block->getNodeNoCheck(relpos)).name
210 <<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
211 debug_stacks_print_to(infostream);
214 block->setNodeNoCheck(relpos, n);
219 Goes recursively through the neighbours of the node.
221 Alters only transparent nodes.
223 If the lighting of the neighbour is lower than the lighting of
224 the node was (before changing it to 0 at the step before), the
225 lighting of the neighbour is set to 0 and then the same stuff
226 repeats for the neighbour.
228 The ending nodes of the routine are stored in light_sources.
229 This is useful when a light is removed. In such case, this
230 routine can be called for the light node and then again for
231 light_sources to re-light the area without the removed light.
233 values of from_nodes are lighting values.
235 void Map::unspreadLight(enum LightBank bank,
236 core::map<v3s16, u8> & from_nodes,
237 core::map<v3s16, bool> & light_sources,
238 core::map<v3s16, MapBlock*> & modified_blocks)
240 INodeDefManager *nodemgr = m_gamedef->ndef();
243 v3s16(0,0,1), // back
245 v3s16(1,0,0), // right
246 v3s16(0,0,-1), // front
247 v3s16(0,-1,0), // bottom
248 v3s16(-1,0,0), // left
251 if(from_nodes.size() == 0)
254 u32 blockchangecount = 0;
256 core::map<v3s16, u8> unlighted_nodes;
257 core::map<v3s16, u8>::Iterator j;
258 j = from_nodes.getIterator();
261 Initialize block cache
264 MapBlock *block = NULL;
265 // Cache this a bit, too
266 bool block_checked_in_modified = false;
268 for(; j.atEnd() == false; j++)
270 v3s16 pos = j.getNode()->getKey();
271 v3s16 blockpos = getNodeBlockPos(pos);
273 // Only fetch a new block if the block position has changed
275 if(block == NULL || blockpos != blockpos_last){
276 block = getBlockNoCreate(blockpos);
277 blockpos_last = blockpos;
279 block_checked_in_modified = false;
283 catch(InvalidPositionException &e)
291 // Calculate relative position in block
292 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
294 // Get node straight from the block
295 MapNode n = block->getNode(relpos);
297 u8 oldlight = j.getNode()->getValue();
299 // Loop through 6 neighbors
300 for(u16 i=0; i<6; i++)
302 // Get the position of the neighbor node
303 v3s16 n2pos = pos + dirs[i];
305 // Get the block where the node is located
306 v3s16 blockpos = getNodeBlockPos(n2pos);
310 // Only fetch a new block if the block position has changed
312 if(block == NULL || blockpos != blockpos_last){
313 block = getBlockNoCreate(blockpos);
314 blockpos_last = blockpos;
316 block_checked_in_modified = false;
320 catch(InvalidPositionException &e)
325 // Calculate relative position in block
326 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
327 // Get node straight from the block
328 MapNode n2 = block->getNode(relpos);
330 bool changed = false;
332 //TODO: Optimize output by optimizing light_sources?
335 If the neighbor is dimmer than what was specified
336 as oldlight (the light of the previous node)
338 if(n2.getLight(bank, nodemgr) < oldlight)
341 And the neighbor is transparent and it has some light
343 if(nodemgr->get(n2).light_propagates
344 && n2.getLight(bank, nodemgr) != 0)
347 Set light to 0 and add to queue
350 u8 current_light = n2.getLight(bank, nodemgr);
351 n2.setLight(bank, 0, nodemgr);
352 block->setNode(relpos, n2);
354 unlighted_nodes.insert(n2pos, current_light);
358 Remove from light_sources if it is there
359 NOTE: This doesn't happen nearly at all
361 /*if(light_sources.find(n2pos))
363 infostream<<"Removed from light_sources"<<std::endl;
364 light_sources.remove(n2pos);
369 if(light_sources.find(n2pos) != NULL)
370 light_sources.remove(n2pos);*/
373 light_sources.insert(n2pos, true);
376 // Add to modified_blocks
377 if(changed == true && block_checked_in_modified == false)
379 // If the block is not found in modified_blocks, add.
380 if(modified_blocks.find(blockpos) == NULL)
382 modified_blocks.insert(blockpos, block);
384 block_checked_in_modified = true;
387 catch(InvalidPositionException &e)
394 /*infostream<<"unspreadLight(): Changed block "
395 <<blockchangecount<<" times"
396 <<" for "<<from_nodes.size()<<" nodes"
399 if(unlighted_nodes.size() > 0)
400 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
404 A single-node wrapper of the above
406 void Map::unLightNeighbors(enum LightBank bank,
407 v3s16 pos, u8 lightwas,
408 core::map<v3s16, bool> & light_sources,
409 core::map<v3s16, MapBlock*> & modified_blocks)
411 core::map<v3s16, u8> from_nodes;
412 from_nodes.insert(pos, lightwas);
414 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
418 Lights neighbors of from_nodes, collects all them and then
421 void Map::spreadLight(enum LightBank bank,
422 core::map<v3s16, bool> & from_nodes,
423 core::map<v3s16, MapBlock*> & modified_blocks)
425 INodeDefManager *nodemgr = m_gamedef->ndef();
427 const v3s16 dirs[6] = {
428 v3s16(0,0,1), // back
430 v3s16(1,0,0), // right
431 v3s16(0,0,-1), // front
432 v3s16(0,-1,0), // bottom
433 v3s16(-1,0,0), // left
436 if(from_nodes.size() == 0)
439 u32 blockchangecount = 0;
441 core::map<v3s16, bool> lighted_nodes;
442 core::map<v3s16, bool>::Iterator j;
443 j = from_nodes.getIterator();
446 Initialize block cache
449 MapBlock *block = NULL;
450 // Cache this a bit, too
451 bool block_checked_in_modified = false;
453 for(; j.atEnd() == false; j++)
454 //for(; j != from_nodes.end(); j++)
456 v3s16 pos = j.getNode()->getKey();
458 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
459 v3s16 blockpos = getNodeBlockPos(pos);
461 // Only fetch a new block if the block position has changed
463 if(block == NULL || blockpos != blockpos_last){
464 block = getBlockNoCreate(blockpos);
465 blockpos_last = blockpos;
467 block_checked_in_modified = false;
471 catch(InvalidPositionException &e)
479 // Calculate relative position in block
480 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
482 // Get node straight from the block
483 MapNode n = block->getNode(relpos);
485 u8 oldlight = n.getLight(bank, nodemgr);
486 u8 newlight = diminish_light(oldlight);
488 // Loop through 6 neighbors
489 for(u16 i=0; i<6; i++){
490 // Get the position of the neighbor node
491 v3s16 n2pos = pos + dirs[i];
493 // Get the block where the node is located
494 v3s16 blockpos = getNodeBlockPos(n2pos);
498 // Only fetch a new block if the block position has changed
500 if(block == NULL || blockpos != blockpos_last){
501 block = getBlockNoCreate(blockpos);
502 blockpos_last = blockpos;
504 block_checked_in_modified = false;
508 catch(InvalidPositionException &e)
513 // Calculate relative position in block
514 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
515 // Get node straight from the block
516 MapNode n2 = block->getNode(relpos);
518 bool changed = false;
520 If the neighbor is brighter than the current node,
521 add to list (it will light up this node on its turn)
523 if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight))
525 lighted_nodes.insert(n2pos, true);
526 //lighted_nodes.push_back(n2pos);
530 If the neighbor is dimmer than how much light this node
531 would spread on it, add to list
533 if(n2.getLight(bank, nodemgr) < newlight)
535 if(nodemgr->get(n2).light_propagates)
537 n2.setLight(bank, newlight, nodemgr);
538 block->setNode(relpos, n2);
539 lighted_nodes.insert(n2pos, true);
540 //lighted_nodes.push_back(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) == NULL)
551 modified_blocks.insert(blockpos, block);
553 block_checked_in_modified = true;
556 catch(InvalidPositionException &e)
563 /*infostream<<"spreadLight(): Changed block "
564 <<blockchangecount<<" times"
565 <<" for "<<from_nodes.size()<<" nodes"
568 if(lighted_nodes.size() > 0)
569 spreadLight(bank, lighted_nodes, modified_blocks);
573 A single-node source variation of the above.
575 void Map::lightNeighbors(enum LightBank bank,
577 core::map<v3s16, MapBlock*> & modified_blocks)
579 core::map<v3s16, bool> from_nodes;
580 from_nodes.insert(pos, true);
581 spreadLight(bank, from_nodes, modified_blocks);
584 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
586 INodeDefManager *nodemgr = m_gamedef->ndef();
589 v3s16(0,0,1), // back
591 v3s16(1,0,0), // right
592 v3s16(0,0,-1), // front
593 v3s16(0,-1,0), // bottom
594 v3s16(-1,0,0), // left
597 u8 brightest_light = 0;
598 v3s16 brightest_pos(0,0,0);
599 bool found_something = false;
601 // Loop through 6 neighbors
602 for(u16 i=0; i<6; i++){
603 // Get the position of the neighbor node
604 v3s16 n2pos = p + dirs[i];
609 catch(InvalidPositionException &e)
613 if(n2.getLight(bank, nodemgr) > brightest_light || found_something == false){
614 brightest_light = n2.getLight(bank, nodemgr);
615 brightest_pos = n2pos;
616 found_something = true;
620 if(found_something == false)
621 throw InvalidPositionException();
623 return brightest_pos;
627 Propagates sunlight down from a node.
628 Starting point gets sunlight.
630 Returns the lowest y value of where the sunlight went.
632 Mud is turned into grass in where the sunlight stops.
634 s16 Map::propagateSunlight(v3s16 start,
635 core::map<v3s16, MapBlock*> & modified_blocks)
637 INodeDefManager *nodemgr = m_gamedef->ndef();
642 v3s16 pos(start.X, y, start.Z);
644 v3s16 blockpos = getNodeBlockPos(pos);
647 block = getBlockNoCreate(blockpos);
649 catch(InvalidPositionException &e)
654 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
655 MapNode n = block->getNode(relpos);
657 if(nodemgr->get(n).sunlight_propagates)
659 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
660 block->setNode(relpos, n);
662 modified_blocks.insert(blockpos, block);
666 // Sunlight goes no further
673 void Map::updateLighting(enum LightBank bank,
674 core::map<v3s16, MapBlock*> & a_blocks,
675 core::map<v3s16, MapBlock*> & modified_blocks)
677 INodeDefManager *nodemgr = m_gamedef->ndef();
679 /*m_dout<<DTIME<<"Map::updateLighting(): "
680 <<a_blocks.size()<<" blocks."<<std::endl;*/
682 //TimeTaker timer("updateLighting");
686 //u32 count_was = modified_blocks.size();
688 core::map<v3s16, MapBlock*> blocks_to_update;
690 core::map<v3s16, bool> light_sources;
692 core::map<v3s16, u8> unlight_from;
694 int num_bottom_invalid = 0;
697 //TimeTaker t("first stuff");
699 core::map<v3s16, MapBlock*>::Iterator i;
700 i = a_blocks.getIterator();
701 for(; i.atEnd() == false; i++)
703 MapBlock *block = i.getNode()->getValue();
707 // Don't bother with dummy blocks.
711 v3s16 pos = block->getPos();
712 v3s16 posnodes = block->getPosRelative();
713 modified_blocks.insert(pos, block);
715 blocks_to_update.insert(pos, block);
718 Clear all light from block
720 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
721 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
722 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
727 MapNode n = block->getNode(p);
728 u8 oldlight = n.getLight(bank, nodemgr);
729 n.setLight(bank, 0, nodemgr);
730 block->setNode(p, n);
732 // If node sources light, add to list
733 u8 source = nodemgr->get(n).light_source;
735 light_sources[p + posnodes] = true;
737 // Collect borders for unlighting
738 if((x==0 || x == MAP_BLOCKSIZE-1
739 || y==0 || y == MAP_BLOCKSIZE-1
740 || z==0 || z == MAP_BLOCKSIZE-1)
743 v3s16 p_map = p + posnodes;
744 unlight_from.insert(p_map, oldlight);
747 catch(InvalidPositionException &e)
750 This would happen when dealing with a
754 infostream<<"updateLighting(): InvalidPositionException"
759 if(bank == LIGHTBANK_DAY)
761 bool bottom_valid = block->propagateSunlight(light_sources);
764 num_bottom_invalid++;
766 // If bottom is valid, we're done.
770 else if(bank == LIGHTBANK_NIGHT)
772 // For night lighting, sunlight is not propagated
777 // Invalid lighting bank
781 /*infostream<<"Bottom for sunlight-propagated block ("
782 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
785 // Bottom sunlight is not valid; get the block and loop to it
789 block = getBlockNoCreate(pos);
791 catch(InvalidPositionException &e)
802 Enable this to disable proper lighting for speeding up map
803 generation for testing or whatever
806 //if(g_settings->get(""))
808 core::map<v3s16, MapBlock*>::Iterator i;
809 i = blocks_to_update.getIterator();
810 for(; i.atEnd() == false; i++)
812 MapBlock *block = i.getNode()->getValue();
813 v3s16 p = block->getPos();
814 block->setLightingExpired(false);
822 //TimeTaker timer("unspreadLight");
823 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
828 u32 diff = modified_blocks.size() - count_was;
829 count_was = modified_blocks.size();
830 infostream<<"unspreadLight modified "<<diff<<std::endl;
834 //TimeTaker timer("spreadLight");
835 spreadLight(bank, light_sources, modified_blocks);
840 u32 diff = modified_blocks.size() - count_was;
841 count_was = modified_blocks.size();
842 infostream<<"spreadLight modified "<<diff<<std::endl;
848 //MapVoxelManipulator vmanip(this);
850 // Make a manual voxel manipulator and load all the blocks
851 // that touch the requested blocks
852 ManualMapVoxelManipulator vmanip(this);
855 //TimeTaker timer("initialEmerge");
857 core::map<v3s16, MapBlock*>::Iterator i;
858 i = blocks_to_update.getIterator();
859 for(; i.atEnd() == false; i++)
861 MapBlock *block = i.getNode()->getValue();
862 v3s16 p = block->getPos();
864 // Add all surrounding blocks
865 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
868 Add all surrounding blocks that have up-to-date lighting
869 NOTE: This doesn't quite do the job (not everything
870 appropriate is lighted)
872 /*for(s16 z=-1; z<=1; z++)
873 for(s16 y=-1; y<=1; y++)
874 for(s16 x=-1; x<=1; x++)
876 v3s16 p2 = p + v3s16(x,y,z);
877 MapBlock *block = getBlockNoCreateNoEx(p2);
882 if(block->getLightingExpired())
884 vmanip.initialEmerge(p2, p2);
887 // Lighting of block will be updated completely
888 block->setLightingExpired(false);
893 //TimeTaker timer("unSpreadLight");
894 vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr);
897 //TimeTaker timer("spreadLight");
898 vmanip.spreadLight(bank, light_sources, nodemgr);
901 //TimeTaker timer("blitBack");
902 vmanip.blitBack(modified_blocks);
904 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
909 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
912 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
913 core::map<v3s16, MapBlock*> & modified_blocks)
915 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
916 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
919 Update information about whether day and night light differ
921 for(core::map<v3s16, MapBlock*>::Iterator
922 i = modified_blocks.getIterator();
923 i.atEnd() == false; i++)
925 MapBlock *block = i.getNode()->getValue();
926 block->expireDayNightDiff();
932 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
933 core::map<v3s16, MapBlock*> &modified_blocks)
935 INodeDefManager *nodemgr = m_gamedef->ndef();
938 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
939 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
942 From this node to nodes underneath:
943 If lighting is sunlight (1.0), unlight neighbours and
948 v3s16 toppos = p + v3s16(0,1,0);
949 v3s16 bottompos = p + v3s16(0,-1,0);
951 bool node_under_sunlight = true;
952 core::map<v3s16, bool> light_sources;
955 If there is a node at top and it doesn't have sunlight,
956 there has not been any sunlight going down.
958 Otherwise there probably is.
961 MapNode topnode = getNode(toppos);
963 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
964 node_under_sunlight = false;
966 catch(InvalidPositionException &e)
971 Remove all light that has come out of this node
974 enum LightBank banks[] =
979 for(s32 i=0; i<2; i++)
981 enum LightBank bank = banks[i];
983 u8 lightwas = getNode(p).getLight(bank, nodemgr);
985 // Add the block of the added node to modified_blocks
986 v3s16 blockpos = getNodeBlockPos(p);
987 MapBlock * block = getBlockNoCreate(blockpos);
988 assert(block != NULL);
989 modified_blocks.insert(blockpos, block);
991 assert(isValidPosition(p));
993 // Unlight neighbours of node.
994 // This means setting light of all consequent dimmer nodes
996 // This also collects the nodes at the border which will spread
997 // light again into this.
998 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1000 n.setLight(bank, 0, nodemgr);
1004 If node lets sunlight through and is under sunlight, it has
1007 if(node_under_sunlight && nodemgr->get(n).sunlight_propagates)
1009 n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr);
1013 Remove node metadata
1016 removeNodeMetadata(p);
1019 Set the node on the map
1025 If node is under sunlight and doesn't let sunlight through,
1026 take all sunlighted nodes under it and clear light from them
1027 and from where the light has been spread.
1028 TODO: This could be optimized by mass-unlighting instead
1031 if(node_under_sunlight && !nodemgr->get(n).sunlight_propagates)
1035 //m_dout<<DTIME<<"y="<<y<<std::endl;
1036 v3s16 n2pos(p.X, y, p.Z);
1040 n2 = getNode(n2pos);
1042 catch(InvalidPositionException &e)
1047 if(n2.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN)
1049 unLightNeighbors(LIGHTBANK_DAY,
1050 n2pos, n2.getLight(LIGHTBANK_DAY, nodemgr),
1051 light_sources, modified_blocks);
1052 n2.setLight(LIGHTBANK_DAY, 0, nodemgr);
1060 for(s32 i=0; i<2; i++)
1062 enum LightBank bank = banks[i];
1065 Spread light from all nodes that might be capable of doing so
1067 spreadLight(bank, light_sources, modified_blocks);
1071 Update information about whether day and night light differ
1073 for(core::map<v3s16, MapBlock*>::Iterator
1074 i = modified_blocks.getIterator();
1075 i.atEnd() == false; i++)
1077 MapBlock *block = i.getNode()->getValue();
1078 block->expireDayNightDiff();
1082 Add neighboring liquid nodes and the node itself if it is
1083 liquid (=water node was added) to transform queue.
1086 v3s16(0,0,0), // self
1087 v3s16(0,0,1), // back
1088 v3s16(0,1,0), // top
1089 v3s16(1,0,0), // right
1090 v3s16(0,0,-1), // front
1091 v3s16(0,-1,0), // bottom
1092 v3s16(-1,0,0), // left
1094 for(u16 i=0; i<7; i++)
1099 v3s16 p2 = p + dirs[i];
1101 MapNode n2 = getNode(p2);
1102 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1104 m_transforming_liquid.push_back(p2);
1107 }catch(InvalidPositionException &e)
1115 void Map::removeNodeAndUpdate(v3s16 p,
1116 core::map<v3s16, MapBlock*> &modified_blocks)
1118 INodeDefManager *nodemgr = m_gamedef->ndef();
1120 /*PrintInfo(m_dout);
1121 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1122 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1124 bool node_under_sunlight = true;
1126 v3s16 toppos = p + v3s16(0,1,0);
1128 // Node will be replaced with this
1129 content_t replace_material = CONTENT_AIR;
1132 If there is a node at top and it doesn't have sunlight,
1133 there will be no sunlight going down.
1136 MapNode topnode = getNode(toppos);
1138 if(topnode.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN)
1139 node_under_sunlight = false;
1141 catch(InvalidPositionException &e)
1145 core::map<v3s16, bool> light_sources;
1147 enum LightBank banks[] =
1152 for(s32 i=0; i<2; i++)
1154 enum LightBank bank = banks[i];
1157 Unlight neighbors (in case the node is a light source)
1159 unLightNeighbors(bank, p,
1160 getNode(p).getLight(bank, nodemgr),
1161 light_sources, modified_blocks);
1165 Remove node metadata
1168 removeNodeMetadata(p);
1172 This also clears the lighting.
1176 n.setContent(replace_material);
1179 for(s32 i=0; i<2; i++)
1181 enum LightBank bank = banks[i];
1184 Recalculate lighting
1186 spreadLight(bank, light_sources, modified_blocks);
1189 // Add the block of the removed node to modified_blocks
1190 v3s16 blockpos = getNodeBlockPos(p);
1191 MapBlock * block = getBlockNoCreate(blockpos);
1192 assert(block != NULL);
1193 modified_blocks.insert(blockpos, block);
1196 If the removed node was under sunlight, propagate the
1197 sunlight down from it and then light all neighbors
1198 of the propagated blocks.
1200 if(node_under_sunlight)
1202 s16 ybottom = propagateSunlight(p, modified_blocks);
1203 /*m_dout<<DTIME<<"Node was under sunlight. "
1204 "Propagating sunlight";
1205 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1207 for(; y >= ybottom; y--)
1209 v3s16 p2(p.X, y, p.Z);
1210 /*m_dout<<DTIME<<"lighting neighbors of node ("
1211 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1213 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1218 // Set the lighting of this node to 0
1219 // TODO: Is this needed? Lighting is cleared up there already.
1221 MapNode n = getNode(p);
1222 n.setLight(LIGHTBANK_DAY, 0, nodemgr);
1225 catch(InvalidPositionException &e)
1231 for(s32 i=0; i<2; i++)
1233 enum LightBank bank = banks[i];
1235 // Get the brightest neighbour node and propagate light from it
1236 v3s16 n2p = getBrightestNeighbour(bank, p);
1238 MapNode n2 = getNode(n2p);
1239 lightNeighbors(bank, n2p, modified_blocks);
1241 catch(InvalidPositionException &e)
1247 Update information about whether day and night light differ
1249 for(core::map<v3s16, MapBlock*>::Iterator
1250 i = modified_blocks.getIterator();
1251 i.atEnd() == false; i++)
1253 MapBlock *block = i.getNode()->getValue();
1254 block->expireDayNightDiff();
1258 Add neighboring liquid nodes and this node to transform queue.
1259 (it's vital for the node itself to get updated last.)
1262 v3s16(0,0,1), // back
1263 v3s16(0,1,0), // top
1264 v3s16(1,0,0), // right
1265 v3s16(0,0,-1), // front
1266 v3s16(0,-1,0), // bottom
1267 v3s16(-1,0,0), // left
1268 v3s16(0,0,0), // self
1270 for(u16 i=0; i<7; i++)
1275 v3s16 p2 = p + dirs[i];
1277 MapNode n2 = getNode(p2);
1278 if(nodemgr->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)
1280 m_transforming_liquid.push_back(p2);
1283 }catch(InvalidPositionException &e)
1289 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1292 event.type = MEET_ADDNODE;
1296 bool succeeded = true;
1298 core::map<v3s16, MapBlock*> modified_blocks;
1299 addNodeAndUpdate(p, n, modified_blocks);
1301 // Copy modified_blocks to event
1302 for(core::map<v3s16, MapBlock*>::Iterator
1303 i = modified_blocks.getIterator();
1304 i.atEnd()==false; i++)
1306 event.modified_blocks.insert(i.getNode()->getKey(), false);
1309 catch(InvalidPositionException &e){
1313 dispatchEvent(&event);
1318 bool Map::removeNodeWithEvent(v3s16 p)
1321 event.type = MEET_REMOVENODE;
1324 bool succeeded = true;
1326 core::map<v3s16, MapBlock*> modified_blocks;
1327 removeNodeAndUpdate(p, modified_blocks);
1329 // Copy modified_blocks to event
1330 for(core::map<v3s16, MapBlock*>::Iterator
1331 i = modified_blocks.getIterator();
1332 i.atEnd()==false; i++)
1334 event.modified_blocks.insert(i.getNode()->getKey(), false);
1337 catch(InvalidPositionException &e){
1341 dispatchEvent(&event);
1346 bool Map::getDayNightDiff(v3s16 blockpos)
1349 v3s16 p = blockpos + v3s16(0,0,0);
1350 MapBlock *b = getBlockNoCreate(p);
1351 if(b->getDayNightDiff())
1354 catch(InvalidPositionException &e){}
1357 v3s16 p = blockpos + v3s16(-1,0,0);
1358 MapBlock *b = getBlockNoCreate(p);
1359 if(b->getDayNightDiff())
1362 catch(InvalidPositionException &e){}
1364 v3s16 p = blockpos + v3s16(0,-1,0);
1365 MapBlock *b = getBlockNoCreate(p);
1366 if(b->getDayNightDiff())
1369 catch(InvalidPositionException &e){}
1371 v3s16 p = blockpos + v3s16(0,0,-1);
1372 MapBlock *b = getBlockNoCreate(p);
1373 if(b->getDayNightDiff())
1376 catch(InvalidPositionException &e){}
1379 v3s16 p = blockpos + v3s16(1,0,0);
1380 MapBlock *b = getBlockNoCreate(p);
1381 if(b->getDayNightDiff())
1384 catch(InvalidPositionException &e){}
1386 v3s16 p = blockpos + v3s16(0,1,0);
1387 MapBlock *b = getBlockNoCreate(p);
1388 if(b->getDayNightDiff())
1391 catch(InvalidPositionException &e){}
1393 v3s16 p = blockpos + v3s16(0,0,1);
1394 MapBlock *b = getBlockNoCreate(p);
1395 if(b->getDayNightDiff())
1398 catch(InvalidPositionException &e){}
1404 Updates usage timers
1406 void Map::timerUpdate(float dtime, float unload_timeout,
1407 core::list<v3s16> *unloaded_blocks)
1409 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1411 // Profile modified reasons
1412 Profiler modprofiler;
1414 core::list<v2s16> sector_deletion_queue;
1415 u32 deleted_blocks_count = 0;
1416 u32 saved_blocks_count = 0;
1417 u32 block_count_all = 0;
1419 core::map<v2s16, MapSector*>::Iterator si;
1422 si = m_sectors.getIterator();
1423 for(; si.atEnd() == false; si++)
1425 MapSector *sector = si.getNode()->getValue();
1427 bool all_blocks_deleted = true;
1429 core::list<MapBlock*> blocks;
1430 sector->getBlocks(blocks);
1432 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1433 i != blocks.end(); i++)
1435 MapBlock *block = (*i);
1437 block->incrementUsageTimer(dtime);
1439 if(block->getUsageTimer() > unload_timeout)
1441 v3s16 p = block->getPos();
1444 if(block->getModified() != MOD_STATE_CLEAN
1445 && save_before_unloading)
1447 modprofiler.add(block->getModifiedReason(), 1);
1449 saved_blocks_count++;
1452 // Delete from memory
1453 sector->deleteBlock(block);
1456 unloaded_blocks->push_back(p);
1458 deleted_blocks_count++;
1462 all_blocks_deleted = false;
1467 if(all_blocks_deleted)
1469 sector_deletion_queue.push_back(si.getNode()->getKey());
1474 // Finally delete the empty sectors
1475 deleteSectors(sector_deletion_queue);
1477 if(deleted_blocks_count != 0)
1479 PrintInfo(infostream); // ServerMap/ClientMap:
1480 infostream<<"Unloaded "<<deleted_blocks_count
1481 <<" blocks from memory";
1482 if(save_before_unloading)
1483 infostream<<", of which "<<saved_blocks_count<<" were written";
1484 infostream<<", "<<block_count_all<<" blocks in memory";
1485 infostream<<"."<<std::endl;
1486 if(saved_blocks_count != 0){
1487 PrintInfo(infostream); // ServerMap/ClientMap:
1488 infostream<<"Blocks modified by: "<<std::endl;
1489 modprofiler.print(infostream);
1494 void Map::deleteSectors(core::list<v2s16> &list)
1496 core::list<v2s16>::Iterator j;
1497 for(j=list.begin(); j!=list.end(); j++)
1499 MapSector *sector = m_sectors[*j];
1500 // If sector is in sector cache, remove it from there
1501 if(m_sector_cache == sector)
1502 m_sector_cache = NULL;
1503 // Remove from map and delete
1504 m_sectors.remove(*j);
1510 void Map::unloadUnusedData(float timeout,
1511 core::list<v3s16> *deleted_blocks)
1513 core::list<v2s16> sector_deletion_queue;
1514 u32 deleted_blocks_count = 0;
1515 u32 saved_blocks_count = 0;
1517 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1518 for(; si.atEnd() == false; si++)
1520 MapSector *sector = si.getNode()->getValue();
1522 bool all_blocks_deleted = true;
1524 core::list<MapBlock*> blocks;
1525 sector->getBlocks(blocks);
1526 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1527 i != blocks.end(); i++)
1529 MapBlock *block = (*i);
1531 if(block->getUsageTimer() > timeout)
1534 if(block->getModified() != MOD_STATE_CLEAN)
1537 saved_blocks_count++;
1539 // Delete from memory
1540 sector->deleteBlock(block);
1541 deleted_blocks_count++;
1545 all_blocks_deleted = false;
1549 if(all_blocks_deleted)
1551 sector_deletion_queue.push_back(si.getNode()->getKey());
1555 deleteSectors(sector_deletion_queue);
1557 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1558 <<", of which "<<saved_blocks_count<<" were wr."
1561 //return sector_deletion_queue.getSize();
1562 //return deleted_blocks_count;
1566 void Map::PrintInfo(std::ostream &out)
1571 #define WATER_DROP_BOOST 4
1575 NEIGHBOR_SAME_LEVEL,
1578 struct NodeNeighbor {
1584 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1586 INodeDefManager *nodemgr = m_gamedef->ndef();
1588 DSTACK(__FUNCTION_NAME);
1589 //TimeTaker timer("transformLiquids()");
1592 u32 initial_size = m_transforming_liquid.size();
1594 /*if(initial_size != 0)
1595 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1597 // list of nodes that due to viscosity have not reached their max level height
1598 UniqueQueue<v3s16> must_reflow;
1600 // List of MapBlocks that will require a lighting update (due to lava)
1601 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1603 while(m_transforming_liquid.size() != 0)
1605 // This should be done here so that it is done when continue is used
1606 if(loopcount >= initial_size * 3)
1611 Get a queued transforming liquid node
1613 v3s16 p0 = m_transforming_liquid.pop_front();
1615 MapNode n0 = getNodeNoEx(p0);
1618 Collect information about current node
1620 s8 liquid_level = -1;
1621 u8 liquid_kind = CONTENT_IGNORE;
1622 LiquidType liquid_type = nodemgr->get(n0).liquid_type;
1623 switch (liquid_type) {
1625 liquid_level = LIQUID_LEVEL_SOURCE;
1626 liquid_kind = nodemgr->getId(nodemgr->get(n0).liquid_alternative_flowing);
1628 case LIQUID_FLOWING:
1629 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1630 liquid_kind = n0.getContent();
1633 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1634 // continue with the next node.
1635 if (n0.getContent() != CONTENT_AIR)
1637 liquid_kind = CONTENT_AIR;
1642 Collect information about the environment
1644 const v3s16 *dirs = g_6dirs;
1645 NodeNeighbor sources[6]; // surrounding sources
1646 int num_sources = 0;
1647 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1649 NodeNeighbor airs[6]; // surrounding air
1651 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1652 int num_neutrals = 0;
1653 bool flowing_down = false;
1654 for (u16 i = 0; i < 6; i++) {
1655 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1658 nt = NEIGHBOR_UPPER;
1661 nt = NEIGHBOR_LOWER;
1664 v3s16 npos = p0 + dirs[i];
1665 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1666 switch (nodemgr->get(nb.n.getContent()).liquid_type) {
1668 if (nb.n.getContent() == CONTENT_AIR) {
1669 airs[num_airs++] = nb;
1670 // if the current node is a water source the neighbor
1671 // should be enqueded for transformation regardless of whether the
1672 // current node changes or not.
1673 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1674 m_transforming_liquid.push_back(npos);
1675 // if the current node happens to be a flowing node, it will start to flow down here.
1676 if (nb.t == NEIGHBOR_LOWER) {
1677 flowing_down = true;
1680 neutrals[num_neutrals++] = nb;
1684 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1685 if (liquid_kind == CONTENT_AIR)
1686 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1687 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1688 neutrals[num_neutrals++] = nb;
1690 // Do not count bottom source, it will screw things up
1692 sources[num_sources++] = nb;
1695 case LIQUID_FLOWING:
1696 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1697 if (liquid_kind == CONTENT_AIR)
1698 liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing);
1699 if (nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing) != liquid_kind) {
1700 neutrals[num_neutrals++] = nb;
1702 flows[num_flows++] = nb;
1703 if (nb.t == NEIGHBOR_LOWER)
1704 flowing_down = true;
1711 decide on the type (and possibly level) of the current node
1713 content_t new_node_content;
1714 s8 new_node_level = -1;
1715 s8 max_node_level = -1;
1716 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1717 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1718 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1719 // it's perfectly safe to use liquid_kind here to determine the new node content.
1720 new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source);
1721 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1722 // liquid_kind is set properly, see above
1723 new_node_content = liquid_kind;
1724 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1726 // no surrounding sources, so get the maximum level that can flow into this node
1727 for (u16 i = 0; i < num_flows; i++) {
1728 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1729 switch (flows[i].t) {
1730 case NEIGHBOR_UPPER:
1731 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1732 max_node_level = LIQUID_LEVEL_MAX;
1733 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1734 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1735 } else if (nb_liquid_level > max_node_level)
1736 max_node_level = nb_liquid_level;
1738 case NEIGHBOR_LOWER:
1740 case NEIGHBOR_SAME_LEVEL:
1741 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1742 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1743 max_node_level = nb_liquid_level - 1;
1749 u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity;
1750 if (viscosity > 1 && max_node_level != liquid_level) {
1751 // amount to gain, limited by viscosity
1752 // must be at least 1 in absolute value
1753 s8 level_inc = max_node_level - liquid_level;
1754 if (level_inc < -viscosity || level_inc > viscosity)
1755 new_node_level = liquid_level + level_inc/viscosity;
1756 else if (level_inc < 0)
1757 new_node_level = liquid_level - 1;
1758 else if (level_inc > 0)
1759 new_node_level = liquid_level + 1;
1760 if (new_node_level != max_node_level)
1761 must_reflow.push_back(p0);
1763 new_node_level = max_node_level;
1765 if (new_node_level >= 0)
1766 new_node_content = liquid_kind;
1768 new_node_content = CONTENT_AIR;
1773 check if anything has changed. if not, just continue with the next node.
1775 if (new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1776 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1777 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1783 update the current node
1785 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1786 if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) {
1787 // set level to last 3 bits, flowing down bit to 4th bit
1788 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1790 // set the liquid level and flow bit to 0
1791 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1793 n0.setContent(new_node_content);
1795 v3s16 blockpos = getNodeBlockPos(p0);
1796 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1798 modified_blocks.insert(blockpos, block);
1799 // If node emits light, MapBlock requires lighting update
1800 if(nodemgr->get(n0).light_source != 0)
1801 lighting_modified_blocks[block->getPos()] = block;
1805 enqueue neighbors for update if neccessary
1807 switch (nodemgr->get(n0.getContent()).liquid_type) {
1809 case LIQUID_FLOWING:
1810 // make sure source flows into all neighboring nodes
1811 for (u16 i = 0; i < num_flows; i++)
1812 if (flows[i].t != NEIGHBOR_UPPER)
1813 m_transforming_liquid.push_back(flows[i].p);
1814 for (u16 i = 0; i < num_airs; i++)
1815 if (airs[i].t != NEIGHBOR_UPPER)
1816 m_transforming_liquid.push_back(airs[i].p);
1819 // this flow has turned to air; neighboring flows might need to do the same
1820 for (u16 i = 0; i < num_flows; i++)
1821 m_transforming_liquid.push_back(flows[i].p);
1825 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1826 while (must_reflow.size() > 0)
1827 m_transforming_liquid.push_back(must_reflow.pop_front());
1828 updateLighting(lighting_modified_blocks, modified_blocks);
1831 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1833 v3s16 blockpos = getNodeBlockPos(p);
1834 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1835 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1837 infostream<<"Map::getNodeMetadata(): Need to emerge "
1838 <<PP(blockpos)<<std::endl;
1839 block = emergeBlock(blockpos, false);
1843 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1847 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1851 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1853 v3s16 blockpos = getNodeBlockPos(p);
1854 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1855 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1857 infostream<<"Map::setNodeMetadata(): Need to emerge "
1858 <<PP(blockpos)<<std::endl;
1859 block = emergeBlock(blockpos, false);
1863 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1867 block->m_node_metadata.set(p_rel, meta);
1870 void Map::removeNodeMetadata(v3s16 p)
1872 v3s16 blockpos = getNodeBlockPos(p);
1873 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1874 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1877 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1881 block->m_node_metadata.remove(p_rel);
1884 NodeTimer Map::getNodeTimer(v3s16 p)
1886 v3s16 blockpos = getNodeBlockPos(p);
1887 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1888 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1890 infostream<<"Map::getNodeTimer(): Need to emerge "
1891 <<PP(blockpos)<<std::endl;
1892 block = emergeBlock(blockpos, false);
1896 infostream<<"WARNING: Map::getNodeTimer(): Block not found"
1900 NodeTimer t = block->m_node_timers.get(p_rel);
1904 void Map::setNodeTimer(v3s16 p, NodeTimer t)
1906 v3s16 blockpos = getNodeBlockPos(p);
1907 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1908 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1910 infostream<<"Map::setNodeTimer(): Need to emerge "
1911 <<PP(blockpos)<<std::endl;
1912 block = emergeBlock(blockpos, false);
1916 infostream<<"WARNING: Map::setNodeTimer(): Block not found"
1920 block->m_node_timers.set(p_rel, t);
1923 void Map::removeNodeTimer(v3s16 p)
1925 v3s16 blockpos = getNodeBlockPos(p);
1926 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1927 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1930 infostream<<"WARNING: Map::removeNodeTimer(): Block not found"
1934 block->m_node_timers.remove(p_rel);
1941 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1942 Map(dout_server, gamedef),
1944 m_map_metadata_changed(true),
1946 m_database_read(NULL),
1947 m_database_write(NULL)
1949 verbosestream<<__FUNCTION_NAME<<std::endl;
1951 //m_chunksize = 8; // Takes a few seconds
1953 if (g_settings->get("fixed_map_seed").empty())
1955 m_seed = (((u64)(myrand()%0xffff)<<0)
1956 + ((u64)(myrand()%0xffff)<<16)
1957 + ((u64)(myrand()%0xffff)<<32)
1958 + ((u64)(myrand()%0xffff)<<48));
1962 m_seed = g_settings->getU64("fixed_map_seed");
1966 Experimental and debug stuff
1973 Try to load map; if not found, create a new one.
1976 m_savedir = savedir;
1977 m_map_saving_enabled = false;
1981 // If directory exists, check contents and load if possible
1982 if(fs::PathExists(m_savedir))
1984 // If directory is empty, it is safe to save into it.
1985 if(fs::GetDirListing(m_savedir).size() == 0)
1987 infostream<<"ServerMap: Empty save directory is valid."
1989 m_map_saving_enabled = true;
1994 // Load map metadata (seed, chunksize)
1997 catch(FileNotGoodException &e){
1998 infostream<<"WARNING: Could not load map metadata"
1999 //<<" Disabling chunk-based generator."
2004 infostream<<"ServerMap: Successfully loaded map "
2005 <<"metadata from "<<savedir
2006 <<", assuming valid save directory."
2007 <<" seed="<<m_seed<<"."
2010 m_map_saving_enabled = true;
2011 // Map loaded, not creating new one
2015 // If directory doesn't exist, it is safe to save to it
2017 m_map_saving_enabled = true;
2020 catch(std::exception &e)
2022 infostream<<"WARNING: ServerMap: Failed to load map from "<<savedir
2023 <<", exception: "<<e.what()<<std::endl;
2024 infostream<<"Please remove the map or fix it."<<std::endl;
2025 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2028 infostream<<"Initializing new map."<<std::endl;
2030 // Create zero sector
2031 emergeSector(v2s16(0,0));
2033 // Initially write whole map
2034 save(MOD_STATE_CLEAN);
2037 ServerMap::~ServerMap()
2039 verbosestream<<__FUNCTION_NAME<<std::endl;
2043 if(m_map_saving_enabled)
2045 // Save only changed parts
2046 save(MOD_STATE_WRITE_AT_UNLOAD);
2047 infostream<<"ServerMap: Saved map to "<<m_savedir<<std::endl;
2051 infostream<<"ServerMap: Map not saved"<<std::endl;
2054 catch(std::exception &e)
2056 infostream<<"ServerMap: Failed to save map to "<<m_savedir
2057 <<", exception: "<<e.what()<<std::endl;
2061 Close database if it was opened
2064 sqlite3_finalize(m_database_read);
2065 if(m_database_write)
2066 sqlite3_finalize(m_database_write);
2068 sqlite3_close(m_database);
2074 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2075 for(; i.atEnd() == false; i++)
2077 MapChunk *chunk = i.getNode()->getValue();
2083 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2085 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2086 if(enable_mapgen_debug_info)
2087 infostream<<"initBlockMake(): "
2088 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<") - "
2089 <<"("<<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z<<")"
2092 //s16 chunksize = 3;
2093 //v3s16 chunk_offset(-1,-1,-1);
2094 //s16 chunksize = 4;
2095 //v3s16 chunk_offset(-1,-1,-1);
2097 v3s16 chunk_offset(-2,-2,-2);
2098 v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize);
2099 v3s16 blockpos_min = blockpos_div * chunksize;
2100 v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1);
2101 blockpos_min += chunk_offset;
2102 blockpos_max += chunk_offset;
2104 //v3s16 extra_borders(1,1,1);
2105 v3s16 extra_borders(1,1,1);
2107 // Do nothing if not inside limits (+-1 because of neighbors)
2108 if(blockpos_over_limit(blockpos_min - extra_borders) ||
2109 blockpos_over_limit(blockpos_max + extra_borders))
2115 data->no_op = false;
2116 data->seed = m_seed;
2117 data->blockpos_min = blockpos_min;
2118 data->blockpos_max = blockpos_max;
2119 data->blockpos_requested = blockpos;
2120 data->nodedef = m_gamedef->ndef();
2123 Create the whole area of this and the neighboring blocks
2126 //TimeTaker timer("initBlockMake() create area");
2128 for(s16 x=blockpos_min.X-extra_borders.X;
2129 x<=blockpos_max.X+extra_borders.X; x++)
2130 for(s16 z=blockpos_min.Z-extra_borders.Z;
2131 z<=blockpos_max.Z+extra_borders.Z; z++)
2133 v2s16 sectorpos(x, z);
2134 // Sector metadata is loaded from disk if not already loaded.
2135 ServerMapSector *sector = createSector(sectorpos);
2138 for(s16 y=blockpos_min.Y-extra_borders.Y;
2139 y<=blockpos_max.Y+extra_borders.Y; y++)
2142 //MapBlock *block = createBlock(p);
2143 // 1) get from memory, 2) load from disk
2144 MapBlock *block = emergeBlock(p, false);
2145 // 3) create a blank one
2148 block = createBlock(p);
2151 Block gets sunlight if this is true.
2153 Refer to the map generator heuristics.
2155 bool ug = mapgen::block_is_underground(data->seed, p);
2156 block->setIsUnderground(ug);
2159 // Lighting will not be valid after make_chunk is called
2160 block->setLightingExpired(true);
2161 // Lighting will be calculated
2162 //block->setLightingExpired(false);
2168 Now we have a big empty area.
2170 Make a ManualMapVoxelManipulator that contains this and the
2174 // The area that contains this block and it's neighbors
2175 v3s16 bigarea_blocks_min = blockpos_min - extra_borders;
2176 v3s16 bigarea_blocks_max = blockpos_max + extra_borders;
2178 data->vmanip = new ManualMapVoxelManipulator(this);
2179 //data->vmanip->setMap(this);
2183 //TimeTaker timer("initBlockMake() initialEmerge");
2184 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2187 // Data is ready now.
2190 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2191 core::map<v3s16, MapBlock*> &changed_blocks)
2193 v3s16 blockpos_min = data->blockpos_min;
2194 v3s16 blockpos_max = data->blockpos_max;
2195 v3s16 blockpos_requested = data->blockpos_requested;
2196 /*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
2197 <<blockpos_requested.Y<<","
2198 <<blockpos_requested.Z<<")"<<std::endl;*/
2200 v3s16 extra_borders(1,1,1);
2204 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2208 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2210 /*infostream<<"Resulting vmanip:"<<std::endl;
2211 data->vmanip.print(infostream);*/
2213 // Make sure affected blocks are loaded
2214 for(s16 x=blockpos_min.X-extra_borders.X;
2215 x<=blockpos_max.X+extra_borders.X; x++)
2216 for(s16 z=blockpos_min.Z-extra_borders.Z;
2217 z<=blockpos_max.Z+extra_borders.Z; z++)
2218 for(s16 y=blockpos_min.Y-extra_borders.Y;
2219 y<=blockpos_max.Y+extra_borders.Y; y++)
2222 // Load from disk if not already in memory
2223 emergeBlock(p, false);
2227 Blit generated stuff to map
2228 NOTE: blitBackAll adds nearly everything to changed_blocks
2232 //TimeTaker timer("finishBlockMake() blitBackAll");
2233 data->vmanip->blitBackAll(&changed_blocks);
2236 if(enable_mapgen_debug_info)
2237 infostream<<"finishBlockMake: changed_blocks.size()="
2238 <<changed_blocks.size()<<std::endl;
2241 Copy transforming liquid information
2243 while(data->transforming_liquid.size() > 0)
2245 v3s16 p = data->transforming_liquid.pop_front();
2246 m_transforming_liquid.push_back(p);
2250 Do stuff in central blocks
2258 TimeTaker t("finishBlockMake lighting update");
2260 core::map<v3s16, MapBlock*> lighting_update_blocks;
2263 for(s16 x=blockpos_min.X-extra_borders.X;
2264 x<=blockpos_max.X+extra_borders.X; x++)
2265 for(s16 z=blockpos_min.Z-extra_borders.Z;
2266 z<=blockpos_max.Z+extra_borders.Z; z++)
2267 for(s16 y=blockpos_min.Y-extra_borders.Y;
2268 y<=blockpos_max.Y+extra_borders.Y; y++)
2271 MapBlock *block = getBlockNoCreateNoEx(p);
2273 lighting_update_blocks.insert(block->getPos(), block);
2276 updateLighting(lighting_update_blocks, changed_blocks);
2280 Set lighting to non-expired state in all of them.
2281 This is cheating, but it is not fast enough if all of them
2282 would actually be updated.
2284 for(s16 x=blockpos_min.X-extra_borders.X;
2285 x<=blockpos_max.X+extra_borders.X; x++)
2286 for(s16 z=blockpos_min.Z-extra_borders.Z;
2287 z<=blockpos_max.Z+extra_borders.Z; z++)
2288 for(s16 y=blockpos_min.Y-extra_borders.Y;
2289 y<=blockpos_max.Y+extra_borders.Y; y++)
2292 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2296 if(enable_mapgen_debug_info == false)
2297 t.stop(true); // Hide output
2302 Go through changed blocks
2304 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2305 i.atEnd() == false; i++)
2307 MapBlock *block = i.getNode()->getValue();
2310 Update day/night difference cache of the MapBlocks
2312 block->expireDayNightDiff();
2314 Set block as modified
2316 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2317 "finishBlockMake expireDayNightDiff");
2321 Set central blocks as generated
2323 for(s16 x=blockpos_min.X; x<=blockpos_max.X; x++)
2324 for(s16 z=blockpos_min.Z; z<=blockpos_max.Z; z++)
2325 for(s16 y=blockpos_min.Y; y<=blockpos_max.Y; y++)
2328 MapBlock *block = getBlockNoCreateNoEx(p);
2330 block->setGenerated(true);
2334 Save changed parts of map
2335 NOTE: Will be saved later.
2337 //save(MOD_STATE_WRITE_AT_UNLOAD);
2339 /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
2340 <<","<<blockpos_requested.Y<<","
2341 <<blockpos_requested.Z<<")"<<std::endl;*/
2343 if(enable_mapgen_debug_info)
2346 Analyze resulting blocks
2348 /*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
2349 for(s16 z=blockpos_min.Z-1; z<=blockpos_max.Z+1; z++)
2350 for(s16 y=blockpos_min.Y-1; y<=blockpos_max.Y+1; y++)*/
2351 for(s16 x=blockpos_min.X-0; x<=blockpos_max.X+0; x++)
2352 for(s16 z=blockpos_min.Z-0; z<=blockpos_max.Z+0; z++)
2353 for(s16 y=blockpos_min.Y-0; y<=blockpos_max.Y+0; y++)
2355 v3s16 p = v3s16(x,y,z);
2356 MapBlock *block = getBlockNoCreateNoEx(p);
2358 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2359 infostream<<"Generated "<<spos<<": "
2360 <<analyze_block(block)<<std::endl;
2365 MapBlock *block = getBlockNoCreateNoEx(blockpos_requested);
2371 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2373 DSTACKF("%s: p2d=(%d,%d)",
2378 Check if it exists already in memory
2380 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2385 Try to load it from disk (with blocks)
2387 //if(loadSectorFull(p2d) == true)
2390 Try to load metadata from disk
2393 if(loadSectorMeta(p2d) == true)
2395 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2398 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2399 throw InvalidPositionException("");
2405 Do not create over-limit
2407 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2408 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2409 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2410 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2411 throw InvalidPositionException("createSector(): pos. over limit");
2414 Generate blank sector
2417 sector = new ServerMapSector(this, p2d, m_gamedef);
2419 // Sector position on map in nodes
2420 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2425 m_sectors.insert(p2d, sector);
2431 This is a quick-hand function for calling makeBlock().
2433 MapBlock * ServerMap::generateBlock(
2435 core::map<v3s16, MapBlock*> &modified_blocks
2438 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2440 /*infostream<<"generateBlock(): "
2441 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2444 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2446 TimeTaker timer("generateBlock");
2448 //MapBlock *block = original_dummy;
2450 v2s16 p2d(p.X, p.Z);
2451 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2454 Do not generate over-limit
2456 if(blockpos_over_limit(p))
2458 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2459 throw InvalidPositionException("generateBlock(): pos. over limit");
2463 Create block make data
2465 mapgen::BlockMakeData data;
2466 initBlockMake(&data, p);
2472 TimeTaker t("mapgen::make_block()");
2473 mapgen::make_block(&data);
2475 if(enable_mapgen_debug_info == false)
2476 t.stop(true); // Hide output
2480 Blit data back on map, update lighting, add mobs and whatever this does
2482 finishBlockMake(&data, modified_blocks);
2487 MapBlock *block = getBlockNoCreateNoEx(p);
2495 bool erroneus_content = false;
2496 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2497 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2498 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2501 MapNode n = block->getNode(p);
2502 if(n.getContent() == CONTENT_IGNORE)
2504 infostream<<"CONTENT_IGNORE at "
2505 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2507 erroneus_content = true;
2511 if(erroneus_content)
2520 Generate a completely empty block
2524 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2525 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2527 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2530 n.setContent(CONTENT_AIR);
2531 block->setNode(v3s16(x0,y0,z0), n);
2537 if(enable_mapgen_debug_info == false)
2538 timer.stop(true); // Hide output
2543 MapBlock * ServerMap::createBlock(v3s16 p)
2545 DSTACKF("%s: p=(%d,%d,%d)",
2546 __FUNCTION_NAME, p.X, p.Y, p.Z);
2549 Do not create over-limit
2551 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2552 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2553 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2554 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2555 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2556 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2557 throw InvalidPositionException("createBlock(): pos. over limit");
2559 v2s16 p2d(p.X, p.Z);
2562 This will create or load a sector if not found in memory.
2563 If block exists on disk, it will be loaded.
2565 NOTE: On old save formats, this will be slow, as it generates
2566 lighting on blocks for them.
2568 ServerMapSector *sector;
2570 sector = (ServerMapSector*)createSector(p2d);
2571 assert(sector->getId() == MAPSECTOR_SERVER);
2573 catch(InvalidPositionException &e)
2575 infostream<<"createBlock: createSector() failed"<<std::endl;
2579 NOTE: This should not be done, or at least the exception
2580 should not be passed on as std::exception, because it
2581 won't be catched at all.
2583 /*catch(std::exception &e)
2585 infostream<<"createBlock: createSector() failed: "
2586 <<e.what()<<std::endl;
2591 Try to get a block from the sector
2594 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2597 if(block->isDummy())
2602 block = sector->createBlankBlock(block_y);
2606 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2608 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2610 p.X, p.Y, p.Z, allow_generate);
2613 MapBlock *block = getBlockNoCreateNoEx(p);
2614 if(block && block->isDummy() == false)
2619 MapBlock *block = loadBlock(p);
2626 core::map<v3s16, MapBlock*> modified_blocks;
2627 MapBlock *block = generateBlock(p, modified_blocks);
2631 event.type = MEET_OTHER;
2634 // Copy modified_blocks to event
2635 for(core::map<v3s16, MapBlock*>::Iterator
2636 i = modified_blocks.getIterator();
2637 i.atEnd()==false; i++)
2639 event.modified_blocks.insert(i.getNode()->getKey(), false);
2643 dispatchEvent(&event);
2652 s16 ServerMap::findGroundLevel(v2s16 p2d)
2656 Uh, just do something random...
2658 // Find existing map from top to down
2661 v3s16 p(p2d.X, max, p2d.Y);
2662 for(; p.Y>min; p.Y--)
2664 MapNode n = getNodeNoEx(p);
2665 if(n.getContent() != CONTENT_IGNORE)
2670 // If this node is not air, go to plan b
2671 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2673 // Search existing walkable and return it
2674 for(; p.Y>min; p.Y--)
2676 MapNode n = getNodeNoEx(p);
2677 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2686 Determine from map generator noise functions
2689 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2692 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2693 //return (s16)level;
2696 void ServerMap::createDatabase() {
2699 e = sqlite3_exec(m_database,
2700 "CREATE TABLE IF NOT EXISTS `blocks` ("
2701 "`pos` INT NOT NULL PRIMARY KEY,"
2704 , NULL, NULL, NULL);
2705 if(e == SQLITE_ABORT)
2706 throw FileNotGoodException("Could not create database structure");
2708 infostream<<"ServerMap: Database structure was created";
2711 void ServerMap::verifyDatabase() {
2716 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2717 bool needs_create = false;
2721 Open the database connection
2724 createDirs(m_savedir);
2726 if(!fs::PathExists(dbp))
2727 needs_create = true;
2729 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2730 if(d != SQLITE_OK) {
2731 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2732 throw FileNotGoodException("Cannot open database file");
2738 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2739 if(d != SQLITE_OK) {
2740 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2741 throw FileNotGoodException("Cannot prepare read statement");
2744 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2745 if(d != SQLITE_OK) {
2746 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2747 throw FileNotGoodException("Cannot prepare write statement");
2750 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2751 if(d != SQLITE_OK) {
2752 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2753 throw FileNotGoodException("Cannot prepare read statement");
2756 infostream<<"ServerMap: Database opened"<<std::endl;
2760 bool ServerMap::loadFromFolders() {
2761 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2766 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2767 return (sqlite3_int64)pos.Z*16777216 +
2768 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2771 void ServerMap::createDirs(std::string path)
2773 if(fs::CreateAllDirs(path) == false)
2775 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2776 <<"\""<<path<<"\""<<std::endl;
2777 throw BaseException("ServerMap failed to create directory");
2781 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2787 snprintf(cc, 9, "%.4x%.4x",
2788 (unsigned int)pos.X&0xffff,
2789 (unsigned int)pos.Y&0xffff);
2791 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2793 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2794 (unsigned int)pos.X&0xfff,
2795 (unsigned int)pos.Y&0xfff);
2797 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2803 v2s16 ServerMap::getSectorPos(std::string dirname)
2807 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2808 assert(spos != std::string::npos);
2809 if(dirname.size() - spos == 8)
2812 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2814 else if(dirname.size() - spos == 3)
2817 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2818 // Sign-extend the 12 bit values up to 16 bits...
2819 if(x&0x800) x|=0xF000;
2820 if(y&0x800) y|=0xF000;
2827 v2s16 pos((s16)x, (s16)y);
2831 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2833 v2s16 p2d = getSectorPos(sectordir);
2835 if(blockfile.size() != 4){
2836 throw InvalidFilenameException("Invalid block filename");
2839 int r = sscanf(blockfile.c_str(), "%4x", &y);
2841 throw InvalidFilenameException("Invalid block filename");
2842 return v3s16(p2d.X, y, p2d.Y);
2845 std::string ServerMap::getBlockFilename(v3s16 p)
2848 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2852 void ServerMap::save(ModifiedState save_level)
2854 DSTACK(__FUNCTION_NAME);
2855 if(m_map_saving_enabled == false)
2857 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2861 if(save_level == MOD_STATE_CLEAN)
2862 infostream<<"ServerMap: Saving whole map, this can take time."
2865 if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
2870 // Profile modified reasons
2871 Profiler modprofiler;
2873 u32 sector_meta_count = 0;
2874 u32 block_count = 0;
2875 u32 block_count_all = 0; // Number of blocks in memory
2877 // Don't do anything with sqlite unless something is really saved
2878 bool save_started = false;
2880 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2881 for(; i.atEnd() == false; i++)
2883 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2884 assert(sector->getId() == MAPSECTOR_SERVER);
2886 if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
2888 saveSectorMeta(sector);
2889 sector_meta_count++;
2891 core::list<MapBlock*> blocks;
2892 sector->getBlocks(blocks);
2893 core::list<MapBlock*>::Iterator j;
2895 for(j=blocks.begin(); j!=blocks.end(); j++)
2897 MapBlock *block = *j;
2901 if(block->getModified() >= save_level)
2906 save_started = true;
2909 modprofiler.add(block->getModifiedReason(), 1);
2914 /*infostream<<"ServerMap: Written block ("
2915 <<block->getPos().X<<","
2916 <<block->getPos().Y<<","
2917 <<block->getPos().Z<<")"
2926 Only print if something happened or saved whole map
2928 if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
2929 || block_count != 0)
2931 infostream<<"ServerMap: Written: "
2932 <<sector_meta_count<<" sector metadata files, "
2933 <<block_count<<" block files"
2934 <<", "<<block_count_all<<" blocks in memory."
2936 PrintInfo(infostream); // ServerMap/ClientMap:
2937 infostream<<"Blocks modified by: "<<std::endl;
2938 modprofiler.print(infostream);
2942 static s32 unsignedToSigned(s32 i, s32 max_positive)
2944 if(i < max_positive)
2947 return i - 2*max_positive;
2950 // modulo of a negative number does not work consistently in C
2951 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2955 return mod - ((-i) % mod);
2958 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2960 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2962 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2964 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2965 return v3s16(x,y,z);
2968 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2970 if(loadFromFolders()){
2971 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2972 <<"all blocks that are stored in flat files"<<std::endl;
2978 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2980 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2981 v3s16 p = getIntegerAsBlock(block_i);
2982 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2988 void ServerMap::saveMapMeta()
2990 DSTACK(__FUNCTION_NAME);
2992 /*infostream<<"ServerMap::saveMapMeta(): "
2996 createDirs(m_savedir);
2998 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2999 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3000 if(os.good() == false)
3002 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3003 <<"could not open"<<fullpath<<std::endl;
3004 throw FileNotGoodException("Cannot open chunk metadata");
3008 params.setU64("seed", m_seed);
3010 params.writeLines(os);
3012 os<<"[end_of_params]\n";
3014 m_map_metadata_changed = false;
3017 void ServerMap::loadMapMeta()
3019 DSTACK(__FUNCTION_NAME);
3021 /*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3024 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3025 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3026 if(is.good() == false)
3028 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3029 <<"could not open"<<fullpath<<std::endl;
3030 throw FileNotGoodException("Cannot open map metadata");
3038 throw SerializationError
3039 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3041 std::getline(is, line);
3042 std::string trimmedline = trim(line);
3043 if(trimmedline == "[end_of_params]")
3045 params.parseConfigLine(line);
3048 m_seed = params.getU64("seed");
3050 verbosestream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3053 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3055 DSTACK(__FUNCTION_NAME);
3056 // Format used for writing
3057 u8 version = SER_FMT_VER_HIGHEST;
3059 v2s16 pos = sector->getPos();
3060 std::string dir = getSectorDir(pos);
3063 std::string fullpath = dir + DIR_DELIM + "meta";
3064 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3065 if(o.good() == false)
3066 throw FileNotGoodException("Cannot open sector metafile");
3068 sector->serialize(o, version);
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.insert(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 MapSector *sector = NULL;
3120 // The directory layout we're going to load from.
3121 // 1 - original sectors/xxxxzzzz/
3122 // 2 - new sectors2/xxx/zzz/
3123 // If we load from anything but the latest structure, we will
3124 // immediately save to the new one, and remove the old.
3126 std::string sectordir1 = getSectorDir(p2d, 1);
3127 std::string sectordir;
3128 if(fs::PathExists(sectordir1))
3130 sectordir = sectordir1;
3135 sectordir = getSectorDir(p2d, 2);
3139 sector = loadSectorMeta(sectordir, loadlayout != 2);
3141 catch(InvalidFilenameException &e)
3145 catch(FileNotGoodException &e)
3149 catch(std::exception &e)
3158 bool ServerMap::loadSectorFull(v2s16 p2d)
3160 DSTACK(__FUNCTION_NAME);
3162 MapSector *sector = NULL;
3164 // The directory layout we're going to load from.
3165 // 1 - original sectors/xxxxzzzz/
3166 // 2 - new sectors2/xxx/zzz/
3167 // If we load from anything but the latest structure, we will
3168 // immediately save to the new one, and remove the old.
3170 std::string sectordir1 = getSectorDir(p2d, 1);
3171 std::string sectordir;
3172 if(fs::PathExists(sectordir1))
3174 sectordir = sectordir1;
3179 sectordir = getSectorDir(p2d, 2);
3183 sector = loadSectorMeta(sectordir, loadlayout != 2);
3185 catch(InvalidFilenameException &e)
3189 catch(FileNotGoodException &e)
3193 catch(std::exception &e)
3201 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3203 std::vector<fs::DirListNode>::iterator i2;
3204 for(i2=list2.begin(); i2!=list2.end(); i2++)
3210 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3212 catch(InvalidFilenameException &e)
3214 // This catches unknown crap in directory
3220 infostream<<"Sector converted to new layout - deleting "<<
3221 sectordir1<<std::endl;
3222 fs::RecursiveDelete(sectordir1);
3229 void ServerMap::beginSave() {
3231 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3232 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3235 void ServerMap::endSave() {
3237 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3238 infostream<<"WARNING: endSave() failed, map might not have saved.";
3241 void ServerMap::saveBlock(MapBlock *block)
3243 DSTACK(__FUNCTION_NAME);
3245 Dummy blocks are not written
3247 if(block->isDummy())
3249 /*v3s16 p = block->getPos();
3250 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3251 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3255 // Format used for writing
3256 u8 version = SER_FMT_VER_HIGHEST;
3258 v3s16 p3d = block->getPos();
3262 v2s16 p2d(p3d.X, p3d.Z);
3263 std::string sectordir = getSectorDir(p2d);
3265 createDirs(sectordir);
3267 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3268 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3269 if(o.good() == false)
3270 throw FileNotGoodException("Cannot open block data");
3273 [0] u8 serialization version
3279 std::ostringstream o(std::ios_base::binary);
3281 o.write((char*)&version, 1);
3284 block->serialize(o, version, true);
3286 // Write block to database
3288 std::string tmp = o.str();
3289 const char *bytes = tmp.c_str();
3291 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3292 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3293 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3294 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3295 int written = sqlite3_step(m_database_write);
3296 if(written != SQLITE_DONE)
3297 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3298 <<sqlite3_errmsg(m_database)<<std::endl;
3299 // Make ready for later reuse
3300 sqlite3_reset(m_database_write);
3302 // We just wrote it to the disk so clear modified flag
3303 block->resetModified();
3306 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3308 DSTACK(__FUNCTION_NAME);
3310 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3313 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3314 if(is.good() == false)
3315 throw FileNotGoodException("Cannot open block file");
3317 v3s16 p3d = getBlockPos(sectordir, blockfile);
3318 v2s16 p2d(p3d.X, p3d.Z);
3320 assert(sector->getPos() == p2d);
3322 u8 version = SER_FMT_VER_INVALID;
3323 is.read((char*)&version, 1);
3326 throw SerializationError("ServerMap::loadBlock(): Failed"
3327 " to read MapBlock version");
3329 /*u32 block_size = MapBlock::serializedLength(version);
3330 SharedBuffer<u8> data(block_size);
3331 is.read((char*)*data, block_size);*/
3333 // This will always return a sector because we're the server
3334 //MapSector *sector = emergeSector(p2d);
3336 MapBlock *block = NULL;
3337 bool created_new = false;
3338 block = sector->getBlockNoCreateNoEx(p3d.Y);
3341 block = sector->createBlankBlockNoInsert(p3d.Y);
3346 block->deSerialize(is, version, true);
3348 // If it's a new block, insert it to the map
3350 sector->insertBlock(block);
3353 Save blocks loaded in old format in new format
3356 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3360 // Should be in database now, so delete the old file
3361 fs::RecursiveDelete(fullpath);
3364 // We just loaded it from the disk, so it's up-to-date.
3365 block->resetModified();
3368 catch(SerializationError &e)
3370 infostream<<"WARNING: Invalid block data on disk "
3371 <<"fullpath="<<fullpath
3372 <<" (SerializationError). "
3373 <<"what()="<<e.what()
3375 //" Ignoring. A new one will be generated.
3378 // TODO: Backup file; name is in fullpath.
3382 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3384 DSTACK(__FUNCTION_NAME);
3387 std::istringstream is(*blob, std::ios_base::binary);
3389 u8 version = SER_FMT_VER_INVALID;
3390 is.read((char*)&version, 1);
3393 throw SerializationError("ServerMap::loadBlock(): Failed"
3394 " to read MapBlock version");
3396 /*u32 block_size = MapBlock::serializedLength(version);
3397 SharedBuffer<u8> data(block_size);
3398 is.read((char*)*data, block_size);*/
3400 // This will always return a sector because we're the server
3401 //MapSector *sector = emergeSector(p2d);
3403 MapBlock *block = NULL;
3404 bool created_new = false;
3405 block = sector->getBlockNoCreateNoEx(p3d.Y);
3408 block = sector->createBlankBlockNoInsert(p3d.Y);
3413 block->deSerialize(is, version, true);
3415 // If it's a new block, insert it to the map
3417 sector->insertBlock(block);
3420 Save blocks loaded in old format in new format
3423 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
3424 // Only save if asked to; no need to update version
3428 // We just loaded it from, so it's up-to-date.
3429 block->resetModified();
3432 catch(SerializationError &e)
3434 errorstream<<"Invalid block data in database"
3435 <<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
3436 <<" (SerializationError): "<<e.what()<<std::endl;
3438 // TODO: Block should be marked as invalid in memory so that it is
3439 // not touched but the game can run
3441 if(g_settings->getBool("ignore_world_load_errors")){
3442 errorstream<<"Ignoring block load error. Duck and cover! "
3443 <<"(ignore_world_load_errors)"<<std::endl;
3445 throw SerializationError("Invalid block data in database");
3451 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3453 DSTACK(__FUNCTION_NAME);
3455 v2s16 p2d(blockpos.X, blockpos.Z);
3457 if(!loadFromFolders()) {
3460 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3461 infostream<<"WARNING: Could not bind block position for load: "
3462 <<sqlite3_errmsg(m_database)<<std::endl;
3463 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3465 Make sure sector is loaded
3467 MapSector *sector = createSector(p2d);
3472 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3473 size_t len = sqlite3_column_bytes(m_database_read, 0);
3475 std::string datastr(data, len);
3477 loadBlock(&datastr, blockpos, sector, false);
3479 sqlite3_step(m_database_read);
3480 // We should never get more than 1 row, so ok to reset
3481 sqlite3_reset(m_database_read);
3483 return getBlockNoCreateNoEx(blockpos);
3485 sqlite3_reset(m_database_read);
3487 // Not found in database, try the files
3490 // The directory layout we're going to load from.
3491 // 1 - original sectors/xxxxzzzz/
3492 // 2 - new sectors2/xxx/zzz/
3493 // If we load from anything but the latest structure, we will
3494 // immediately save to the new one, and remove the old.
3496 std::string sectordir1 = getSectorDir(p2d, 1);
3497 std::string sectordir;
3498 if(fs::PathExists(sectordir1))
3500 sectordir = sectordir1;
3505 sectordir = getSectorDir(p2d, 2);
3509 Make sure sector is loaded
3511 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3515 sector = loadSectorMeta(sectordir, loadlayout != 2);
3517 catch(InvalidFilenameException &e)
3521 catch(FileNotGoodException &e)
3525 catch(std::exception &e)
3532 Make sure file exists
3535 std::string blockfilename = getBlockFilename(blockpos);
3536 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3540 Load block and save it to the database
3542 loadBlock(sectordir, blockfilename, sector, true);
3543 return getBlockNoCreateNoEx(blockpos);
3546 void ServerMap::PrintInfo(std::ostream &out)
3555 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3560 MapVoxelManipulator::~MapVoxelManipulator()
3562 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3566 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3568 TimeTaker timer1("emerge", &emerge_time);
3570 // Units of these are MapBlocks
3571 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3572 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3574 VoxelArea block_area_nodes
3575 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3577 addArea(block_area_nodes);
3579 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3580 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3581 for(s32 x=p_min.X; x<=p_max.X; x++)
3584 core::map<v3s16, bool>::Node *n;
3585 n = m_loaded_blocks.find(p);
3589 bool block_data_inexistent = false;
3592 TimeTaker timer1("emerge load", &emerge_load_time);
3594 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
3595 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3597 a.print(infostream);
3598 infostream<<std::endl;*/
3600 MapBlock *block = m_map->getBlockNoCreate(p);
3601 if(block->isDummy())
3602 block_data_inexistent = true;
3604 block->copyTo(*this);
3606 catch(InvalidPositionException &e)
3608 block_data_inexistent = true;
3611 if(block_data_inexistent)
3613 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3614 // Fill with VOXELFLAG_INEXISTENT
3615 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3616 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3618 s32 i = m_area.index(a.MinEdge.X,y,z);
3619 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3623 m_loaded_blocks.insert(p, !block_data_inexistent);
3626 //infostream<<"emerge done"<<std::endl;
3630 SUGG: Add an option to only update eg. water and air nodes.
3631 This will make it interfere less with important stuff if
3634 void MapVoxelManipulator::blitBack
3635 (core::map<v3s16, MapBlock*> & modified_blocks)
3637 if(m_area.getExtent() == v3s16(0,0,0))
3640 //TimeTaker timer1("blitBack");
3642 /*infostream<<"blitBack(): m_loaded_blocks.size()="
3643 <<m_loaded_blocks.size()<<std::endl;*/
3646 Initialize block cache
3648 v3s16 blockpos_last;
3649 MapBlock *block = NULL;
3650 bool block_checked_in_modified = false;
3652 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3653 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3654 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3658 u8 f = m_flags[m_area.index(p)];
3659 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3662 MapNode &n = m_data[m_area.index(p)];
3664 v3s16 blockpos = getNodeBlockPos(p);
3669 if(block == NULL || blockpos != blockpos_last){
3670 block = m_map->getBlockNoCreate(blockpos);
3671 blockpos_last = blockpos;
3672 block_checked_in_modified = false;
3675 // Calculate relative position in block
3676 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3678 // Don't continue if nothing has changed here
3679 if(block->getNode(relpos) == n)
3682 //m_map->setNode(m_area.MinEdge + p, n);
3683 block->setNode(relpos, n);
3686 Make sure block is in modified_blocks
3688 if(block_checked_in_modified == false)
3690 modified_blocks[blockpos] = block;
3691 block_checked_in_modified = true;
3694 catch(InvalidPositionException &e)
3700 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3701 MapVoxelManipulator(map),
3702 m_create_area(false)
3706 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3710 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3712 // Just create the area so that it can be pointed to
3713 VoxelManipulator::emerge(a, caller_id);
3716 void ManualMapVoxelManipulator::initialEmerge(
3717 v3s16 blockpos_min, v3s16 blockpos_max)
3719 TimeTaker timer1("initialEmerge", &emerge_time);
3721 // Units of these are MapBlocks
3722 v3s16 p_min = blockpos_min;
3723 v3s16 p_max = blockpos_max;
3725 VoxelArea block_area_nodes
3726 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3728 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3731 infostream<<"initialEmerge: area: ";
3732 block_area_nodes.print(infostream);
3733 infostream<<" ("<<size_MB<<"MB)";
3734 infostream<<std::endl;
3737 addArea(block_area_nodes);
3739 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3740 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3741 for(s32 x=p_min.X; x<=p_max.X; x++)
3744 core::map<v3s16, bool>::Node *n;
3745 n = m_loaded_blocks.find(p);
3749 bool block_data_inexistent = false;
3752 TimeTaker timer1("emerge load", &emerge_load_time);
3754 MapBlock *block = m_map->getBlockNoCreate(p);
3755 if(block->isDummy())
3756 block_data_inexistent = true;
3758 block->copyTo(*this);
3760 catch(InvalidPositionException &e)
3762 block_data_inexistent = true;
3765 if(block_data_inexistent)
3768 Mark area inexistent
3770 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3771 // Fill with VOXELFLAG_INEXISTENT
3772 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3773 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3775 s32 i = m_area.index(a.MinEdge.X,y,z);
3776 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3780 m_loaded_blocks.insert(p, !block_data_inexistent);
3784 void ManualMapVoxelManipulator::blitBackAll(
3785 core::map<v3s16, MapBlock*> * modified_blocks)
3787 if(m_area.getExtent() == v3s16(0,0,0))
3791 Copy data of all blocks
3793 for(core::map<v3s16, bool>::Iterator
3794 i = m_loaded_blocks.getIterator();
3795 i.atEnd() == false; i++)
3797 v3s16 p = i.getNode()->getKey();
3798 bool existed = i.getNode()->getValue();
3799 if(existed == false)
3801 // The Great Bug was found using this
3802 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
3803 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3807 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3810 infostream<<"WARNING: "<<__FUNCTION_NAME
3811 <<": got NULL block "
3812 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3817 block->copyFrom(*this);
3820 modified_blocks->insert(p, block);