3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
30 #include "nodemetadata.h"
36 SQLite format specification:
37 - Initially only replaces sectors/ and sectors2/
44 Map::Map(std::ostream &dout):
48 /*m_sector_mutex.Init();
49 assert(m_sector_mutex.IsInitialized());*/
57 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
58 for(; i.atEnd() == false; i++)
60 MapSector *sector = i.getNode()->getValue();
65 void Map::addEventReceiver(MapEventReceiver *event_receiver)
67 m_event_receivers.insert(event_receiver, false);
70 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
72 if(m_event_receivers.find(event_receiver) == NULL)
74 m_event_receivers.remove(event_receiver);
77 void Map::dispatchEvent(MapEditEvent *event)
79 for(core::map<MapEventReceiver*, bool>::Iterator
80 i = m_event_receivers.getIterator();
81 i.atEnd()==false; i++)
83 MapEventReceiver* event_receiver = i.getNode()->getKey();
84 event_receiver->onMapEditEvent(event);
88 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
90 if(m_sector_cache != NULL && p == m_sector_cache_p){
91 MapSector * sector = m_sector_cache;
95 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
100 MapSector *sector = n->getValue();
102 // Cache the last result
103 m_sector_cache_p = p;
104 m_sector_cache = sector;
109 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
111 return getSectorNoGenerateNoExNoLock(p);
114 MapSector * Map::getSectorNoGenerate(v2s16 p)
116 MapSector *sector = getSectorNoGenerateNoEx(p);
118 throw InvalidPositionException();
123 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerateNoEx(p2d);
129 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
133 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
135 MapBlock *block = getBlockNoCreateNoEx(p3d);
137 throw InvalidPositionException();
141 bool Map::isNodeUnderground(v3s16 p)
143 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock * block = getBlockNoCreate(blockpos);
146 return block->getIsUnderground();
148 catch(InvalidPositionException &e)
154 bool Map::isValidPosition(v3s16 p)
156 v3s16 blockpos = getNodeBlockPos(p);
157 MapBlock *block = getBlockNoCreate(blockpos);
158 return (block != NULL);
161 // Returns a CONTENT_IGNORE node if not found
162 MapNode Map::getNodeNoEx(v3s16 p)
164 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock *block = getBlockNoCreateNoEx(blockpos);
167 return MapNode(CONTENT_IGNORE);
168 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
169 return block->getNodeNoCheck(relpos);
172 // throws InvalidPositionException if not found
173 MapNode Map::getNode(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock *block = getBlockNoCreateNoEx(blockpos);
178 throw InvalidPositionException();
179 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
180 return block->getNodeNoCheck(relpos);
183 // throws InvalidPositionException if not found
184 void Map::setNode(v3s16 p, MapNode & n)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreate(blockpos);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 block->setNodeNoCheck(relpos, n);
194 Goes recursively through the neighbours of the node.
196 Alters only transparent nodes.
198 If the lighting of the neighbour is lower than the lighting of
199 the node was (before changing it to 0 at the step before), the
200 lighting of the neighbour is set to 0 and then the same stuff
201 repeats for the neighbour.
203 The ending nodes of the routine are stored in light_sources.
204 This is useful when a light is removed. In such case, this
205 routine can be called for the light node and then again for
206 light_sources to re-light the area without the removed light.
208 values of from_nodes are lighting values.
210 void Map::unspreadLight(enum LightBank bank,
211 core::map<v3s16, u8> & from_nodes,
212 core::map<v3s16, bool> & light_sources,
213 core::map<v3s16, MapBlock*> & modified_blocks)
216 v3s16(0,0,1), // back
218 v3s16(1,0,0), // right
219 v3s16(0,0,-1), // front
220 v3s16(0,-1,0), // bottom
221 v3s16(-1,0,0), // left
224 if(from_nodes.size() == 0)
227 u32 blockchangecount = 0;
229 core::map<v3s16, u8> unlighted_nodes;
230 core::map<v3s16, u8>::Iterator j;
231 j = from_nodes.getIterator();
234 Initialize block cache
237 MapBlock *block = NULL;
238 // Cache this a bit, too
239 bool block_checked_in_modified = false;
241 for(; j.atEnd() == false; j++)
243 v3s16 pos = j.getNode()->getKey();
244 v3s16 blockpos = getNodeBlockPos(pos);
246 // Only fetch a new block if the block position has changed
248 if(block == NULL || blockpos != blockpos_last){
249 block = getBlockNoCreate(blockpos);
250 blockpos_last = blockpos;
252 block_checked_in_modified = false;
256 catch(InvalidPositionException &e)
264 // Calculate relative position in block
265 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
267 // Get node straight from the block
268 MapNode n = block->getNode(relpos);
270 u8 oldlight = j.getNode()->getValue();
272 // Loop through 6 neighbors
273 for(u16 i=0; i<6; i++)
275 // Get the position of the neighbor node
276 v3s16 n2pos = pos + dirs[i];
278 // Get the block where the node is located
279 v3s16 blockpos = getNodeBlockPos(n2pos);
283 // Only fetch a new block if the block position has changed
285 if(block == NULL || blockpos != blockpos_last){
286 block = getBlockNoCreate(blockpos);
287 blockpos_last = blockpos;
289 block_checked_in_modified = false;
293 catch(InvalidPositionException &e)
298 // Calculate relative position in block
299 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
300 // Get node straight from the block
301 MapNode n2 = block->getNode(relpos);
303 bool changed = false;
305 //TODO: Optimize output by optimizing light_sources?
308 If the neighbor is dimmer than what was specified
309 as oldlight (the light of the previous node)
311 if(n2.getLight(bank) < oldlight)
314 And the neighbor is transparent and it has some light
316 if(n2.light_propagates() && n2.getLight(bank) != 0)
319 Set light to 0 and add to queue
322 u8 current_light = n2.getLight(bank);
323 n2.setLight(bank, 0);
324 block->setNode(relpos, n2);
326 unlighted_nodes.insert(n2pos, current_light);
330 Remove from light_sources if it is there
331 NOTE: This doesn't happen nearly at all
333 /*if(light_sources.find(n2pos))
335 std::cout<<"Removed from light_sources"<<std::endl;
336 light_sources.remove(n2pos);
341 if(light_sources.find(n2pos) != NULL)
342 light_sources.remove(n2pos);*/
345 light_sources.insert(n2pos, true);
348 // Add to modified_blocks
349 if(changed == true && block_checked_in_modified == false)
351 // If the block is not found in modified_blocks, add.
352 if(modified_blocks.find(blockpos) == NULL)
354 modified_blocks.insert(blockpos, block);
356 block_checked_in_modified = true;
359 catch(InvalidPositionException &e)
366 /*dstream<<"unspreadLight(): Changed block "
367 <<blockchangecount<<" times"
368 <<" for "<<from_nodes.size()<<" nodes"
371 if(unlighted_nodes.size() > 0)
372 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
376 A single-node wrapper of the above
378 void Map::unLightNeighbors(enum LightBank bank,
379 v3s16 pos, u8 lightwas,
380 core::map<v3s16, bool> & light_sources,
381 core::map<v3s16, MapBlock*> & modified_blocks)
383 core::map<v3s16, u8> from_nodes;
384 from_nodes.insert(pos, lightwas);
386 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
390 Lights neighbors of from_nodes, collects all them and then
393 void Map::spreadLight(enum LightBank bank,
394 core::map<v3s16, bool> & from_nodes,
395 core::map<v3s16, MapBlock*> & modified_blocks)
397 const v3s16 dirs[6] = {
398 v3s16(0,0,1), // back
400 v3s16(1,0,0), // right
401 v3s16(0,0,-1), // front
402 v3s16(0,-1,0), // bottom
403 v3s16(-1,0,0), // left
406 if(from_nodes.size() == 0)
409 u32 blockchangecount = 0;
411 core::map<v3s16, bool> lighted_nodes;
412 core::map<v3s16, bool>::Iterator j;
413 j = from_nodes.getIterator();
416 Initialize block cache
419 MapBlock *block = NULL;
420 // Cache this a bit, too
421 bool block_checked_in_modified = false;
423 for(; j.atEnd() == false; j++)
424 //for(; j != from_nodes.end(); j++)
426 v3s16 pos = j.getNode()->getKey();
428 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
429 v3s16 blockpos = getNodeBlockPos(pos);
431 // Only fetch a new block if the block position has changed
433 if(block == NULL || blockpos != blockpos_last){
434 block = getBlockNoCreate(blockpos);
435 blockpos_last = blockpos;
437 block_checked_in_modified = false;
441 catch(InvalidPositionException &e)
449 // Calculate relative position in block
450 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
452 // Get node straight from the block
453 MapNode n = block->getNode(relpos);
455 u8 oldlight = n.getLight(bank);
456 u8 newlight = diminish_light(oldlight);
458 // Loop through 6 neighbors
459 for(u16 i=0; i<6; i++){
460 // Get the position of the neighbor node
461 v3s16 n2pos = pos + dirs[i];
463 // Get the block where the node is located
464 v3s16 blockpos = getNodeBlockPos(n2pos);
468 // Only fetch a new block if the block position has changed
470 if(block == NULL || blockpos != blockpos_last){
471 block = getBlockNoCreate(blockpos);
472 blockpos_last = blockpos;
474 block_checked_in_modified = false;
478 catch(InvalidPositionException &e)
483 // Calculate relative position in block
484 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
485 // Get node straight from the block
486 MapNode n2 = block->getNode(relpos);
488 bool changed = false;
490 If the neighbor is brighter than the current node,
491 add to list (it will light up this node on its turn)
493 if(n2.getLight(bank) > undiminish_light(oldlight))
495 lighted_nodes.insert(n2pos, true);
496 //lighted_nodes.push_back(n2pos);
500 If the neighbor is dimmer than how much light this node
501 would spread on it, add to list
503 if(n2.getLight(bank) < newlight)
505 if(n2.light_propagates())
507 n2.setLight(bank, newlight);
508 block->setNode(relpos, n2);
509 lighted_nodes.insert(n2pos, true);
510 //lighted_nodes.push_back(n2pos);
515 // Add to modified_blocks
516 if(changed == true && block_checked_in_modified == false)
518 // If the block is not found in modified_blocks, add.
519 if(modified_blocks.find(blockpos) == NULL)
521 modified_blocks.insert(blockpos, block);
523 block_checked_in_modified = true;
526 catch(InvalidPositionException &e)
533 /*dstream<<"spreadLight(): Changed block "
534 <<blockchangecount<<" times"
535 <<" for "<<from_nodes.size()<<" nodes"
538 if(lighted_nodes.size() > 0)
539 spreadLight(bank, lighted_nodes, modified_blocks);
543 A single-node source variation of the above.
545 void Map::lightNeighbors(enum LightBank bank,
547 core::map<v3s16, MapBlock*> & modified_blocks)
549 core::map<v3s16, bool> from_nodes;
550 from_nodes.insert(pos, true);
551 spreadLight(bank, from_nodes, modified_blocks);
554 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
557 v3s16(0,0,1), // back
559 v3s16(1,0,0), // right
560 v3s16(0,0,-1), // front
561 v3s16(0,-1,0), // bottom
562 v3s16(-1,0,0), // left
565 u8 brightest_light = 0;
566 v3s16 brightest_pos(0,0,0);
567 bool found_something = false;
569 // Loop through 6 neighbors
570 for(u16 i=0; i<6; i++){
571 // Get the position of the neighbor node
572 v3s16 n2pos = p + dirs[i];
577 catch(InvalidPositionException &e)
581 if(n2.getLight(bank) > brightest_light || found_something == false){
582 brightest_light = n2.getLight(bank);
583 brightest_pos = n2pos;
584 found_something = true;
588 if(found_something == false)
589 throw InvalidPositionException();
591 return brightest_pos;
595 Propagates sunlight down from a node.
596 Starting point gets sunlight.
598 Returns the lowest y value of where the sunlight went.
600 Mud is turned into grass in where the sunlight stops.
602 s16 Map::propagateSunlight(v3s16 start,
603 core::map<v3s16, MapBlock*> & modified_blocks)
608 v3s16 pos(start.X, y, start.Z);
610 v3s16 blockpos = getNodeBlockPos(pos);
613 block = getBlockNoCreate(blockpos);
615 catch(InvalidPositionException &e)
620 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
621 MapNode n = block->getNode(relpos);
623 if(n.sunlight_propagates())
625 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
626 block->setNode(relpos, n);
628 modified_blocks.insert(blockpos, block);
632 /*// Turn mud into grass
633 if(n.d == CONTENT_MUD)
636 block->setNode(relpos, n);
637 modified_blocks.insert(blockpos, block);
640 // Sunlight goes no further
647 void Map::updateLighting(enum LightBank bank,
648 core::map<v3s16, MapBlock*> & a_blocks,
649 core::map<v3s16, MapBlock*> & modified_blocks)
651 /*m_dout<<DTIME<<"Map::updateLighting(): "
652 <<a_blocks.size()<<" blocks."<<std::endl;*/
654 //TimeTaker timer("updateLighting");
658 //u32 count_was = modified_blocks.size();
660 core::map<v3s16, MapBlock*> blocks_to_update;
662 core::map<v3s16, bool> light_sources;
664 core::map<v3s16, u8> unlight_from;
666 core::map<v3s16, MapBlock*>::Iterator i;
667 i = a_blocks.getIterator();
668 for(; i.atEnd() == false; i++)
670 MapBlock *block = i.getNode()->getValue();
674 // Don't bother with dummy blocks.
678 v3s16 pos = block->getPos();
679 modified_blocks.insert(pos, block);
681 blocks_to_update.insert(pos, block);
684 Clear all light from block
686 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
687 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
688 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
693 MapNode n = block->getNode(v3s16(x,y,z));
694 u8 oldlight = n.getLight(bank);
696 block->setNode(v3s16(x,y,z), n);
698 // Collect borders for unlighting
699 if(x==0 || x == MAP_BLOCKSIZE-1
700 || y==0 || y == MAP_BLOCKSIZE-1
701 || z==0 || z == MAP_BLOCKSIZE-1)
703 v3s16 p_map = p + v3s16(
706 MAP_BLOCKSIZE*pos.Z);
707 unlight_from.insert(p_map, oldlight);
710 catch(InvalidPositionException &e)
713 This would happen when dealing with a
717 dstream<<"updateLighting(): InvalidPositionException"
722 if(bank == LIGHTBANK_DAY)
724 bool bottom_valid = block->propagateSunlight(light_sources);
726 // If bottom is valid, we're done.
730 else if(bank == LIGHTBANK_NIGHT)
732 // For night lighting, sunlight is not propagated
737 // Invalid lighting bank
741 /*dstream<<"Bottom for sunlight-propagated block ("
742 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
745 // Bottom sunlight is not valid; get the block and loop to it
749 block = getBlockNoCreate(pos);
751 catch(InvalidPositionException &e)
760 Enable this to disable proper lighting for speeding up map
761 generation for testing or whatever
764 //if(g_settings.get(""))
766 core::map<v3s16, MapBlock*>::Iterator i;
767 i = blocks_to_update.getIterator();
768 for(; i.atEnd() == false; i++)
770 MapBlock *block = i.getNode()->getValue();
771 v3s16 p = block->getPos();
772 block->setLightingExpired(false);
780 TimeTaker timer("unspreadLight");
781 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
786 u32 diff = modified_blocks.size() - count_was;
787 count_was = modified_blocks.size();
788 dstream<<"unspreadLight modified "<<diff<<std::endl;
792 TimeTaker timer("spreadLight");
793 spreadLight(bank, light_sources, modified_blocks);
798 u32 diff = modified_blocks.size() - count_was;
799 count_was = modified_blocks.size();
800 dstream<<"spreadLight modified "<<diff<<std::endl;
805 //MapVoxelManipulator vmanip(this);
807 // Make a manual voxel manipulator and load all the blocks
808 // that touch the requested blocks
809 ManualMapVoxelManipulator vmanip(this);
810 core::map<v3s16, MapBlock*>::Iterator i;
811 i = blocks_to_update.getIterator();
812 for(; i.atEnd() == false; i++)
814 MapBlock *block = i.getNode()->getValue();
815 v3s16 p = block->getPos();
817 // Add all surrounding blocks
818 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
821 Add all surrounding blocks that have up-to-date lighting
822 NOTE: This doesn't quite do the job (not everything
823 appropriate is lighted)
825 /*for(s16 z=-1; z<=1; z++)
826 for(s16 y=-1; y<=1; y++)
827 for(s16 x=-1; x<=1; x++)
830 MapBlock *block = getBlockNoCreateNoEx(p);
835 if(block->getLightingExpired())
837 vmanip.initialEmerge(p, p);
840 // Lighting of block will be updated completely
841 block->setLightingExpired(false);
845 //TimeTaker timer("unSpreadLight");
846 vmanip.unspreadLight(bank, unlight_from, light_sources);
849 //TimeTaker timer("spreadLight");
850 vmanip.spreadLight(bank, light_sources);
853 //TimeTaker timer("blitBack");
854 vmanip.blitBack(modified_blocks);
856 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
860 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
863 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
864 core::map<v3s16, MapBlock*> & modified_blocks)
866 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
867 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
870 Update information about whether day and night light differ
872 for(core::map<v3s16, MapBlock*>::Iterator
873 i = modified_blocks.getIterator();
874 i.atEnd() == false; i++)
876 MapBlock *block = i.getNode()->getValue();
877 block->updateDayNightDiff();
883 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
884 core::map<v3s16, MapBlock*> &modified_blocks)
887 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
888 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
891 From this node to nodes underneath:
892 If lighting is sunlight (1.0), unlight neighbours and
897 v3s16 toppos = p + v3s16(0,1,0);
898 v3s16 bottompos = p + v3s16(0,-1,0);
900 bool node_under_sunlight = true;
901 core::map<v3s16, bool> light_sources;
904 If there is a node at top and it doesn't have sunlight,
905 there has not been any sunlight going down.
907 Otherwise there probably is.
910 MapNode topnode = getNode(toppos);
912 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
913 node_under_sunlight = false;
915 catch(InvalidPositionException &e)
921 If the new node is solid and there is grass below, change it to mud
923 if(content_features(n.d).walkable == true)
926 MapNode bottomnode = getNode(bottompos);
928 if(bottomnode.d == CONTENT_GRASS
929 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
931 bottomnode.d = CONTENT_MUD;
932 setNode(bottompos, bottomnode);
935 catch(InvalidPositionException &e)
943 If the new node is mud and it is under sunlight, change it
946 if(n.d == CONTENT_MUD && node_under_sunlight)
953 Remove all light that has come out of this node
956 enum LightBank banks[] =
961 for(s32 i=0; i<2; i++)
963 enum LightBank bank = banks[i];
965 u8 lightwas = getNode(p).getLight(bank);
967 // Add the block of the added node to modified_blocks
968 v3s16 blockpos = getNodeBlockPos(p);
969 MapBlock * block = getBlockNoCreate(blockpos);
970 assert(block != NULL);
971 modified_blocks.insert(blockpos, block);
973 assert(isValidPosition(p));
975 // Unlight neighbours of node.
976 // This means setting light of all consequent dimmer nodes
978 // This also collects the nodes at the border which will spread
979 // light again into this.
980 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
986 If node lets sunlight through and is under sunlight, it has
989 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
991 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
995 Set the node on the map
1004 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
1007 NodeMetadata *meta = meta_proto->clone();
1008 setNodeMetadata(p, meta);
1012 If node is under sunlight and doesn't let sunlight through,
1013 take all sunlighted nodes under it and clear light from them
1014 and from where the light has been spread.
1015 TODO: This could be optimized by mass-unlighting instead
1018 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
1022 //m_dout<<DTIME<<"y="<<y<<std::endl;
1023 v3s16 n2pos(p.X, y, p.Z);
1027 n2 = getNode(n2pos);
1029 catch(InvalidPositionException &e)
1034 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1036 unLightNeighbors(LIGHTBANK_DAY,
1037 n2pos, n2.getLight(LIGHTBANK_DAY),
1038 light_sources, modified_blocks);
1039 n2.setLight(LIGHTBANK_DAY, 0);
1047 for(s32 i=0; i<2; i++)
1049 enum LightBank bank = banks[i];
1052 Spread light from all nodes that might be capable of doing so
1054 spreadLight(bank, light_sources, modified_blocks);
1058 Update information about whether day and night light differ
1060 for(core::map<v3s16, MapBlock*>::Iterator
1061 i = modified_blocks.getIterator();
1062 i.atEnd() == false; i++)
1064 MapBlock *block = i.getNode()->getValue();
1065 block->updateDayNightDiff();
1069 Add neighboring liquid nodes and the node itself if it is
1070 liquid (=water node was added) to transform queue.
1073 v3s16(0,0,0), // self
1074 v3s16(0,0,1), // back
1075 v3s16(0,1,0), // top
1076 v3s16(1,0,0), // right
1077 v3s16(0,0,-1), // front
1078 v3s16(0,-1,0), // bottom
1079 v3s16(-1,0,0), // left
1081 for(u16 i=0; i<7; i++)
1086 v3s16 p2 = p + dirs[i];
1088 MapNode n2 = getNode(p2);
1089 if(content_liquid(n2.d))
1091 m_transforming_liquid.push_back(p2);
1094 }catch(InvalidPositionException &e)
1102 void Map::removeNodeAndUpdate(v3s16 p,
1103 core::map<v3s16, MapBlock*> &modified_blocks)
1105 /*PrintInfo(m_dout);
1106 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1107 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1109 bool node_under_sunlight = true;
1111 v3s16 toppos = p + v3s16(0,1,0);
1113 // Node will be replaced with this
1114 u8 replace_material = CONTENT_AIR;
1117 If there is a node at top and it doesn't have sunlight,
1118 there will be no sunlight going down.
1121 MapNode topnode = getNode(toppos);
1123 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1124 node_under_sunlight = false;
1126 catch(InvalidPositionException &e)
1130 core::map<v3s16, bool> light_sources;
1132 enum LightBank banks[] =
1137 for(s32 i=0; i<2; i++)
1139 enum LightBank bank = banks[i];
1142 Unlight neighbors (in case the node is a light source)
1144 unLightNeighbors(bank, p,
1145 getNode(p).getLight(bank),
1146 light_sources, modified_blocks);
1150 Remove node metadata
1153 removeNodeMetadata(p);
1157 This also clears the lighting.
1161 n.d = replace_material;
1164 for(s32 i=0; i<2; i++)
1166 enum LightBank bank = banks[i];
1169 Recalculate lighting
1171 spreadLight(bank, light_sources, modified_blocks);
1174 // Add the block of the removed node to modified_blocks
1175 v3s16 blockpos = getNodeBlockPos(p);
1176 MapBlock * block = getBlockNoCreate(blockpos);
1177 assert(block != NULL);
1178 modified_blocks.insert(blockpos, block);
1181 If the removed node was under sunlight, propagate the
1182 sunlight down from it and then light all neighbors
1183 of the propagated blocks.
1185 if(node_under_sunlight)
1187 s16 ybottom = propagateSunlight(p, modified_blocks);
1188 /*m_dout<<DTIME<<"Node was under sunlight. "
1189 "Propagating sunlight";
1190 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1192 for(; y >= ybottom; y--)
1194 v3s16 p2(p.X, y, p.Z);
1195 /*m_dout<<DTIME<<"lighting neighbors of node ("
1196 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1198 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1203 // Set the lighting of this node to 0
1204 // TODO: Is this needed? Lighting is cleared up there already.
1206 MapNode n = getNode(p);
1207 n.setLight(LIGHTBANK_DAY, 0);
1210 catch(InvalidPositionException &e)
1216 for(s32 i=0; i<2; i++)
1218 enum LightBank bank = banks[i];
1220 // Get the brightest neighbour node and propagate light from it
1221 v3s16 n2p = getBrightestNeighbour(bank, p);
1223 MapNode n2 = getNode(n2p);
1224 lightNeighbors(bank, n2p, modified_blocks);
1226 catch(InvalidPositionException &e)
1232 Update information about whether day and night light differ
1234 for(core::map<v3s16, MapBlock*>::Iterator
1235 i = modified_blocks.getIterator();
1236 i.atEnd() == false; i++)
1238 MapBlock *block = i.getNode()->getValue();
1239 block->updateDayNightDiff();
1243 Add neighboring liquid nodes to transform queue.
1245 Also add horizontal neighbors of node on top of removed node
1246 because they could be affected of the water on top flowing
1247 down instead of into them.
1250 v3s16(0,0,1), // back
1251 v3s16(0,1,0), // top
1252 v3s16(1,1,0), // topright
1253 v3s16(-1,1,0), // topleft
1254 v3s16(0,1,1), // topback
1255 v3s16(0,1,-1), // topfront
1256 v3s16(1,0,0), // right
1257 v3s16(0,0,-1), // front
1258 v3s16(0,-1,0), // bottom
1259 v3s16(-1,0,0), // left
1261 for(u16 i=0; i<10; i++)
1266 v3s16 p2 = p + dirs[i];
1268 MapNode n2 = getNode(p2);
1269 if(content_liquid(n2.d))
1271 m_transforming_liquid.push_back(p2);
1274 }catch(InvalidPositionException &e)
1280 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1283 event.type = MEET_ADDNODE;
1287 bool succeeded = true;
1289 core::map<v3s16, MapBlock*> modified_blocks;
1290 addNodeAndUpdate(p, n, modified_blocks);
1292 // Copy modified_blocks to event
1293 for(core::map<v3s16, MapBlock*>::Iterator
1294 i = modified_blocks.getIterator();
1295 i.atEnd()==false; i++)
1297 event.modified_blocks.insert(i.getNode()->getKey(), false);
1300 catch(InvalidPositionException &e){
1304 dispatchEvent(&event);
1309 bool Map::removeNodeWithEvent(v3s16 p)
1312 event.type = MEET_REMOVENODE;
1315 bool succeeded = true;
1317 core::map<v3s16, MapBlock*> modified_blocks;
1318 removeNodeAndUpdate(p, modified_blocks);
1320 // Copy modified_blocks to event
1321 for(core::map<v3s16, MapBlock*>::Iterator
1322 i = modified_blocks.getIterator();
1323 i.atEnd()==false; i++)
1325 event.modified_blocks.insert(i.getNode()->getKey(), false);
1328 catch(InvalidPositionException &e){
1332 dispatchEvent(&event);
1337 bool Map::dayNightDiffed(v3s16 blockpos)
1340 v3s16 p = blockpos + v3s16(0,0,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1348 v3s16 p = blockpos + v3s16(-1,0,0);
1349 MapBlock *b = getBlockNoCreate(p);
1350 if(b->dayNightDiffed())
1353 catch(InvalidPositionException &e){}
1355 v3s16 p = blockpos + v3s16(0,-1,0);
1356 MapBlock *b = getBlockNoCreate(p);
1357 if(b->dayNightDiffed())
1360 catch(InvalidPositionException &e){}
1362 v3s16 p = blockpos + v3s16(0,0,-1);
1363 MapBlock *b = getBlockNoCreate(p);
1364 if(b->dayNightDiffed())
1367 catch(InvalidPositionException &e){}
1370 v3s16 p = blockpos + v3s16(1,0,0);
1371 MapBlock *b = getBlockNoCreate(p);
1372 if(b->dayNightDiffed())
1375 catch(InvalidPositionException &e){}
1377 v3s16 p = blockpos + v3s16(0,1,0);
1378 MapBlock *b = getBlockNoCreate(p);
1379 if(b->dayNightDiffed())
1382 catch(InvalidPositionException &e){}
1384 v3s16 p = blockpos + v3s16(0,0,1);
1385 MapBlock *b = getBlockNoCreate(p);
1386 if(b->dayNightDiffed())
1389 catch(InvalidPositionException &e){}
1395 Updates usage timers
1397 void Map::timerUpdate(float dtime, float unload_timeout,
1398 core::list<v3s16> *unloaded_blocks)
1400 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1402 core::list<v2s16> sector_deletion_queue;
1403 u32 deleted_blocks_count = 0;
1404 u32 saved_blocks_count = 0;
1406 core::map<v2s16, MapSector*>::Iterator si;
1408 si = m_sectors.getIterator();
1409 for(; si.atEnd() == false; si++)
1411 MapSector *sector = si.getNode()->getValue();
1413 bool all_blocks_deleted = true;
1415 core::list<MapBlock*> blocks;
1416 sector->getBlocks(blocks);
1417 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1418 i != blocks.end(); i++)
1420 MapBlock *block = (*i);
1422 block->incrementUsageTimer(dtime);
1424 if(block->getUsageTimer() > unload_timeout)
1426 v3s16 p = block->getPos();
1429 if(block->getModified() != MOD_STATE_CLEAN
1430 && save_before_unloading)
1433 saved_blocks_count++;
1436 // Delete from memory
1437 sector->deleteBlock(block);
1440 unloaded_blocks->push_back(p);
1442 deleted_blocks_count++;
1446 all_blocks_deleted = false;
1450 if(all_blocks_deleted)
1452 sector_deletion_queue.push_back(si.getNode()->getKey());
1456 // Finally delete the empty sectors
1457 deleteSectors(sector_deletion_queue);
1459 if(deleted_blocks_count != 0)
1461 PrintInfo(dstream); // ServerMap/ClientMap:
1462 dstream<<"Unloaded "<<deleted_blocks_count
1463 <<" blocks from memory";
1464 if(save_before_unloading)
1465 dstream<<", of which "<<saved_blocks_count<<" were written";
1466 dstream<<"."<<std::endl;
1470 void Map::deleteSectors(core::list<v2s16> &list)
1472 core::list<v2s16>::Iterator j;
1473 for(j=list.begin(); j!=list.end(); j++)
1475 MapSector *sector = m_sectors[*j];
1476 // If sector is in sector cache, remove it from there
1477 if(m_sector_cache == sector)
1478 m_sector_cache = NULL;
1479 // Remove from map and delete
1480 m_sectors.remove(*j);
1486 void Map::unloadUnusedData(float timeout,
1487 core::list<v3s16> *deleted_blocks)
1489 core::list<v2s16> sector_deletion_queue;
1490 u32 deleted_blocks_count = 0;
1491 u32 saved_blocks_count = 0;
1493 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1494 for(; si.atEnd() == false; si++)
1496 MapSector *sector = si.getNode()->getValue();
1498 bool all_blocks_deleted = true;
1500 core::list<MapBlock*> blocks;
1501 sector->getBlocks(blocks);
1502 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1503 i != blocks.end(); i++)
1505 MapBlock *block = (*i);
1507 if(block->getUsageTimer() > timeout)
1510 if(block->getModified() != MOD_STATE_CLEAN)
1513 saved_blocks_count++;
1515 // Delete from memory
1516 sector->deleteBlock(block);
1517 deleted_blocks_count++;
1521 all_blocks_deleted = false;
1525 if(all_blocks_deleted)
1527 sector_deletion_queue.push_back(si.getNode()->getKey());
1531 deleteSectors(sector_deletion_queue);
1533 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1534 <<", of which "<<saved_blocks_count<<" were wr."
1537 //return sector_deletion_queue.getSize();
1538 //return deleted_blocks_count;
1542 void Map::PrintInfo(std::ostream &out)
1547 #define WATER_DROP_BOOST 4
1549 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1551 DSTACK(__FUNCTION_NAME);
1552 //TimeTaker timer("transformLiquids()");
1555 u32 initial_size = m_transforming_liquid.size();
1557 /*if(initial_size != 0)
1558 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1560 while(m_transforming_liquid.size() != 0)
1563 Get a queued transforming liquid node
1565 v3s16 p0 = m_transforming_liquid.pop_front();
1567 MapNode n0 = getNodeNoEx(p0);
1569 // Don't deal with non-liquids
1570 if(content_liquid(n0.d) == false)
1573 bool is_source = !content_flowing_liquid(n0.d);
1575 u8 liquid_level = 8;
1576 if(is_source == false)
1577 liquid_level = n0.param2 & 0x0f;
1579 // Turn possible source into non-source
1580 u8 nonsource_c = make_liquid_flowing(n0.d);
1582 // Counts surrounding liquid source blocks
1583 u8 surrounding_sources = 0;
1586 If not source, check that some node flows into this one
1587 and what is the level of liquid in this one
1589 if(is_source == false)
1591 s8 new_liquid_level_max = -1;
1593 v3s16 dirs_from[5] = {
1594 v3s16(0,1,0), // top
1595 v3s16(0,0,1), // back
1596 v3s16(1,0,0), // right
1597 v3s16(0,0,-1), // front
1598 v3s16(-1,0,0), // left
1600 for(u16 i=0; i<5; i++)
1602 bool from_top = (i==0);
1604 v3s16 p2 = p0 + dirs_from[i];
1605 MapNode n2 = getNodeNoEx(p2);
1607 if(content_liquid(n2.d))
1609 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1610 // Check that the liquids are the same type
1611 if(n2_nonsource_c != nonsource_c)
1613 dstream<<"WARNING: Not handling: different liquids"
1614 " collide"<<std::endl;
1617 bool n2_is_source = !content_flowing_liquid(n2.d);
1618 s8 n2_liquid_level = 8;
1620 surrounding_sources++;
1622 n2_liquid_level = n2.param2 & 0x07;
1624 s8 new_liquid_level = -1;
1627 //new_liquid_level = 7;
1628 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1629 new_liquid_level = 7;
1631 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1633 else if(n2_liquid_level > 0)
1635 // If the neighbor node isn't a source and flows downwards,
1636 // it doesn't flow into this node
1639 new_liquid_level = n2_liquid_level - 1;
1644 MapNode n3 = getNodeNoEx(p2 + v3s16(0,-1,0));
1645 // NOTE: collision of different liquids not yet handled here.
1646 if (content_features(n3.d).liquid_type != LIQUID_FLOWING)
1647 new_liquid_level = n2_liquid_level - 1;
1651 if(new_liquid_level > new_liquid_level_max)
1652 new_liquid_level_max = new_liquid_level;
1657 If liquid level should be something else, update it and
1658 add all the neighboring water nodes to the transform queue.
1660 if(new_liquid_level_max != liquid_level || (!is_source && surrounding_sources >= 2))
1662 if (surrounding_sources >= 2)
1664 n0.d = content_features(n0.d).liquid_alternative_source;
1667 else if(new_liquid_level_max == -1)
1669 // Remove water alltoghether
1676 n0.param2 = new_liquid_level_max;
1677 liquid_level = new_liquid_level_max;
1681 // Block has been modified
1683 v3s16 blockpos = getNodeBlockPos(p0);
1684 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1686 modified_blocks.insert(blockpos, block);
1690 Add neighboring non-source liquid nodes to transform queue.
1693 v3s16(0,0,1), // back
1694 v3s16(0,1,0), // top
1695 v3s16(1,0,0), // right
1696 v3s16(0,0,-1), // front
1697 v3s16(0,-1,0), // bottom
1698 v3s16(-1,0,0), // left
1700 for(u16 i=0; i<6; i++)
1702 v3s16 p2 = p0 + dirs[i];
1704 MapNode n2 = getNodeNoEx(p2);
1705 if(content_flowing_liquid(n2.d))
1707 m_transforming_liquid.push_back(p2);
1713 // Get a new one from queue if the node has turned into non-water
1714 if(content_liquid(n0.d) == false)
1718 Flow water from this node
1720 v3s16 dirs_to[5] = {
1721 v3s16(0,-1,0), // bottom
1722 v3s16(0,0,1), // back
1723 v3s16(1,0,0), // right
1724 v3s16(0,0,-1), // front
1725 v3s16(-1,0,0), // left
1727 for(u16 i=0; i<5; i++)
1729 bool to_bottom = (i == 0);
1731 // If liquid is at lowest possible height, it's not going
1732 // anywhere except down
1733 if(liquid_level == 0 && to_bottom == false)
1736 u8 liquid_next_level = 0;
1737 // If going to bottom
1740 //liquid_next_level = 7;
1741 if(liquid_level >= 7 - WATER_DROP_BOOST)
1742 liquid_next_level = 7;
1744 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1747 liquid_next_level = liquid_level - 1;
1749 bool n2_changed = false;
1750 bool flowed = false;
1752 v3s16 p2 = p0 + dirs_to[i];
1754 MapNode n2 = getNodeNoEx(p2);
1755 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1757 if(content_liquid(n2.d))
1759 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1760 // Check that the liquids are the same type
1761 if(n2_nonsource_c != nonsource_c)
1763 dstream<<"WARNING: Not handling: different liquids"
1764 " collide"<<std::endl;
1767 bool n2_is_source = !content_flowing_liquid(n2.d);
1768 u8 n2_liquid_level = 8;
1769 if(n2_is_source == false)
1770 n2_liquid_level = n2.param2 & 0x07;
1779 // Just flow into the source, nothing changes.
1780 // n2_changed is not set because destination didn't change
1785 if(liquid_next_level > n2_liquid_level)
1787 n2.param2 = liquid_next_level;
1795 else if(n2.d == CONTENT_AIR)
1798 n2.param2 = liquid_next_level;
1805 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1809 m_transforming_liquid.push_back(p2);
1811 v3s16 blockpos = getNodeBlockPos(p2);
1812 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1814 modified_blocks.insert(blockpos, block);
1817 // If n2_changed to bottom, don't flow anywhere else
1818 if(to_bottom && flowed && !is_source)
1823 //if(loopcount >= 100000)
1824 if(loopcount >= initial_size * 1)
1827 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1830 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1832 v3s16 blockpos = getNodeBlockPos(p);
1833 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1834 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1837 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1841 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1845 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1847 v3s16 blockpos = getNodeBlockPos(p);
1848 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1849 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1852 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1856 block->m_node_metadata.set(p_rel, meta);
1859 void Map::removeNodeMetadata(v3s16 p)
1861 v3s16 blockpos = getNodeBlockPos(p);
1862 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1863 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1866 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1870 block->m_node_metadata.remove(p_rel);
1873 void Map::nodeMetadataStep(float dtime,
1874 core::map<v3s16, MapBlock*> &changed_blocks)
1878 Currently there is no way to ensure that all the necessary
1879 blocks are loaded when this is run. (They might get unloaded)
1880 NOTE: ^- Actually, that might not be so. In a quick test it
1881 reloaded a block with a furnace when I walked back to it from
1884 core::map<v2s16, MapSector*>::Iterator si;
1885 si = m_sectors.getIterator();
1886 for(; si.atEnd() == false; si++)
1888 MapSector *sector = si.getNode()->getValue();
1889 core::list< MapBlock * > sectorblocks;
1890 sector->getBlocks(sectorblocks);
1891 core::list< MapBlock * >::Iterator i;
1892 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1894 MapBlock *block = *i;
1895 bool changed = block->m_node_metadata.step(dtime);
1897 changed_blocks[block->getPos()] = block;
1906 ServerMap::ServerMap(std::string savedir):
1909 m_map_metadata_changed(true)
1911 dstream<<__FUNCTION_NAME<<std::endl;
1913 //m_chunksize = 8; // Takes a few seconds
1915 m_seed = (((u64)(myrand()%0xffff)<<0)
1916 + ((u64)(myrand()%0xffff)<<16)
1917 + ((u64)(myrand()%0xffff)<<32)
1918 + ((u64)(myrand()%0xffff)<<48));
1921 Experimental and debug stuff
1928 Try to load map; if not found, create a new one.
1931 m_savedir = savedir;
1932 m_map_saving_enabled = false;
1936 // If directory exists, check contents and load if possible
1937 if(fs::PathExists(m_savedir))
1939 // If directory is empty, it is safe to save into it.
1940 if(fs::GetDirListing(m_savedir).size() == 0)
1942 dstream<<DTIME<<"Server: Empty save directory is valid."
1944 m_map_saving_enabled = true;
1949 // Load map metadata (seed, chunksize)
1952 catch(FileNotGoodException &e){
1953 dstream<<DTIME<<"WARNING: Could not load map metadata"
1954 //<<" Disabling chunk-based generator."
1960 // Load chunk metadata
1963 catch(FileNotGoodException &e){
1964 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1965 <<" Disabling chunk-based generator."
1970 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1971 "metadata and sector (0,0) from "<<savedir<<
1972 ", assuming valid save directory."
1975 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1976 <<"and chunk metadata from "<<savedir
1977 <<", assuming valid save directory."
1980 m_map_saving_enabled = true;
1981 // Map loaded, not creating new one
1985 // If directory doesn't exist, it is safe to save to it
1987 m_map_saving_enabled = true;
1990 catch(std::exception &e)
1992 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1993 <<", exception: "<<e.what()<<std::endl;
1994 dstream<<"Please remove the map or fix it."<<std::endl;
1995 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1998 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
2000 // Create zero sector
2001 emergeSector(v2s16(0,0));
2003 // Initially write whole map
2007 ServerMap::~ServerMap()
2009 dstream<<__FUNCTION_NAME<<std::endl;
2013 if(m_map_saving_enabled)
2015 // Save only changed parts
2017 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2021 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2024 catch(std::exception &e)
2026 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2027 <<", exception: "<<e.what()<<std::endl;
2034 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2035 for(; i.atEnd() == false; i++)
2037 MapChunk *chunk = i.getNode()->getValue();
2043 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2045 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2046 <<blockpos.Z<<")"<<std::endl;*/
2048 data->no_op = false;
2049 data->seed = m_seed;
2050 data->blockpos = blockpos;
2053 Create the whole area of this and the neighboring blocks
2056 //TimeTaker timer("initBlockMake() create area");
2058 for(s16 x=-1; x<=1; x++)
2059 for(s16 z=-1; z<=1; z++)
2061 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2062 // Sector metadata is loaded from disk if not already loaded.
2063 ServerMapSector *sector = createSector(sectorpos);
2066 for(s16 y=-1; y<=1; y++)
2068 MapBlock *block = createBlock(blockpos);
2070 // Lighting won't be calculated
2071 block->setLightingExpired(true);
2072 // Lighting will be calculated
2073 //block->setLightingExpired(false);
2076 Block gets sunlight if this is true.
2078 This should be set to true when the top side of a block
2079 is completely exposed to the sky.
2081 block->setIsUnderground(false);
2087 Now we have a big empty area.
2089 Make a ManualMapVoxelManipulator that contains this and the
2093 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2094 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2096 data->vmanip = new ManualMapVoxelManipulator(this);
2097 //data->vmanip->setMap(this);
2101 //TimeTaker timer("initBlockMake() initialEmerge");
2102 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2105 // Data is ready now.
2108 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2109 core::map<v3s16, MapBlock*> &changed_blocks)
2111 v3s16 blockpos = data->blockpos;
2112 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2113 <<blockpos.Z<<")"<<std::endl;*/
2117 dstream<<"finishBlockMake(): no-op"<<std::endl;
2121 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2123 /*dstream<<"Resulting vmanip:"<<std::endl;
2124 data->vmanip.print(dstream);*/
2127 Blit generated stuff to map
2128 NOTE: blitBackAll adds nearly everything to changed_blocks
2132 //TimeTaker timer("finishBlockMake() blitBackAll");
2133 data->vmanip->blitBackAll(&changed_blocks);
2136 if(enable_mapgen_debug_info)
2137 dstream<<"finishBlockMake: changed_blocks.size()="
2138 <<changed_blocks.size()<<std::endl;
2141 Copy transforming liquid information
2143 while(data->transforming_liquid.size() > 0)
2145 v3s16 p = data->transforming_liquid.pop_front();
2146 m_transforming_liquid.push_back(p);
2152 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2156 Set is_underground flag for lighting with sunlight
2159 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2162 Add sunlight to central block.
2163 This makes in-dark-spawning monsters to not flood the whole thing.
2164 Do not spread the light, though.
2166 /*core::map<v3s16, bool> light_sources;
2167 bool black_air_left = false;
2168 block->propagateSunlight(light_sources, true, &black_air_left);*/
2171 NOTE: Lighting and object adding shouldn't really be here, but
2172 lighting is a bit tricky to move properly to makeBlock.
2173 TODO: Do this the right way anyway, that is, move it to makeBlock.
2174 - There needs to be some way for makeBlock to report back if
2175 the lighting update is going further down because of the
2176 new block blocking light
2181 NOTE: This takes ~60ms, TODO: Investigate why
2184 TimeTaker t("finishBlockMake lighting update");
2186 core::map<v3s16, MapBlock*> lighting_update_blocks;
2188 lighting_update_blocks.insert(block->getPos(), block);
2190 // All modified blocks
2191 for(core::map<v3s16, MapBlock*>::Iterator
2192 i = changed_blocks.getIterator();
2193 i.atEnd() == false; i++)
2195 lighting_update_blocks.insert(i.getNode()->getKey(),
2196 i.getNode()->getValue());
2199 updateLighting(lighting_update_blocks, changed_blocks);
2201 if(enable_mapgen_debug_info == false)
2202 t.stop(true); // Hide output
2206 Add random objects to block
2208 mapgen::add_random_objects(block);
2211 Go through changed blocks
2213 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2214 i.atEnd() == false; i++)
2216 MapBlock *block = i.getNode()->getValue();
2219 Update day/night difference cache of the MapBlocks
2221 block->updateDayNightDiff();
2223 Set block as modified
2225 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2229 Set central block as generated
2231 block->setGenerated(true);
2234 Save changed parts of map
2235 NOTE: Will be saved later.
2239 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2240 <<blockpos.Z<<")"<<std::endl;*/
2245 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2247 DSTACKF("%s: p2d=(%d,%d)",
2252 Check if it exists already in memory
2254 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2259 Try to load it from disk (with blocks)
2261 //if(loadSectorFull(p2d) == true)
2264 Try to load metadata from disk
2266 if(loadSectorMeta(p2d) == true)
2268 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2271 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2272 throw InvalidPositionException("");
2278 Do not create over-limit
2280 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2281 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2282 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2283 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2284 throw InvalidPositionException("createSector(): pos. over limit");
2287 Generate blank sector
2290 sector = new ServerMapSector(this, p2d);
2292 // Sector position on map in nodes
2293 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2298 m_sectors.insert(p2d, sector);
2304 This is a quick-hand function for calling makeBlock().
2306 MapBlock * ServerMap::generateBlock(
2308 core::map<v3s16, MapBlock*> &modified_blocks
2311 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2313 /*dstream<<"generateBlock(): "
2314 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2317 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2319 TimeTaker timer("generateBlock");
2321 //MapBlock *block = original_dummy;
2323 v2s16 p2d(p.X, p.Z);
2324 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2327 Do not generate over-limit
2329 if(blockpos_over_limit(p))
2331 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2332 throw InvalidPositionException("generateBlock(): pos. over limit");
2336 Create block make data
2338 mapgen::BlockMakeData data;
2339 initBlockMake(&data, p);
2345 TimeTaker t("mapgen::make_block()");
2346 mapgen::make_block(&data);
2348 if(enable_mapgen_debug_info == false)
2349 t.stop(true); // Hide output
2353 Blit data back on map, update lighting, add mobs and whatever this does
2355 finishBlockMake(&data, modified_blocks);
2360 MapBlock *block = getBlockNoCreateNoEx(p);
2367 bool erroneus_content = false;
2368 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2369 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2370 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2373 MapNode n = block->getNode(p);
2374 if(n.d == CONTENT_IGNORE)
2376 dstream<<"CONTENT_IGNORE at "
2377 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2379 erroneus_content = true;
2383 if(erroneus_content)
2391 Generate a completely empty block
2393 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2394 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2396 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2402 n.d = CONTENT_STONE;
2403 block->setNode(v3s16(x0,y0,z0), n);
2408 if(enable_mapgen_debug_info == false)
2409 timer.stop(true); // Hide output
2414 MapBlock * ServerMap::createBlock(v3s16 p)
2416 DSTACKF("%s: p=(%d,%d,%d)",
2417 __FUNCTION_NAME, p.X, p.Y, p.Z);
2420 Do not create over-limit
2422 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2423 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2424 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2425 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2426 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2427 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2428 throw InvalidPositionException("createBlock(): pos. over limit");
2430 v2s16 p2d(p.X, p.Z);
2433 This will create or load a sector if not found in memory.
2434 If block exists on disk, it will be loaded.
2436 NOTE: On old save formats, this will be slow, as it generates
2437 lighting on blocks for them.
2439 ServerMapSector *sector;
2441 sector = (ServerMapSector*)createSector(p2d);
2442 assert(sector->getId() == MAPSECTOR_SERVER);
2444 catch(InvalidPositionException &e)
2446 dstream<<"createBlock: createSector() failed"<<std::endl;
2450 NOTE: This should not be done, or at least the exception
2451 should not be passed on as std::exception, because it
2452 won't be catched at all.
2454 /*catch(std::exception &e)
2456 dstream<<"createBlock: createSector() failed: "
2457 <<e.what()<<std::endl;
2462 Try to get a block from the sector
2465 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2468 if(block->isDummy())
2473 block = sector->createBlankBlock(block_y);
2477 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2479 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2481 p.X, p.Y, p.Z, allow_generate);
2484 MapBlock *block = getBlockNoCreateNoEx(p);
2490 MapBlock *block = loadBlock(p);
2497 core::map<v3s16, MapBlock*> modified_blocks;
2498 MapBlock *block = generateBlock(p, modified_blocks);
2502 event.type = MEET_OTHER;
2505 // Copy modified_blocks to event
2506 for(core::map<v3s16, MapBlock*>::Iterator
2507 i = modified_blocks.getIterator();
2508 i.atEnd()==false; i++)
2510 event.modified_blocks.insert(i.getNode()->getKey(), false);
2514 dispatchEvent(&event);
2525 Do not generate over-limit
2527 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2528 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2529 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2530 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2531 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2532 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2533 throw InvalidPositionException("emergeBlock(): pos. over limit");
2535 v2s16 p2d(p.X, p.Z);
2538 This will create or load a sector if not found in memory.
2539 If block exists on disk, it will be loaded.
2541 ServerMapSector *sector;
2543 sector = createSector(p2d);
2544 //sector = emergeSector(p2d, changed_blocks);
2546 catch(InvalidPositionException &e)
2548 dstream<<"emergeBlock: createSector() failed: "
2549 <<e.what()<<std::endl;
2550 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2552 <<"You could try to delete it."<<std::endl;
2555 catch(VersionMismatchException &e)
2557 dstream<<"emergeBlock: createSector() failed: "
2558 <<e.what()<<std::endl;
2559 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2561 <<"You could try to delete it."<<std::endl;
2566 Try to get a block from the sector
2569 bool does_not_exist = false;
2570 bool lighting_expired = false;
2571 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2573 // If not found, try loading from disk
2576 block = loadBlock(p);
2582 does_not_exist = true;
2584 else if(block->isDummy() == true)
2586 does_not_exist = true;
2588 else if(block->getLightingExpired())
2590 lighting_expired = true;
2595 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2600 If block was not found on disk and not going to generate a
2601 new one, make sure there is a dummy block in place.
2603 if(only_from_disk && (does_not_exist || lighting_expired))
2605 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2609 // Create dummy block
2610 block = new MapBlock(this, p, true);
2612 // Add block to sector
2613 sector->insertBlock(block);
2619 //dstream<<"Not found on disk, generating."<<std::endl;
2621 //TimeTaker("emergeBlock() generate");
2623 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2626 If the block doesn't exist, generate the block.
2630 block = generateBlock(p, block, sector, changed_blocks,
2631 lighting_invalidated_blocks);
2634 if(lighting_expired)
2636 lighting_invalidated_blocks.insert(p, block);
2641 Initially update sunlight
2644 core::map<v3s16, bool> light_sources;
2645 bool black_air_left = false;
2646 bool bottom_invalid =
2647 block->propagateSunlight(light_sources, true,
2650 // If sunlight didn't reach everywhere and part of block is
2651 // above ground, lighting has to be properly updated
2652 //if(black_air_left && some_part_underground)
2655 lighting_invalidated_blocks[block->getPos()] = block;
2660 lighting_invalidated_blocks[block->getPos()] = block;
2669 s16 ServerMap::findGroundLevel(v2s16 p2d)
2673 Uh, just do something random...
2675 // Find existing map from top to down
2678 v3s16 p(p2d.X, max, p2d.Y);
2679 for(; p.Y>min; p.Y--)
2681 MapNode n = getNodeNoEx(p);
2682 if(n.d != CONTENT_IGNORE)
2687 // If this node is not air, go to plan b
2688 if(getNodeNoEx(p).d != CONTENT_AIR)
2690 // Search existing walkable and return it
2691 for(; p.Y>min; p.Y--)
2693 MapNode n = getNodeNoEx(p);
2694 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2703 Determine from map generator noise functions
2706 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2709 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2710 //return (s16)level;
2713 void ServerMap::createDirs(std::string path)
2715 if(fs::CreateAllDirs(path) == false)
2717 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2718 <<"\""<<path<<"\""<<std::endl;
2719 throw BaseException("ServerMap failed to create directory");
2723 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2729 snprintf(cc, 9, "%.4x%.4x",
2730 (unsigned int)pos.X&0xffff,
2731 (unsigned int)pos.Y&0xffff);
2733 return m_savedir + "/sectors/" + cc;
2735 snprintf(cc, 9, "%.3x/%.3x",
2736 (unsigned int)pos.X&0xfff,
2737 (unsigned int)pos.Y&0xfff);
2739 return m_savedir + "/sectors2/" + cc;
2745 v2s16 ServerMap::getSectorPos(std::string dirname)
2749 size_t spos = dirname.rfind('/') + 1;
2750 assert(spos != std::string::npos);
2751 if(dirname.size() - spos == 8)
2754 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2756 else if(dirname.size() - spos == 3)
2759 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2760 // Sign-extend the 12 bit values up to 16 bits...
2761 if(x&0x800) x|=0xF000;
2762 if(y&0x800) y|=0xF000;
2769 v2s16 pos((s16)x, (s16)y);
2773 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2775 v2s16 p2d = getSectorPos(sectordir);
2777 if(blockfile.size() != 4){
2778 throw InvalidFilenameException("Invalid block filename");
2781 int r = sscanf(blockfile.c_str(), "%4x", &y);
2783 throw InvalidFilenameException("Invalid block filename");
2784 return v3s16(p2d.X, y, p2d.Y);
2787 std::string ServerMap::getBlockFilename(v3s16 p)
2790 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2794 void ServerMap::save(bool only_changed)
2796 DSTACK(__FUNCTION_NAME);
2797 if(m_map_saving_enabled == false)
2799 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2803 if(only_changed == false)
2804 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2807 if(only_changed == false || m_map_metadata_changed)
2812 u32 sector_meta_count = 0;
2813 u32 block_count = 0;
2814 u32 block_count_all = 0; // Number of blocks in memory
2816 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2817 for(; i.atEnd() == false; i++)
2819 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2820 assert(sector->getId() == MAPSECTOR_SERVER);
2822 if(sector->differs_from_disk || only_changed == false)
2824 saveSectorMeta(sector);
2825 sector_meta_count++;
2827 core::list<MapBlock*> blocks;
2828 sector->getBlocks(blocks);
2829 core::list<MapBlock*>::Iterator j;
2830 for(j=blocks.begin(); j!=blocks.end(); j++)
2832 MapBlock *block = *j;
2836 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2837 || only_changed == false)
2842 /*dstream<<"ServerMap: Written block ("
2843 <<block->getPos().X<<","
2844 <<block->getPos().Y<<","
2845 <<block->getPos().Z<<")"
2852 Only print if something happened or saved whole map
2854 if(only_changed == false || sector_meta_count != 0
2855 || block_count != 0)
2857 dstream<<DTIME<<"ServerMap: Written: "
2858 <<sector_meta_count<<" sector metadata files, "
2859 <<block_count<<" block files"
2860 <<", "<<block_count_all<<" blocks in memory."
2865 void ServerMap::saveMapMeta()
2867 DSTACK(__FUNCTION_NAME);
2869 dstream<<"INFO: ServerMap::saveMapMeta(): "
2873 createDirs(m_savedir);
2875 std::string fullpath = m_savedir + "/map_meta.txt";
2876 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2877 if(os.good() == false)
2879 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2880 <<"could not open"<<fullpath<<std::endl;
2881 throw FileNotGoodException("Cannot open chunk metadata");
2885 params.setU64("seed", m_seed);
2887 params.writeLines(os);
2889 os<<"[end_of_params]\n";
2891 m_map_metadata_changed = false;
2894 void ServerMap::loadMapMeta()
2896 DSTACK(__FUNCTION_NAME);
2898 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2901 std::string fullpath = m_savedir + "/map_meta.txt";
2902 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2903 if(is.good() == false)
2905 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2906 <<"could not open"<<fullpath<<std::endl;
2907 throw FileNotGoodException("Cannot open map metadata");
2915 throw SerializationError
2916 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2918 std::getline(is, line);
2919 std::string trimmedline = trim(line);
2920 if(trimmedline == "[end_of_params]")
2922 params.parseConfigLine(line);
2925 m_seed = params.getU64("seed");
2927 dstream<<"INFO: ServerMap::loadMapMeta(): "
2932 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2934 DSTACK(__FUNCTION_NAME);
2935 // Format used for writing
2936 u8 version = SER_FMT_VER_HIGHEST;
2938 v2s16 pos = sector->getPos();
2939 std::string dir = getSectorDir(pos);
2942 std::string fullpath = dir + "/meta";
2943 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2944 if(o.good() == false)
2945 throw FileNotGoodException("Cannot open sector metafile");
2947 sector->serialize(o, version);
2949 sector->differs_from_disk = false;
2952 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2954 DSTACK(__FUNCTION_NAME);
2956 v2s16 p2d = getSectorPos(sectordir);
2958 ServerMapSector *sector = NULL;
2960 std::string fullpath = sectordir + "/meta";
2961 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2962 if(is.good() == false)
2964 // If the directory exists anyway, it probably is in some old
2965 // format. Just go ahead and create the sector.
2966 if(fs::PathExists(sectordir))
2968 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2969 <<fullpath<<" doesn't exist but directory does."
2970 <<" Continuing with a sector with no metadata."
2972 sector = new ServerMapSector(this, p2d);
2973 m_sectors.insert(p2d, sector);
2977 throw FileNotGoodException("Cannot open sector metafile");
2982 sector = ServerMapSector::deSerialize
2983 (is, this, p2d, m_sectors);
2985 saveSectorMeta(sector);
2988 sector->differs_from_disk = false;
2993 bool ServerMap::loadSectorMeta(v2s16 p2d)
2995 DSTACK(__FUNCTION_NAME);
2997 MapSector *sector = NULL;
2999 // The directory layout we're going to load from.
3000 // 1 - original sectors/xxxxzzzz/
3001 // 2 - new sectors2/xxx/zzz/
3002 // If we load from anything but the latest structure, we will
3003 // immediately save to the new one, and remove the old.
3005 std::string sectordir1 = getSectorDir(p2d, 1);
3006 std::string sectordir;
3007 if(fs::PathExists(sectordir1))
3009 sectordir = sectordir1;
3014 sectordir = getSectorDir(p2d, 2);
3018 sector = loadSectorMeta(sectordir, loadlayout != 2);
3020 catch(InvalidFilenameException &e)
3024 catch(FileNotGoodException &e)
3028 catch(std::exception &e)
3037 bool ServerMap::loadSectorFull(v2s16 p2d)
3039 DSTACK(__FUNCTION_NAME);
3041 MapSector *sector = NULL;
3043 // The directory layout we're going to load from.
3044 // 1 - original sectors/xxxxzzzz/
3045 // 2 - new sectors2/xxx/zzz/
3046 // If we load from anything but the latest structure, we will
3047 // immediately save to the new one, and remove the old.
3049 std::string sectordir1 = getSectorDir(p2d, 1);
3050 std::string sectordir;
3051 if(fs::PathExists(sectordir1))
3053 sectordir = sectordir1;
3058 sectordir = getSectorDir(p2d, 2);
3062 sector = loadSectorMeta(sectordir, loadlayout != 2);
3064 catch(InvalidFilenameException &e)
3068 catch(FileNotGoodException &e)
3072 catch(std::exception &e)
3080 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3082 std::vector<fs::DirListNode>::iterator i2;
3083 for(i2=list2.begin(); i2!=list2.end(); i2++)
3089 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3091 catch(InvalidFilenameException &e)
3093 // This catches unknown crap in directory
3099 dstream<<"Sector converted to new layout - deleting "<<
3100 sectordir1<<std::endl;
3101 fs::RecursiveDelete(sectordir1);
3108 void ServerMap::saveBlock(MapBlock *block)
3110 DSTACK(__FUNCTION_NAME);
3112 Dummy blocks are not written
3114 if(block->isDummy())
3116 /*v3s16 p = block->getPos();
3117 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3118 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3122 // Format used for writing
3123 u8 version = SER_FMT_VER_HIGHEST;
3125 v3s16 p3d = block->getPos();
3127 v2s16 p2d(p3d.X, p3d.Z);
3128 std::string sectordir = getSectorDir(p2d);
3130 createDirs(sectordir);
3132 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3133 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3134 if(o.good() == false)
3135 throw FileNotGoodException("Cannot open block data");
3138 [0] u8 serialization version
3141 o.write((char*)&version, 1);
3144 block->serialize(o, version);
3146 // Write extra data stored on disk
3147 block->serializeDiskExtra(o, version);
3149 // We just wrote it to the disk so clear modified flag
3150 block->resetModified();
3153 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3155 DSTACK(__FUNCTION_NAME);
3157 std::string fullpath = sectordir+"/"+blockfile;
3160 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3161 if(is.good() == false)
3162 throw FileNotGoodException("Cannot open block file");
3164 v3s16 p3d = getBlockPos(sectordir, blockfile);
3165 v2s16 p2d(p3d.X, p3d.Z);
3167 assert(sector->getPos() == p2d);
3169 u8 version = SER_FMT_VER_INVALID;
3170 is.read((char*)&version, 1);
3173 throw SerializationError("ServerMap::loadBlock(): Failed"
3174 " to read MapBlock version");
3176 /*u32 block_size = MapBlock::serializedLength(version);
3177 SharedBuffer<u8> data(block_size);
3178 is.read((char*)*data, block_size);*/
3180 // This will always return a sector because we're the server
3181 //MapSector *sector = emergeSector(p2d);
3183 MapBlock *block = NULL;
3184 bool created_new = false;
3185 block = sector->getBlockNoCreateNoEx(p3d.Y);
3188 block = sector->createBlankBlockNoInsert(p3d.Y);
3193 block->deSerialize(is, version);
3195 // Read extra data stored on disk
3196 block->deSerializeDiskExtra(is, version);
3198 // If it's a new block, insert it to the map
3200 sector->insertBlock(block);
3203 Save blocks loaded in old format in new format
3206 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3211 // We just loaded it from the disk, so it's up-to-date.
3212 block->resetModified();
3215 catch(SerializationError &e)
3217 dstream<<"WARNING: Invalid block data on disk "
3218 <<"fullpath="<<fullpath
3219 <<" (SerializationError). "
3220 <<"what()="<<e.what()
3222 //" Ignoring. A new one will be generated.
3225 // TODO: Backup file; name is in fullpath.
3229 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3231 DSTACK(__FUNCTION_NAME);
3233 v2s16 p2d(blockpos.X, blockpos.Z);
3235 // The directory layout we're going to load from.
3236 // 1 - original sectors/xxxxzzzz/
3237 // 2 - new sectors2/xxx/zzz/
3238 // If we load from anything but the latest structure, we will
3239 // immediately save to the new one, and remove the old.
3241 std::string sectordir1 = getSectorDir(p2d, 1);
3242 std::string sectordir;
3243 if(fs::PathExists(sectordir1))
3245 sectordir = sectordir1;
3250 sectordir = getSectorDir(p2d, 2);
3254 Make sure sector is loaded
3256 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3260 sector = loadSectorMeta(sectordir, loadlayout != 2);
3262 catch(InvalidFilenameException &e)
3266 catch(FileNotGoodException &e)
3270 catch(std::exception &e)
3277 Make sure file exists
3280 std::string blockfilename = getBlockFilename(blockpos);
3281 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3287 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3288 return getBlockNoCreateNoEx(blockpos);
3291 void ServerMap::PrintInfo(std::ostream &out)
3302 ClientMap::ClientMap(
3304 MapDrawControl &control,
3305 scene::ISceneNode* parent,
3306 scene::ISceneManager* mgr,
3310 scene::ISceneNode(parent, mgr, id),
3313 m_camera_position(0,0,0),
3314 m_camera_direction(0,0,1)
3316 m_camera_mutex.Init();
3317 assert(m_camera_mutex.IsInitialized());
3319 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3320 BS*1000000,BS*1000000,BS*1000000);
3323 ClientMap::~ClientMap()
3325 /*JMutexAutoLock lock(mesh_mutex);
3334 MapSector * ClientMap::emergeSector(v2s16 p2d)
3336 DSTACK(__FUNCTION_NAME);
3337 // Check that it doesn't exist already
3339 return getSectorNoGenerate(p2d);
3341 catch(InvalidPositionException &e)
3346 ClientMapSector *sector = new ClientMapSector(this, p2d);
3349 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3350 m_sectors.insert(p2d, sector);
3357 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3359 DSTACK(__FUNCTION_NAME);
3360 ClientMapSector *sector = NULL;
3362 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3364 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3368 sector = (ClientMapSector*)n->getValue();
3369 assert(sector->getId() == MAPSECTOR_CLIENT);
3373 sector = new ClientMapSector(this, p2d);
3375 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3376 m_sectors.insert(p2d, sector);
3380 sector->deSerialize(is);
3384 void ClientMap::OnRegisterSceneNode()
3388 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3389 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3392 ISceneNode::OnRegisterSceneNode();
3395 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3397 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3398 DSTACK(__FUNCTION_NAME);
3400 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3403 This is called two times per frame, reset on the non-transparent one
3405 if(pass == scene::ESNRP_SOLID)
3407 m_last_drawn_sectors.clear();
3411 Get time for measuring timeout.
3413 Measuring time is very useful for long delays when the
3414 machine is swapping a lot.
3416 int time1 = time(0);
3418 //u32 daynight_ratio = m_client->getDayNightRatio();
3420 m_camera_mutex.Lock();
3421 v3f camera_position = m_camera_position;
3422 v3f camera_direction = m_camera_direction;
3423 m_camera_mutex.Unlock();
3426 Get all blocks and draw all visible ones
3429 v3s16 cam_pos_nodes(
3430 camera_position.X / BS,
3431 camera_position.Y / BS,
3432 camera_position.Z / BS);
3434 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3436 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3437 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3439 // Take a fair amount as we will be dropping more out later
3441 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3442 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3443 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3445 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3446 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3447 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3449 u32 vertex_count = 0;
3451 // For limiting number of mesh updates per frame
3452 u32 mesh_update_count = 0;
3454 u32 blocks_would_have_drawn = 0;
3455 u32 blocks_drawn = 0;
3457 int timecheck_counter = 0;
3458 core::map<v2s16, MapSector*>::Iterator si;
3459 si = m_sectors.getIterator();
3460 for(; si.atEnd() == false; si++)
3463 timecheck_counter++;
3464 if(timecheck_counter > 50)
3466 timecheck_counter = 0;
3467 int time2 = time(0);
3468 if(time2 > time1 + 4)
3470 dstream<<"ClientMap::renderMap(): "
3471 "Rendering takes ages, returning."
3478 MapSector *sector = si.getNode()->getValue();
3479 v2s16 sp = sector->getPos();
3481 if(m_control.range_all == false)
3483 if(sp.X < p_blocks_min.X
3484 || sp.X > p_blocks_max.X
3485 || sp.Y < p_blocks_min.Z
3486 || sp.Y > p_blocks_max.Z)
3490 core::list< MapBlock * > sectorblocks;
3491 sector->getBlocks(sectorblocks);
3497 u32 sector_blocks_drawn = 0;
3499 core::list< MapBlock * >::Iterator i;
3500 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3502 MapBlock *block = *i;
3505 Compare block position to camera position, skip
3506 if not seen on display
3509 float range = 100000 * BS;
3510 if(m_control.range_all == false)
3511 range = m_control.wanted_range * BS;
3514 if(isBlockInSight(block->getPos(), camera_position,
3515 camera_direction, range, &d) == false)
3520 // Okay, this block will be drawn. Reset usage timer.
3521 block->resetUsageTimer();
3523 // This is ugly (spherical distance limit?)
3524 /*if(m_control.range_all == false &&
3525 d - 0.5*BS*MAP_BLOCKSIZE > range)
3530 Update expired mesh (used for day/night change)
3532 It doesn't work exactly like it should now with the
3533 tasked mesh update but whatever.
3536 bool mesh_expired = false;
3539 JMutexAutoLock lock(block->mesh_mutex);
3541 mesh_expired = block->getMeshExpired();
3543 // Mesh has not been expired and there is no mesh:
3544 // block has no content
3545 if(block->mesh == NULL && mesh_expired == false)
3549 f32 faraway = BS*50;
3550 //f32 faraway = m_control.wanted_range * BS;
3553 This has to be done with the mesh_mutex unlocked
3555 // Pretty random but this should work somewhat nicely
3556 if(mesh_expired && (
3557 (mesh_update_count < 3
3558 && (d < faraway || mesh_update_count < 2)
3561 (m_control.range_all && mesh_update_count < 20)
3564 /*if(mesh_expired && mesh_update_count < 6
3565 && (d < faraway || mesh_update_count < 3))*/
3567 mesh_update_count++;
3569 // Mesh has been expired: generate new mesh
3570 //block->updateMesh(daynight_ratio);
3571 m_client->addUpdateMeshTask(block->getPos());
3573 mesh_expired = false;
3578 Draw the faces of the block
3581 JMutexAutoLock lock(block->mesh_mutex);
3583 scene::SMesh *mesh = block->mesh;
3588 blocks_would_have_drawn++;
3589 if(blocks_drawn >= m_control.wanted_max_blocks
3590 && m_control.range_all == false
3591 && d > m_control.wanted_min_range * BS)
3595 sector_blocks_drawn++;
3597 u32 c = mesh->getMeshBufferCount();
3599 for(u32 i=0; i<c; i++)
3601 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3602 const video::SMaterial& material = buf->getMaterial();
3603 video::IMaterialRenderer* rnd =
3604 driver->getMaterialRenderer(material.MaterialType);
3605 bool transparent = (rnd && rnd->isTransparent());
3606 // Render transparent on transparent pass and likewise.
3607 if(transparent == is_transparent_pass)
3610 This *shouldn't* hurt too much because Irrlicht
3611 doesn't change opengl textures if the old
3612 material is set again.
3614 driver->setMaterial(buf->getMaterial());
3615 driver->drawMeshBuffer(buf);
3616 vertex_count += buf->getVertexCount();
3620 } // foreach sectorblocks
3622 if(sector_blocks_drawn != 0)
3624 m_last_drawn_sectors[sp] = true;
3628 m_control.blocks_drawn = blocks_drawn;
3629 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3631 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3632 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3635 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3636 core::map<v3s16, MapBlock*> *affected_blocks)
3638 bool changed = false;
3640 Add it to all blocks touching it
3643 v3s16(0,0,0), // this
3644 v3s16(0,0,1), // back
3645 v3s16(0,1,0), // top
3646 v3s16(1,0,0), // right
3647 v3s16(0,0,-1), // front
3648 v3s16(0,-1,0), // bottom
3649 v3s16(-1,0,0), // left
3651 for(u16 i=0; i<7; i++)
3653 v3s16 p2 = p + dirs[i];
3654 // Block position of neighbor (or requested) node
3655 v3s16 blockpos = getNodeBlockPos(p2);
3656 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3657 if(blockref == NULL)
3659 // Relative position of requested node
3660 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3661 if(blockref->setTempMod(relpos, mod))
3666 if(changed && affected_blocks!=NULL)
3668 for(u16 i=0; i<7; i++)
3670 v3s16 p2 = p + dirs[i];
3671 // Block position of neighbor (or requested) node
3672 v3s16 blockpos = getNodeBlockPos(p2);
3673 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3674 if(blockref == NULL)
3676 affected_blocks->insert(blockpos, blockref);
3682 bool ClientMap::clearTempMod(v3s16 p,
3683 core::map<v3s16, MapBlock*> *affected_blocks)
3685 bool changed = false;
3687 v3s16(0,0,0), // this
3688 v3s16(0,0,1), // back
3689 v3s16(0,1,0), // top
3690 v3s16(1,0,0), // right
3691 v3s16(0,0,-1), // front
3692 v3s16(0,-1,0), // bottom
3693 v3s16(-1,0,0), // left
3695 for(u16 i=0; i<7; i++)
3697 v3s16 p2 = p + dirs[i];
3698 // Block position of neighbor (or requested) node
3699 v3s16 blockpos = getNodeBlockPos(p2);
3700 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3701 if(blockref == NULL)
3703 // Relative position of requested node
3704 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3705 if(blockref->clearTempMod(relpos))
3710 if(changed && affected_blocks!=NULL)
3712 for(u16 i=0; i<7; i++)
3714 v3s16 p2 = p + dirs[i];
3715 // Block position of neighbor (or requested) node
3716 v3s16 blockpos = getNodeBlockPos(p2);
3717 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3718 if(blockref == NULL)
3720 affected_blocks->insert(blockpos, blockref);
3726 void ClientMap::expireMeshes(bool only_daynight_diffed)
3728 TimeTaker timer("expireMeshes()");
3730 core::map<v2s16, MapSector*>::Iterator si;
3731 si = m_sectors.getIterator();
3732 for(; si.atEnd() == false; si++)
3734 MapSector *sector = si.getNode()->getValue();
3736 core::list< MapBlock * > sectorblocks;
3737 sector->getBlocks(sectorblocks);
3739 core::list< MapBlock * >::Iterator i;
3740 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3742 MapBlock *block = *i;
3744 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3750 JMutexAutoLock lock(block->mesh_mutex);
3751 if(block->mesh != NULL)
3753 /*block->mesh->drop();
3754 block->mesh = NULL;*/
3755 block->setMeshExpired(true);
3762 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3764 assert(mapType() == MAPTYPE_CLIENT);
3767 v3s16 p = blockpos + v3s16(0,0,0);
3768 MapBlock *b = getBlockNoCreate(p);
3769 b->updateMesh(daynight_ratio);
3770 //b->setMeshExpired(true);
3772 catch(InvalidPositionException &e){}
3775 v3s16 p = blockpos + v3s16(-1,0,0);
3776 MapBlock *b = getBlockNoCreate(p);
3777 b->updateMesh(daynight_ratio);
3778 //b->setMeshExpired(true);
3780 catch(InvalidPositionException &e){}
3782 v3s16 p = blockpos + v3s16(0,-1,0);
3783 MapBlock *b = getBlockNoCreate(p);
3784 b->updateMesh(daynight_ratio);
3785 //b->setMeshExpired(true);
3787 catch(InvalidPositionException &e){}
3789 v3s16 p = blockpos + v3s16(0,0,-1);
3790 MapBlock *b = getBlockNoCreate(p);
3791 b->updateMesh(daynight_ratio);
3792 //b->setMeshExpired(true);
3794 catch(InvalidPositionException &e){}
3799 Update mesh of block in which the node is, and if the node is at the
3800 leading edge, update the appropriate leading blocks too.
3802 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3810 v3s16 blockposes[4];
3811 for(u32 i=0; i<4; i++)
3813 v3s16 np = nodepos + dirs[i];
3814 blockposes[i] = getNodeBlockPos(np);
3815 // Don't update mesh of block if it has been done already
3816 bool already_updated = false;
3817 for(u32 j=0; j<i; j++)
3819 if(blockposes[j] == blockposes[i])
3821 already_updated = true;
3828 MapBlock *b = getBlockNoCreate(blockposes[i]);
3829 b->updateMesh(daynight_ratio);
3834 void ClientMap::PrintInfo(std::ostream &out)
3845 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3850 MapVoxelManipulator::~MapVoxelManipulator()
3852 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3856 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3858 TimeTaker timer1("emerge", &emerge_time);
3860 // Units of these are MapBlocks
3861 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3862 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3864 VoxelArea block_area_nodes
3865 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3867 addArea(block_area_nodes);
3869 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3870 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3871 for(s32 x=p_min.X; x<=p_max.X; x++)
3874 core::map<v3s16, bool>::Node *n;
3875 n = m_loaded_blocks.find(p);
3879 bool block_data_inexistent = false;
3882 TimeTaker timer1("emerge load", &emerge_load_time);
3884 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3885 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3888 dstream<<std::endl;*/
3890 MapBlock *block = m_map->getBlockNoCreate(p);
3891 if(block->isDummy())
3892 block_data_inexistent = true;
3894 block->copyTo(*this);
3896 catch(InvalidPositionException &e)
3898 block_data_inexistent = true;
3901 if(block_data_inexistent)
3903 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3904 // Fill with VOXELFLAG_INEXISTENT
3905 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3906 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3908 s32 i = m_area.index(a.MinEdge.X,y,z);
3909 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3913 m_loaded_blocks.insert(p, !block_data_inexistent);
3916 //dstream<<"emerge done"<<std::endl;
3920 SUGG: Add an option to only update eg. water and air nodes.
3921 This will make it interfere less with important stuff if
3924 void MapVoxelManipulator::blitBack
3925 (core::map<v3s16, MapBlock*> & modified_blocks)
3927 if(m_area.getExtent() == v3s16(0,0,0))
3930 //TimeTaker timer1("blitBack");
3932 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3933 <<m_loaded_blocks.size()<<std::endl;*/
3936 Initialize block cache
3938 v3s16 blockpos_last;
3939 MapBlock *block = NULL;
3940 bool block_checked_in_modified = false;
3942 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3943 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3944 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3948 u8 f = m_flags[m_area.index(p)];
3949 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3952 MapNode &n = m_data[m_area.index(p)];
3954 v3s16 blockpos = getNodeBlockPos(p);
3959 if(block == NULL || blockpos != blockpos_last){
3960 block = m_map->getBlockNoCreate(blockpos);
3961 blockpos_last = blockpos;
3962 block_checked_in_modified = false;
3965 // Calculate relative position in block
3966 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3968 // Don't continue if nothing has changed here
3969 if(block->getNode(relpos) == n)
3972 //m_map->setNode(m_area.MinEdge + p, n);
3973 block->setNode(relpos, n);
3976 Make sure block is in modified_blocks
3978 if(block_checked_in_modified == false)
3980 modified_blocks[blockpos] = block;
3981 block_checked_in_modified = true;
3984 catch(InvalidPositionException &e)
3990 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3991 MapVoxelManipulator(map),
3992 m_create_area(false)
3996 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4000 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4002 // Just create the area so that it can be pointed to
4003 VoxelManipulator::emerge(a, caller_id);
4006 void ManualMapVoxelManipulator::initialEmerge(
4007 v3s16 blockpos_min, v3s16 blockpos_max)
4009 TimeTaker timer1("initialEmerge", &emerge_time);
4011 // Units of these are MapBlocks
4012 v3s16 p_min = blockpos_min;
4013 v3s16 p_max = blockpos_max;
4015 VoxelArea block_area_nodes
4016 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4018 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4021 dstream<<"initialEmerge: area: ";
4022 block_area_nodes.print(dstream);
4023 dstream<<" ("<<size_MB<<"MB)";
4027 addArea(block_area_nodes);
4029 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4030 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4031 for(s32 x=p_min.X; x<=p_max.X; x++)
4034 core::map<v3s16, bool>::Node *n;
4035 n = m_loaded_blocks.find(p);
4039 bool block_data_inexistent = false;
4042 TimeTaker timer1("emerge load", &emerge_load_time);
4044 MapBlock *block = m_map->getBlockNoCreate(p);
4045 if(block->isDummy())
4046 block_data_inexistent = true;
4048 block->copyTo(*this);
4050 catch(InvalidPositionException &e)
4052 block_data_inexistent = true;
4055 if(block_data_inexistent)
4058 Mark area inexistent
4060 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4061 // Fill with VOXELFLAG_INEXISTENT
4062 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4063 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4065 s32 i = m_area.index(a.MinEdge.X,y,z);
4066 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4070 m_loaded_blocks.insert(p, !block_data_inexistent);
4074 void ManualMapVoxelManipulator::blitBackAll(
4075 core::map<v3s16, MapBlock*> * modified_blocks)
4077 if(m_area.getExtent() == v3s16(0,0,0))
4081 Copy data of all blocks
4083 for(core::map<v3s16, bool>::Iterator
4084 i = m_loaded_blocks.getIterator();
4085 i.atEnd() == false; i++)
4087 bool existed = i.getNode()->getValue();
4088 if(existed == false)
4090 v3s16 p = i.getNode()->getKey();
4091 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4094 dstream<<"WARNING: "<<__FUNCTION_NAME
4095 <<": got NULL block "
4096 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4101 block->copyFrom(*this);
4104 modified_blocks->insert(p, block);