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.
1246 v3s16(0,0,1), // back
1247 v3s16(0,1,0), // top
1248 v3s16(1,0,0), // right
1249 v3s16(0,0,-1), // front
1250 v3s16(0,-1,0), // bottom
1251 v3s16(-1,0,0), // left
1253 for(u16 i=0; i<6; i++)
1258 v3s16 p2 = p + dirs[i];
1260 MapNode n2 = getNode(p2);
1261 if(content_liquid(n2.d))
1263 m_transforming_liquid.push_back(p2);
1266 }catch(InvalidPositionException &e)
1272 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1275 event.type = MEET_ADDNODE;
1279 bool succeeded = true;
1281 core::map<v3s16, MapBlock*> modified_blocks;
1282 addNodeAndUpdate(p, n, modified_blocks);
1284 // Copy modified_blocks to event
1285 for(core::map<v3s16, MapBlock*>::Iterator
1286 i = modified_blocks.getIterator();
1287 i.atEnd()==false; i++)
1289 event.modified_blocks.insert(i.getNode()->getKey(), false);
1292 catch(InvalidPositionException &e){
1296 dispatchEvent(&event);
1301 bool Map::removeNodeWithEvent(v3s16 p)
1304 event.type = MEET_REMOVENODE;
1307 bool succeeded = true;
1309 core::map<v3s16, MapBlock*> modified_blocks;
1310 removeNodeAndUpdate(p, modified_blocks);
1312 // Copy modified_blocks to event
1313 for(core::map<v3s16, MapBlock*>::Iterator
1314 i = modified_blocks.getIterator();
1315 i.atEnd()==false; i++)
1317 event.modified_blocks.insert(i.getNode()->getKey(), false);
1320 catch(InvalidPositionException &e){
1324 dispatchEvent(&event);
1329 bool Map::dayNightDiffed(v3s16 blockpos)
1332 v3s16 p = blockpos + v3s16(0,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1340 v3s16 p = blockpos + v3s16(-1,0,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1347 v3s16 p = blockpos + v3s16(0,-1,0);
1348 MapBlock *b = getBlockNoCreate(p);
1349 if(b->dayNightDiffed())
1352 catch(InvalidPositionException &e){}
1354 v3s16 p = blockpos + v3s16(0,0,-1);
1355 MapBlock *b = getBlockNoCreate(p);
1356 if(b->dayNightDiffed())
1359 catch(InvalidPositionException &e){}
1362 v3s16 p = blockpos + v3s16(1,0,0);
1363 MapBlock *b = getBlockNoCreate(p);
1364 if(b->dayNightDiffed())
1367 catch(InvalidPositionException &e){}
1369 v3s16 p = blockpos + v3s16(0,1,0);
1370 MapBlock *b = getBlockNoCreate(p);
1371 if(b->dayNightDiffed())
1374 catch(InvalidPositionException &e){}
1376 v3s16 p = blockpos + v3s16(0,0,1);
1377 MapBlock *b = getBlockNoCreate(p);
1378 if(b->dayNightDiffed())
1381 catch(InvalidPositionException &e){}
1387 Updates usage timers
1389 void Map::timerUpdate(float dtime, float unload_timeout,
1390 core::list<v3s16> *unloaded_blocks)
1392 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1394 core::list<v2s16> sector_deletion_queue;
1395 u32 deleted_blocks_count = 0;
1396 u32 saved_blocks_count = 0;
1398 core::map<v2s16, MapSector*>::Iterator si;
1400 si = m_sectors.getIterator();
1401 for(; si.atEnd() == false; si++)
1403 MapSector *sector = si.getNode()->getValue();
1405 bool all_blocks_deleted = true;
1407 core::list<MapBlock*> blocks;
1408 sector->getBlocks(blocks);
1409 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1410 i != blocks.end(); i++)
1412 MapBlock *block = (*i);
1414 block->incrementUsageTimer(dtime);
1416 if(block->getUsageTimer() > unload_timeout)
1418 v3s16 p = block->getPos();
1421 if(block->getModified() != MOD_STATE_CLEAN
1422 && save_before_unloading)
1425 saved_blocks_count++;
1428 // Delete from memory
1429 sector->deleteBlock(block);
1432 unloaded_blocks->push_back(p);
1434 deleted_blocks_count++;
1438 all_blocks_deleted = false;
1442 if(all_blocks_deleted)
1444 sector_deletion_queue.push_back(si.getNode()->getKey());
1448 // Finally delete the empty sectors
1449 deleteSectors(sector_deletion_queue);
1451 if(deleted_blocks_count != 0)
1453 PrintInfo(dstream); // ServerMap/ClientMap:
1454 dstream<<"Unloaded "<<deleted_blocks_count
1455 <<" blocks from memory";
1456 if(save_before_unloading)
1457 dstream<<", of which "<<saved_blocks_count<<" were written";
1458 dstream<<"."<<std::endl;
1462 void Map::deleteSectors(core::list<v2s16> &list)
1464 core::list<v2s16>::Iterator j;
1465 for(j=list.begin(); j!=list.end(); j++)
1467 MapSector *sector = m_sectors[*j];
1468 // If sector is in sector cache, remove it from there
1469 if(m_sector_cache == sector)
1470 m_sector_cache = NULL;
1471 // Remove from map and delete
1472 m_sectors.remove(*j);
1478 void Map::unloadUnusedData(float timeout,
1479 core::list<v3s16> *deleted_blocks)
1481 core::list<v2s16> sector_deletion_queue;
1482 u32 deleted_blocks_count = 0;
1483 u32 saved_blocks_count = 0;
1485 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1486 for(; si.atEnd() == false; si++)
1488 MapSector *sector = si.getNode()->getValue();
1490 bool all_blocks_deleted = true;
1492 core::list<MapBlock*> blocks;
1493 sector->getBlocks(blocks);
1494 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1495 i != blocks.end(); i++)
1497 MapBlock *block = (*i);
1499 if(block->getUsageTimer() > timeout)
1502 if(block->getModified() != MOD_STATE_CLEAN)
1505 saved_blocks_count++;
1507 // Delete from memory
1508 sector->deleteBlock(block);
1509 deleted_blocks_count++;
1513 all_blocks_deleted = false;
1517 if(all_blocks_deleted)
1519 sector_deletion_queue.push_back(si.getNode()->getKey());
1523 deleteSectors(sector_deletion_queue);
1525 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1526 <<", of which "<<saved_blocks_count<<" were wr."
1529 //return sector_deletion_queue.getSize();
1530 //return deleted_blocks_count;
1534 void Map::PrintInfo(std::ostream &out)
1539 #define WATER_DROP_BOOST 4
1541 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1543 DSTACK(__FUNCTION_NAME);
1544 //TimeTaker timer("transformLiquids()");
1547 u32 initial_size = m_transforming_liquid.size();
1549 /*if(initial_size != 0)
1550 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1552 while(m_transforming_liquid.size() != 0)
1555 Get a queued transforming liquid node
1557 v3s16 p0 = m_transforming_liquid.pop_front();
1559 MapNode n0 = getNodeNoEx(p0);
1561 // Don't deal with non-liquids
1562 if(content_liquid(n0.d) == false)
1565 bool is_source = !content_flowing_liquid(n0.d);
1567 u8 liquid_level = 8;
1568 if(is_source == false)
1569 liquid_level = n0.param2 & 0x0f;
1571 // Turn possible source into non-source
1572 u8 nonsource_c = make_liquid_flowing(n0.d);
1575 If not source, check that some node flows into this one
1576 and what is the level of liquid in this one
1578 if(is_source == false)
1580 s8 new_liquid_level_max = -1;
1582 v3s16 dirs_from[5] = {
1583 v3s16(0,1,0), // top
1584 v3s16(0,0,1), // back
1585 v3s16(1,0,0), // right
1586 v3s16(0,0,-1), // front
1587 v3s16(-1,0,0), // left
1589 for(u16 i=0; i<5; i++)
1591 bool from_top = (i==0);
1593 v3s16 p2 = p0 + dirs_from[i];
1594 MapNode n2 = getNodeNoEx(p2);
1596 if(content_liquid(n2.d))
1598 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1599 // Check that the liquids are the same type
1600 if(n2_nonsource_c != nonsource_c)
1602 dstream<<"WARNING: Not handling: different liquids"
1603 " collide"<<std::endl;
1606 bool n2_is_source = !content_flowing_liquid(n2.d);
1607 s8 n2_liquid_level = 8;
1608 if(n2_is_source == false)
1609 n2_liquid_level = n2.param2 & 0x07;
1611 s8 new_liquid_level = -1;
1614 //new_liquid_level = 7;
1615 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1616 new_liquid_level = 7;
1618 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1620 else if(n2_liquid_level > 0)
1622 new_liquid_level = n2_liquid_level - 1;
1625 if(new_liquid_level > new_liquid_level_max)
1626 new_liquid_level_max = new_liquid_level;
1631 If liquid level should be something else, update it and
1632 add all the neighboring water nodes to the transform queue.
1634 if(new_liquid_level_max != liquid_level)
1636 if(new_liquid_level_max == -1)
1638 // Remove water alltoghether
1645 n0.param2 = new_liquid_level_max;
1649 // Block has been modified
1651 v3s16 blockpos = getNodeBlockPos(p0);
1652 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1654 modified_blocks.insert(blockpos, block);
1658 Add neighboring non-source liquid nodes to transform queue.
1661 v3s16(0,0,1), // back
1662 v3s16(0,1,0), // top
1663 v3s16(1,0,0), // right
1664 v3s16(0,0,-1), // front
1665 v3s16(0,-1,0), // bottom
1666 v3s16(-1,0,0), // left
1668 for(u16 i=0; i<6; i++)
1670 v3s16 p2 = p0 + dirs[i];
1672 MapNode n2 = getNodeNoEx(p2);
1673 if(content_flowing_liquid(n2.d))
1675 m_transforming_liquid.push_back(p2);
1681 // Get a new one from queue if the node has turned into non-water
1682 if(content_liquid(n0.d) == false)
1686 Flow water from this node
1688 v3s16 dirs_to[5] = {
1689 v3s16(0,-1,0), // bottom
1690 v3s16(0,0,1), // back
1691 v3s16(1,0,0), // right
1692 v3s16(0,0,-1), // front
1693 v3s16(-1,0,0), // left
1695 for(u16 i=0; i<5; i++)
1697 bool to_bottom = (i == 0);
1699 // If liquid is at lowest possible height, it's not going
1700 // anywhere except down
1701 if(liquid_level == 0 && to_bottom == false)
1704 u8 liquid_next_level = 0;
1705 // If going to bottom
1708 //liquid_next_level = 7;
1709 if(liquid_level >= 7 - WATER_DROP_BOOST)
1710 liquid_next_level = 7;
1712 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1715 liquid_next_level = liquid_level - 1;
1717 bool n2_changed = false;
1718 bool flowed = false;
1720 v3s16 p2 = p0 + dirs_to[i];
1722 MapNode n2 = getNodeNoEx(p2);
1723 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1725 if(content_liquid(n2.d))
1727 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1728 // Check that the liquids are the same type
1729 if(n2_nonsource_c != nonsource_c)
1731 dstream<<"WARNING: Not handling: different liquids"
1732 " collide"<<std::endl;
1735 bool n2_is_source = !content_flowing_liquid(n2.d);
1736 u8 n2_liquid_level = 8;
1737 if(n2_is_source == false)
1738 n2_liquid_level = n2.param2 & 0x07;
1747 // Just flow into the source, nothing changes.
1748 // n2_changed is not set because destination didn't change
1753 if(liquid_next_level > liquid_level)
1755 n2.param2 = liquid_next_level;
1763 else if(n2.d == CONTENT_AIR)
1766 n2.param2 = liquid_next_level;
1773 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1777 m_transforming_liquid.push_back(p2);
1779 v3s16 blockpos = getNodeBlockPos(p2);
1780 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1782 modified_blocks.insert(blockpos, block);
1785 // If n2_changed to bottom, don't flow anywhere else
1786 if(to_bottom && flowed && !is_source)
1791 //if(loopcount >= 100000)
1792 if(loopcount >= initial_size * 1)
1795 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1798 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1800 v3s16 blockpos = getNodeBlockPos(p);
1801 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1802 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1805 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1809 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1813 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1815 v3s16 blockpos = getNodeBlockPos(p);
1816 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1817 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1820 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1824 block->m_node_metadata.set(p_rel, meta);
1827 void Map::removeNodeMetadata(v3s16 p)
1829 v3s16 blockpos = getNodeBlockPos(p);
1830 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1831 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1834 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1838 block->m_node_metadata.remove(p_rel);
1841 void Map::nodeMetadataStep(float dtime,
1842 core::map<v3s16, MapBlock*> &changed_blocks)
1846 Currently there is no way to ensure that all the necessary
1847 blocks are loaded when this is run. (They might get unloaded)
1848 NOTE: ^- Actually, that might not be so. In a quick test it
1849 reloaded a block with a furnace when I walked back to it from
1852 core::map<v2s16, MapSector*>::Iterator si;
1853 si = m_sectors.getIterator();
1854 for(; si.atEnd() == false; si++)
1856 MapSector *sector = si.getNode()->getValue();
1857 core::list< MapBlock * > sectorblocks;
1858 sector->getBlocks(sectorblocks);
1859 core::list< MapBlock * >::Iterator i;
1860 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1862 MapBlock *block = *i;
1863 bool changed = block->m_node_metadata.step(dtime);
1865 changed_blocks[block->getPos()] = block;
1874 ServerMap::ServerMap(std::string savedir):
1877 m_map_metadata_changed(true)
1879 dstream<<__FUNCTION_NAME<<std::endl;
1881 //m_chunksize = 8; // Takes a few seconds
1883 m_seed = (((u64)(myrand()%0xffff)<<0)
1884 + ((u64)(myrand()%0xffff)<<16)
1885 + ((u64)(myrand()%0xffff)<<32)
1886 + ((u64)(myrand()%0xffff)<<48));
1889 Experimental and debug stuff
1896 Try to load map; if not found, create a new one.
1899 m_savedir = savedir;
1900 m_map_saving_enabled = false;
1904 // If directory exists, check contents and load if possible
1905 if(fs::PathExists(m_savedir))
1907 // If directory is empty, it is safe to save into it.
1908 if(fs::GetDirListing(m_savedir).size() == 0)
1910 dstream<<DTIME<<"Server: Empty save directory is valid."
1912 m_map_saving_enabled = true;
1917 // Load map metadata (seed, chunksize)
1920 catch(FileNotGoodException &e){
1921 dstream<<DTIME<<"WARNING: Could not load map metadata"
1922 //<<" Disabling chunk-based generator."
1928 // Load chunk metadata
1931 catch(FileNotGoodException &e){
1932 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1933 <<" Disabling chunk-based generator."
1938 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1939 "metadata and sector (0,0) from "<<savedir<<
1940 ", assuming valid save directory."
1943 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1944 <<"and chunk metadata from "<<savedir
1945 <<", assuming valid save directory."
1948 m_map_saving_enabled = true;
1949 // Map loaded, not creating new one
1953 // If directory doesn't exist, it is safe to save to it
1955 m_map_saving_enabled = true;
1958 catch(std::exception &e)
1960 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1961 <<", exception: "<<e.what()<<std::endl;
1962 dstream<<"Please remove the map or fix it."<<std::endl;
1963 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1966 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1968 // Create zero sector
1969 emergeSector(v2s16(0,0));
1971 // Initially write whole map
1975 ServerMap::~ServerMap()
1977 dstream<<__FUNCTION_NAME<<std::endl;
1981 if(m_map_saving_enabled)
1983 // Save only changed parts
1985 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1989 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1992 catch(std::exception &e)
1994 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1995 <<", exception: "<<e.what()<<std::endl;
2002 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2003 for(; i.atEnd() == false; i++)
2005 MapChunk *chunk = i.getNode()->getValue();
2011 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2013 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2014 <<blockpos.Z<<")"<<std::endl;*/
2016 data->no_op = false;
2017 data->seed = m_seed;
2018 data->blockpos = blockpos;
2021 Create the whole area of this and the neighboring blocks
2024 //TimeTaker timer("initBlockMake() create area");
2026 for(s16 x=-1; x<=1; x++)
2027 for(s16 z=-1; z<=1; z++)
2029 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2030 // Sector metadata is loaded from disk if not already loaded.
2031 ServerMapSector *sector = createSector(sectorpos);
2034 for(s16 y=-1; y<=1; y++)
2036 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2037 //MapBlock *block = createBlock(p);
2038 // 1) get from memory, 2) load from disk
2039 MapBlock *block = emergeBlock(p, false);
2040 // 3) create a blank one
2043 block = createBlock(p);
2046 Block gets sunlight if this is true.
2048 Refer to the map generator heuristics.
2050 bool ug = mapgen::block_is_underground(data->seed, p);
2051 block->setIsUnderground(ug);
2054 // Lighting will not be valid after make_chunk is called
2055 block->setLightingExpired(true);
2056 // Lighting will be calculated
2057 //block->setLightingExpired(false);
2063 Now we have a big empty area.
2065 Make a ManualMapVoxelManipulator that contains this and the
2069 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2070 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2072 data->vmanip = new ManualMapVoxelManipulator(this);
2073 //data->vmanip->setMap(this);
2077 //TimeTaker timer("initBlockMake() initialEmerge");
2078 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2081 // Data is ready now.
2084 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2085 core::map<v3s16, MapBlock*> &changed_blocks)
2087 v3s16 blockpos = data->blockpos;
2088 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2089 <<blockpos.Z<<")"<<std::endl;*/
2093 dstream<<"finishBlockMake(): no-op"<<std::endl;
2097 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2099 /*dstream<<"Resulting vmanip:"<<std::endl;
2100 data->vmanip.print(dstream);*/
2103 Blit generated stuff to map
2104 NOTE: blitBackAll adds nearly everything to changed_blocks
2108 //TimeTaker timer("finishBlockMake() blitBackAll");
2109 data->vmanip->blitBackAll(&changed_blocks);
2112 if(enable_mapgen_debug_info)
2113 dstream<<"finishBlockMake: changed_blocks.size()="
2114 <<changed_blocks.size()<<std::endl;
2117 Copy transforming liquid information
2119 while(data->transforming_liquid.size() > 0)
2121 v3s16 p = data->transforming_liquid.pop_front();
2122 m_transforming_liquid.push_back(p);
2128 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2132 Set is_underground flag for lighting with sunlight.
2134 Refer to map generator heuristics.
2136 NOTE: This is done in initChunkMake
2138 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2142 Add sunlight to central block.
2143 This makes in-dark-spawning monsters to not flood the whole thing.
2144 Do not spread the light, though.
2146 /*core::map<v3s16, bool> light_sources;
2147 bool black_air_left = false;
2148 block->propagateSunlight(light_sources, true, &black_air_left);*/
2151 NOTE: Lighting and object adding shouldn't really be here, but
2152 lighting is a bit tricky to move properly to makeBlock.
2153 TODO: Do this the right way anyway, that is, move it to makeBlock.
2154 - There needs to be some way for makeBlock to report back if
2155 the lighting update is going further down because of the
2156 new block blocking light
2161 NOTE: This takes ~60ms, TODO: Investigate why
2164 TimeTaker t("finishBlockMake lighting update");
2166 core::map<v3s16, MapBlock*> lighting_update_blocks;
2169 lighting_update_blocks.insert(block->getPos(), block);
2174 v3s16 p = block->getPos()+v3s16(x,1,z);
2175 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2179 // All modified blocks
2180 // NOTE: Should this be done? If this is not done, then the lighting
2181 // of the others will be updated in a different place, one by one, i
2182 // think... or they might not? Well, at least they are left marked as
2183 // "lighting expired"; it seems that is not handled at all anywhere,
2184 // so enabling this will slow it down A LOT because otherwise it
2185 // would not do this at all. This causes the black trees.
2186 for(core::map<v3s16, MapBlock*>::Iterator
2187 i = changed_blocks.getIterator();
2188 i.atEnd() == false; i++)
2190 lighting_update_blocks.insert(i.getNode()->getKey(),
2191 i.getNode()->getValue());
2193 /*// Also force-add all the upmost blocks for proper sunlight
2194 for(s16 x=-1; x<=1; x++)
2195 for(s16 z=-1; z<=1; z++)
2197 v3s16 p = block->getPos()+v3s16(x,1,z);
2198 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2201 updateLighting(lighting_update_blocks, changed_blocks);
2204 Set lighting to non-expired state in all of them.
2205 This is cheating, but it is not fast enough if all of them
2206 would actually be updated.
2208 for(s16 x=-1; x<=1; x++)
2209 for(s16 y=-1; y<=1; y++)
2210 for(s16 z=-1; z<=1; z++)
2212 v3s16 p = block->getPos()+v3s16(x,y,z);
2213 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2216 if(enable_mapgen_debug_info == false)
2217 t.stop(true); // Hide output
2221 Add random objects to block
2223 mapgen::add_random_objects(block);
2226 Go through changed blocks
2228 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2229 i.atEnd() == false; i++)
2231 MapBlock *block = i.getNode()->getValue();
2234 Update day/night difference cache of the MapBlocks
2236 block->updateDayNightDiff();
2238 Set block as modified
2240 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2244 Set central block as generated
2246 block->setGenerated(true);
2249 Save changed parts of map
2250 NOTE: Will be saved later.
2254 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2255 <<blockpos.Z<<")"<<std::endl;*/
2260 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2262 DSTACKF("%s: p2d=(%d,%d)",
2267 Check if it exists already in memory
2269 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2274 Try to load it from disk (with blocks)
2276 //if(loadSectorFull(p2d) == true)
2279 Try to load metadata from disk
2281 if(loadSectorMeta(p2d) == true)
2283 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2286 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2287 throw InvalidPositionException("");
2293 Do not create over-limit
2295 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2296 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2297 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2298 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2299 throw InvalidPositionException("createSector(): pos. over limit");
2302 Generate blank sector
2305 sector = new ServerMapSector(this, p2d);
2307 // Sector position on map in nodes
2308 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2313 m_sectors.insert(p2d, sector);
2319 This is a quick-hand function for calling makeBlock().
2321 MapBlock * ServerMap::generateBlock(
2323 core::map<v3s16, MapBlock*> &modified_blocks
2326 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2328 /*dstream<<"generateBlock(): "
2329 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2332 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2334 TimeTaker timer("generateBlock");
2336 //MapBlock *block = original_dummy;
2338 v2s16 p2d(p.X, p.Z);
2339 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2342 Do not generate over-limit
2344 if(blockpos_over_limit(p))
2346 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2347 throw InvalidPositionException("generateBlock(): pos. over limit");
2351 Create block make data
2353 mapgen::BlockMakeData data;
2354 initBlockMake(&data, p);
2360 TimeTaker t("mapgen::make_block()");
2361 mapgen::make_block(&data);
2363 if(enable_mapgen_debug_info == false)
2364 t.stop(true); // Hide output
2368 Blit data back on map, update lighting, add mobs and whatever this does
2370 finishBlockMake(&data, modified_blocks);
2375 MapBlock *block = getBlockNoCreateNoEx(p);
2382 bool erroneus_content = false;
2383 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2384 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2385 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2388 MapNode n = block->getNode(p);
2389 if(n.d == CONTENT_IGNORE)
2391 dstream<<"CONTENT_IGNORE at "
2392 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2394 erroneus_content = true;
2398 if(erroneus_content)
2406 Generate a completely empty block
2408 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2409 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2411 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2417 n.d = CONTENT_STONE;
2418 block->setNode(v3s16(x0,y0,z0), n);
2423 if(enable_mapgen_debug_info == false)
2424 timer.stop(true); // Hide output
2429 MapBlock * ServerMap::createBlock(v3s16 p)
2431 DSTACKF("%s: p=(%d,%d,%d)",
2432 __FUNCTION_NAME, p.X, p.Y, p.Z);
2435 Do not create over-limit
2437 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2438 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2439 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2440 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2441 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2442 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2443 throw InvalidPositionException("createBlock(): pos. over limit");
2445 v2s16 p2d(p.X, p.Z);
2448 This will create or load a sector if not found in memory.
2449 If block exists on disk, it will be loaded.
2451 NOTE: On old save formats, this will be slow, as it generates
2452 lighting on blocks for them.
2454 ServerMapSector *sector;
2456 sector = (ServerMapSector*)createSector(p2d);
2457 assert(sector->getId() == MAPSECTOR_SERVER);
2459 catch(InvalidPositionException &e)
2461 dstream<<"createBlock: createSector() failed"<<std::endl;
2465 NOTE: This should not be done, or at least the exception
2466 should not be passed on as std::exception, because it
2467 won't be catched at all.
2469 /*catch(std::exception &e)
2471 dstream<<"createBlock: createSector() failed: "
2472 <<e.what()<<std::endl;
2477 Try to get a block from the sector
2480 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2483 if(block->isDummy())
2488 block = sector->createBlankBlock(block_y);
2492 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2494 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2496 p.X, p.Y, p.Z, allow_generate);
2499 MapBlock *block = getBlockNoCreateNoEx(p);
2500 if(block && block->isDummy() == false)
2505 MapBlock *block = loadBlock(p);
2512 core::map<v3s16, MapBlock*> modified_blocks;
2513 MapBlock *block = generateBlock(p, modified_blocks);
2517 event.type = MEET_OTHER;
2520 // Copy modified_blocks to event
2521 for(core::map<v3s16, MapBlock*>::Iterator
2522 i = modified_blocks.getIterator();
2523 i.atEnd()==false; i++)
2525 event.modified_blocks.insert(i.getNode()->getKey(), false);
2529 dispatchEvent(&event);
2540 Do not generate over-limit
2542 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2543 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2544 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2545 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2546 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2547 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2548 throw InvalidPositionException("emergeBlock(): pos. over limit");
2550 v2s16 p2d(p.X, p.Z);
2553 This will create or load a sector if not found in memory.
2554 If block exists on disk, it will be loaded.
2556 ServerMapSector *sector;
2558 sector = createSector(p2d);
2559 //sector = emergeSector(p2d, changed_blocks);
2561 catch(InvalidPositionException &e)
2563 dstream<<"emergeBlock: createSector() failed: "
2564 <<e.what()<<std::endl;
2565 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2567 <<"You could try to delete it."<<std::endl;
2570 catch(VersionMismatchException &e)
2572 dstream<<"emergeBlock: createSector() failed: "
2573 <<e.what()<<std::endl;
2574 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2576 <<"You could try to delete it."<<std::endl;
2581 Try to get a block from the sector
2584 bool does_not_exist = false;
2585 bool lighting_expired = false;
2586 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2588 // If not found, try loading from disk
2591 block = loadBlock(p);
2597 does_not_exist = true;
2599 else if(block->isDummy() == true)
2601 does_not_exist = true;
2603 else if(block->getLightingExpired())
2605 lighting_expired = true;
2610 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2615 If block was not found on disk and not going to generate a
2616 new one, make sure there is a dummy block in place.
2618 if(only_from_disk && (does_not_exist || lighting_expired))
2620 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2624 // Create dummy block
2625 block = new MapBlock(this, p, true);
2627 // Add block to sector
2628 sector->insertBlock(block);
2634 //dstream<<"Not found on disk, generating."<<std::endl;
2636 //TimeTaker("emergeBlock() generate");
2638 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2641 If the block doesn't exist, generate the block.
2645 block = generateBlock(p, block, sector, changed_blocks,
2646 lighting_invalidated_blocks);
2649 if(lighting_expired)
2651 lighting_invalidated_blocks.insert(p, block);
2656 Initially update sunlight
2659 core::map<v3s16, bool> light_sources;
2660 bool black_air_left = false;
2661 bool bottom_invalid =
2662 block->propagateSunlight(light_sources, true,
2665 // If sunlight didn't reach everywhere and part of block is
2666 // above ground, lighting has to be properly updated
2667 //if(black_air_left && some_part_underground)
2670 lighting_invalidated_blocks[block->getPos()] = block;
2675 lighting_invalidated_blocks[block->getPos()] = block;
2684 s16 ServerMap::findGroundLevel(v2s16 p2d)
2688 Uh, just do something random...
2690 // Find existing map from top to down
2693 v3s16 p(p2d.X, max, p2d.Y);
2694 for(; p.Y>min; p.Y--)
2696 MapNode n = getNodeNoEx(p);
2697 if(n.d != CONTENT_IGNORE)
2702 // If this node is not air, go to plan b
2703 if(getNodeNoEx(p).d != CONTENT_AIR)
2705 // Search existing walkable and return it
2706 for(; p.Y>min; p.Y--)
2708 MapNode n = getNodeNoEx(p);
2709 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2718 Determine from map generator noise functions
2721 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2724 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2725 //return (s16)level;
2728 void ServerMap::createDirs(std::string path)
2730 if(fs::CreateAllDirs(path) == false)
2732 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2733 <<"\""<<path<<"\""<<std::endl;
2734 throw BaseException("ServerMap failed to create directory");
2738 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2744 snprintf(cc, 9, "%.4x%.4x",
2745 (unsigned int)pos.X&0xffff,
2746 (unsigned int)pos.Y&0xffff);
2748 return m_savedir + "/sectors/" + cc;
2750 snprintf(cc, 9, "%.3x/%.3x",
2751 (unsigned int)pos.X&0xfff,
2752 (unsigned int)pos.Y&0xfff);
2754 return m_savedir + "/sectors2/" + cc;
2760 v2s16 ServerMap::getSectorPos(std::string dirname)
2764 size_t spos = dirname.rfind('/') + 1;
2765 assert(spos != std::string::npos);
2766 if(dirname.size() - spos == 8)
2769 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2771 else if(dirname.size() - spos == 3)
2774 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2775 // Sign-extend the 12 bit values up to 16 bits...
2776 if(x&0x800) x|=0xF000;
2777 if(y&0x800) y|=0xF000;
2784 v2s16 pos((s16)x, (s16)y);
2788 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2790 v2s16 p2d = getSectorPos(sectordir);
2792 if(blockfile.size() != 4){
2793 throw InvalidFilenameException("Invalid block filename");
2796 int r = sscanf(blockfile.c_str(), "%4x", &y);
2798 throw InvalidFilenameException("Invalid block filename");
2799 return v3s16(p2d.X, y, p2d.Y);
2802 std::string ServerMap::getBlockFilename(v3s16 p)
2805 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2809 void ServerMap::save(bool only_changed)
2811 DSTACK(__FUNCTION_NAME);
2812 if(m_map_saving_enabled == false)
2814 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2818 if(only_changed == false)
2819 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2822 if(only_changed == false || m_map_metadata_changed)
2827 u32 sector_meta_count = 0;
2828 u32 block_count = 0;
2829 u32 block_count_all = 0; // Number of blocks in memory
2831 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2832 for(; i.atEnd() == false; i++)
2834 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2835 assert(sector->getId() == MAPSECTOR_SERVER);
2837 if(sector->differs_from_disk || only_changed == false)
2839 saveSectorMeta(sector);
2840 sector_meta_count++;
2842 core::list<MapBlock*> blocks;
2843 sector->getBlocks(blocks);
2844 core::list<MapBlock*>::Iterator j;
2845 for(j=blocks.begin(); j!=blocks.end(); j++)
2847 MapBlock *block = *j;
2851 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2852 || only_changed == false)
2857 /*dstream<<"ServerMap: Written block ("
2858 <<block->getPos().X<<","
2859 <<block->getPos().Y<<","
2860 <<block->getPos().Z<<")"
2867 Only print if something happened or saved whole map
2869 if(only_changed == false || sector_meta_count != 0
2870 || block_count != 0)
2872 dstream<<DTIME<<"ServerMap: Written: "
2873 <<sector_meta_count<<" sector metadata files, "
2874 <<block_count<<" block files"
2875 <<", "<<block_count_all<<" blocks in memory."
2880 void ServerMap::saveMapMeta()
2882 DSTACK(__FUNCTION_NAME);
2884 dstream<<"INFO: ServerMap::saveMapMeta(): "
2888 createDirs(m_savedir);
2890 std::string fullpath = m_savedir + "/map_meta.txt";
2891 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2892 if(os.good() == false)
2894 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2895 <<"could not open"<<fullpath<<std::endl;
2896 throw FileNotGoodException("Cannot open chunk metadata");
2900 params.setU64("seed", m_seed);
2902 params.writeLines(os);
2904 os<<"[end_of_params]\n";
2906 m_map_metadata_changed = false;
2909 void ServerMap::loadMapMeta()
2911 DSTACK(__FUNCTION_NAME);
2913 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2916 std::string fullpath = m_savedir + "/map_meta.txt";
2917 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2918 if(is.good() == false)
2920 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2921 <<"could not open"<<fullpath<<std::endl;
2922 throw FileNotGoodException("Cannot open map metadata");
2930 throw SerializationError
2931 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2933 std::getline(is, line);
2934 std::string trimmedline = trim(line);
2935 if(trimmedline == "[end_of_params]")
2937 params.parseConfigLine(line);
2940 m_seed = params.getU64("seed");
2942 dstream<<"INFO: ServerMap::loadMapMeta(): "
2947 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2949 DSTACK(__FUNCTION_NAME);
2950 // Format used for writing
2951 u8 version = SER_FMT_VER_HIGHEST;
2953 v2s16 pos = sector->getPos();
2954 std::string dir = getSectorDir(pos);
2957 std::string fullpath = dir + "/meta";
2958 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2959 if(o.good() == false)
2960 throw FileNotGoodException("Cannot open sector metafile");
2962 sector->serialize(o, version);
2964 sector->differs_from_disk = false;
2967 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2969 DSTACK(__FUNCTION_NAME);
2971 v2s16 p2d = getSectorPos(sectordir);
2973 ServerMapSector *sector = NULL;
2975 std::string fullpath = sectordir + "/meta";
2976 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2977 if(is.good() == false)
2979 // If the directory exists anyway, it probably is in some old
2980 // format. Just go ahead and create the sector.
2981 if(fs::PathExists(sectordir))
2983 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2984 <<fullpath<<" doesn't exist but directory does."
2985 <<" Continuing with a sector with no metadata."
2987 sector = new ServerMapSector(this, p2d);
2988 m_sectors.insert(p2d, sector);
2992 throw FileNotGoodException("Cannot open sector metafile");
2997 sector = ServerMapSector::deSerialize
2998 (is, this, p2d, m_sectors);
3000 saveSectorMeta(sector);
3003 sector->differs_from_disk = false;
3008 bool ServerMap::loadSectorMeta(v2s16 p2d)
3010 DSTACK(__FUNCTION_NAME);
3012 MapSector *sector = NULL;
3014 // The directory layout we're going to load from.
3015 // 1 - original sectors/xxxxzzzz/
3016 // 2 - new sectors2/xxx/zzz/
3017 // If we load from anything but the latest structure, we will
3018 // immediately save to the new one, and remove the old.
3020 std::string sectordir1 = getSectorDir(p2d, 1);
3021 std::string sectordir;
3022 if(fs::PathExists(sectordir1))
3024 sectordir = sectordir1;
3029 sectordir = getSectorDir(p2d, 2);
3033 sector = loadSectorMeta(sectordir, loadlayout != 2);
3035 catch(InvalidFilenameException &e)
3039 catch(FileNotGoodException &e)
3043 catch(std::exception &e)
3052 bool ServerMap::loadSectorFull(v2s16 p2d)
3054 DSTACK(__FUNCTION_NAME);
3056 MapSector *sector = NULL;
3058 // The directory layout we're going to load from.
3059 // 1 - original sectors/xxxxzzzz/
3060 // 2 - new sectors2/xxx/zzz/
3061 // If we load from anything but the latest structure, we will
3062 // immediately save to the new one, and remove the old.
3064 std::string sectordir1 = getSectorDir(p2d, 1);
3065 std::string sectordir;
3066 if(fs::PathExists(sectordir1))
3068 sectordir = sectordir1;
3073 sectordir = getSectorDir(p2d, 2);
3077 sector = loadSectorMeta(sectordir, loadlayout != 2);
3079 catch(InvalidFilenameException &e)
3083 catch(FileNotGoodException &e)
3087 catch(std::exception &e)
3095 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3097 std::vector<fs::DirListNode>::iterator i2;
3098 for(i2=list2.begin(); i2!=list2.end(); i2++)
3104 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3106 catch(InvalidFilenameException &e)
3108 // This catches unknown crap in directory
3114 dstream<<"Sector converted to new layout - deleting "<<
3115 sectordir1<<std::endl;
3116 fs::RecursiveDelete(sectordir1);
3123 void ServerMap::saveBlock(MapBlock *block)
3125 DSTACK(__FUNCTION_NAME);
3127 Dummy blocks are not written
3129 if(block->isDummy())
3131 /*v3s16 p = block->getPos();
3132 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3133 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3137 // Format used for writing
3138 u8 version = SER_FMT_VER_HIGHEST;
3140 v3s16 p3d = block->getPos();
3142 v2s16 p2d(p3d.X, p3d.Z);
3143 std::string sectordir = getSectorDir(p2d);
3145 createDirs(sectordir);
3147 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3148 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3149 if(o.good() == false)
3150 throw FileNotGoodException("Cannot open block data");
3153 [0] u8 serialization version
3156 o.write((char*)&version, 1);
3159 block->serialize(o, version);
3161 // Write extra data stored on disk
3162 block->serializeDiskExtra(o, version);
3164 // We just wrote it to the disk so clear modified flag
3165 block->resetModified();
3168 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3170 DSTACK(__FUNCTION_NAME);
3172 std::string fullpath = sectordir+"/"+blockfile;
3175 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3176 if(is.good() == false)
3177 throw FileNotGoodException("Cannot open block file");
3179 v3s16 p3d = getBlockPos(sectordir, blockfile);
3180 v2s16 p2d(p3d.X, p3d.Z);
3182 assert(sector->getPos() == p2d);
3184 u8 version = SER_FMT_VER_INVALID;
3185 is.read((char*)&version, 1);
3188 throw SerializationError("ServerMap::loadBlock(): Failed"
3189 " to read MapBlock version");
3191 /*u32 block_size = MapBlock::serializedLength(version);
3192 SharedBuffer<u8> data(block_size);
3193 is.read((char*)*data, block_size);*/
3195 // This will always return a sector because we're the server
3196 //MapSector *sector = emergeSector(p2d);
3198 MapBlock *block = NULL;
3199 bool created_new = false;
3200 block = sector->getBlockNoCreateNoEx(p3d.Y);
3203 block = sector->createBlankBlockNoInsert(p3d.Y);
3208 block->deSerialize(is, version);
3210 // Read extra data stored on disk
3211 block->deSerializeDiskExtra(is, version);
3213 // If it's a new block, insert it to the map
3215 sector->insertBlock(block);
3218 Save blocks loaded in old format in new format
3221 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3226 // We just loaded it from the disk, so it's up-to-date.
3227 block->resetModified();
3230 catch(SerializationError &e)
3232 dstream<<"WARNING: Invalid block data on disk "
3233 <<"fullpath="<<fullpath
3234 <<" (SerializationError). "
3235 <<"what()="<<e.what()
3237 //" Ignoring. A new one will be generated.
3240 // TODO: Backup file; name is in fullpath.
3244 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3246 DSTACK(__FUNCTION_NAME);
3248 v2s16 p2d(blockpos.X, blockpos.Z);
3250 // The directory layout we're going to load from.
3251 // 1 - original sectors/xxxxzzzz/
3252 // 2 - new sectors2/xxx/zzz/
3253 // If we load from anything but the latest structure, we will
3254 // immediately save to the new one, and remove the old.
3256 std::string sectordir1 = getSectorDir(p2d, 1);
3257 std::string sectordir;
3258 if(fs::PathExists(sectordir1))
3260 sectordir = sectordir1;
3265 sectordir = getSectorDir(p2d, 2);
3269 Make sure sector is loaded
3271 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3275 sector = loadSectorMeta(sectordir, loadlayout != 2);
3277 catch(InvalidFilenameException &e)
3281 catch(FileNotGoodException &e)
3285 catch(std::exception &e)
3292 Make sure file exists
3295 std::string blockfilename = getBlockFilename(blockpos);
3296 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3302 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3303 return getBlockNoCreateNoEx(blockpos);
3306 void ServerMap::PrintInfo(std::ostream &out)
3317 ClientMap::ClientMap(
3319 MapDrawControl &control,
3320 scene::ISceneNode* parent,
3321 scene::ISceneManager* mgr,
3325 scene::ISceneNode(parent, mgr, id),
3328 m_camera_position(0,0,0),
3329 m_camera_direction(0,0,1)
3331 m_camera_mutex.Init();
3332 assert(m_camera_mutex.IsInitialized());
3334 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3335 BS*1000000,BS*1000000,BS*1000000);
3338 ClientMap::~ClientMap()
3340 /*JMutexAutoLock lock(mesh_mutex);
3349 MapSector * ClientMap::emergeSector(v2s16 p2d)
3351 DSTACK(__FUNCTION_NAME);
3352 // Check that it doesn't exist already
3354 return getSectorNoGenerate(p2d);
3356 catch(InvalidPositionException &e)
3361 ClientMapSector *sector = new ClientMapSector(this, p2d);
3364 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3365 m_sectors.insert(p2d, sector);
3372 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3374 DSTACK(__FUNCTION_NAME);
3375 ClientMapSector *sector = NULL;
3377 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3379 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3383 sector = (ClientMapSector*)n->getValue();
3384 assert(sector->getId() == MAPSECTOR_CLIENT);
3388 sector = new ClientMapSector(this, p2d);
3390 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3391 m_sectors.insert(p2d, sector);
3395 sector->deSerialize(is);
3399 void ClientMap::OnRegisterSceneNode()
3403 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3404 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3407 ISceneNode::OnRegisterSceneNode();
3410 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3412 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3413 DSTACK(__FUNCTION_NAME);
3415 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3418 This is called two times per frame, reset on the non-transparent one
3420 if(pass == scene::ESNRP_SOLID)
3422 m_last_drawn_sectors.clear();
3426 Get time for measuring timeout.
3428 Measuring time is very useful for long delays when the
3429 machine is swapping a lot.
3431 int time1 = time(0);
3433 //u32 daynight_ratio = m_client->getDayNightRatio();
3435 m_camera_mutex.Lock();
3436 v3f camera_position = m_camera_position;
3437 v3f camera_direction = m_camera_direction;
3438 m_camera_mutex.Unlock();
3441 Get all blocks and draw all visible ones
3444 v3s16 cam_pos_nodes(
3445 camera_position.X / BS,
3446 camera_position.Y / BS,
3447 camera_position.Z / BS);
3449 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3451 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3452 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3454 // Take a fair amount as we will be dropping more out later
3456 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3457 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3458 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3460 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3461 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3462 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3464 u32 vertex_count = 0;
3466 // For limiting number of mesh updates per frame
3467 u32 mesh_update_count = 0;
3469 u32 blocks_would_have_drawn = 0;
3470 u32 blocks_drawn = 0;
3472 int timecheck_counter = 0;
3473 core::map<v2s16, MapSector*>::Iterator si;
3474 si = m_sectors.getIterator();
3475 for(; si.atEnd() == false; si++)
3478 timecheck_counter++;
3479 if(timecheck_counter > 50)
3481 timecheck_counter = 0;
3482 int time2 = time(0);
3483 if(time2 > time1 + 4)
3485 dstream<<"ClientMap::renderMap(): "
3486 "Rendering takes ages, returning."
3493 MapSector *sector = si.getNode()->getValue();
3494 v2s16 sp = sector->getPos();
3496 if(m_control.range_all == false)
3498 if(sp.X < p_blocks_min.X
3499 || sp.X > p_blocks_max.X
3500 || sp.Y < p_blocks_min.Z
3501 || sp.Y > p_blocks_max.Z)
3505 core::list< MapBlock * > sectorblocks;
3506 sector->getBlocks(sectorblocks);
3512 u32 sector_blocks_drawn = 0;
3514 core::list< MapBlock * >::Iterator i;
3515 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3517 MapBlock *block = *i;
3520 Compare block position to camera position, skip
3521 if not seen on display
3524 float range = 100000 * BS;
3525 if(m_control.range_all == false)
3526 range = m_control.wanted_range * BS;
3529 if(isBlockInSight(block->getPos(), camera_position,
3530 camera_direction, range, &d) == false)
3535 // Okay, this block will be drawn. Reset usage timer.
3536 block->resetUsageTimer();
3538 // This is ugly (spherical distance limit?)
3539 /*if(m_control.range_all == false &&
3540 d - 0.5*BS*MAP_BLOCKSIZE > range)
3545 Update expired mesh (used for day/night change)
3547 It doesn't work exactly like it should now with the
3548 tasked mesh update but whatever.
3551 bool mesh_expired = false;
3554 JMutexAutoLock lock(block->mesh_mutex);
3556 mesh_expired = block->getMeshExpired();
3558 // Mesh has not been expired and there is no mesh:
3559 // block has no content
3560 if(block->mesh == NULL && mesh_expired == false)
3564 f32 faraway = BS*50;
3565 //f32 faraway = m_control.wanted_range * BS;
3568 This has to be done with the mesh_mutex unlocked
3570 // Pretty random but this should work somewhat nicely
3571 if(mesh_expired && (
3572 (mesh_update_count < 3
3573 && (d < faraway || mesh_update_count < 2)
3576 (m_control.range_all && mesh_update_count < 20)
3579 /*if(mesh_expired && mesh_update_count < 6
3580 && (d < faraway || mesh_update_count < 3))*/
3582 mesh_update_count++;
3584 // Mesh has been expired: generate new mesh
3585 //block->updateMesh(daynight_ratio);
3586 m_client->addUpdateMeshTask(block->getPos());
3588 mesh_expired = false;
3593 Draw the faces of the block
3596 JMutexAutoLock lock(block->mesh_mutex);
3598 scene::SMesh *mesh = block->mesh;
3603 blocks_would_have_drawn++;
3604 if(blocks_drawn >= m_control.wanted_max_blocks
3605 && m_control.range_all == false
3606 && d > m_control.wanted_min_range * BS)
3610 sector_blocks_drawn++;
3612 u32 c = mesh->getMeshBufferCount();
3614 for(u32 i=0; i<c; i++)
3616 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3617 const video::SMaterial& material = buf->getMaterial();
3618 video::IMaterialRenderer* rnd =
3619 driver->getMaterialRenderer(material.MaterialType);
3620 bool transparent = (rnd && rnd->isTransparent());
3621 // Render transparent on transparent pass and likewise.
3622 if(transparent == is_transparent_pass)
3625 This *shouldn't* hurt too much because Irrlicht
3626 doesn't change opengl textures if the old
3627 material is set again.
3629 driver->setMaterial(buf->getMaterial());
3630 driver->drawMeshBuffer(buf);
3631 vertex_count += buf->getVertexCount();
3635 } // foreach sectorblocks
3637 if(sector_blocks_drawn != 0)
3639 m_last_drawn_sectors[sp] = true;
3643 m_control.blocks_drawn = blocks_drawn;
3644 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3646 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3647 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3650 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3651 core::map<v3s16, MapBlock*> *affected_blocks)
3653 bool changed = false;
3655 Add it to all blocks touching it
3658 v3s16(0,0,0), // this
3659 v3s16(0,0,1), // back
3660 v3s16(0,1,0), // top
3661 v3s16(1,0,0), // right
3662 v3s16(0,0,-1), // front
3663 v3s16(0,-1,0), // bottom
3664 v3s16(-1,0,0), // left
3666 for(u16 i=0; i<7; i++)
3668 v3s16 p2 = p + dirs[i];
3669 // Block position of neighbor (or requested) node
3670 v3s16 blockpos = getNodeBlockPos(p2);
3671 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3672 if(blockref == NULL)
3674 // Relative position of requested node
3675 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3676 if(blockref->setTempMod(relpos, mod))
3681 if(changed && affected_blocks!=NULL)
3683 for(u16 i=0; i<7; i++)
3685 v3s16 p2 = p + dirs[i];
3686 // Block position of neighbor (or requested) node
3687 v3s16 blockpos = getNodeBlockPos(p2);
3688 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3689 if(blockref == NULL)
3691 affected_blocks->insert(blockpos, blockref);
3697 bool ClientMap::clearTempMod(v3s16 p,
3698 core::map<v3s16, MapBlock*> *affected_blocks)
3700 bool changed = false;
3702 v3s16(0,0,0), // this
3703 v3s16(0,0,1), // back
3704 v3s16(0,1,0), // top
3705 v3s16(1,0,0), // right
3706 v3s16(0,0,-1), // front
3707 v3s16(0,-1,0), // bottom
3708 v3s16(-1,0,0), // left
3710 for(u16 i=0; i<7; i++)
3712 v3s16 p2 = p + dirs[i];
3713 // Block position of neighbor (or requested) node
3714 v3s16 blockpos = getNodeBlockPos(p2);
3715 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3716 if(blockref == NULL)
3718 // Relative position of requested node
3719 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3720 if(blockref->clearTempMod(relpos))
3725 if(changed && affected_blocks!=NULL)
3727 for(u16 i=0; i<7; i++)
3729 v3s16 p2 = p + dirs[i];
3730 // Block position of neighbor (or requested) node
3731 v3s16 blockpos = getNodeBlockPos(p2);
3732 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3733 if(blockref == NULL)
3735 affected_blocks->insert(blockpos, blockref);
3741 void ClientMap::expireMeshes(bool only_daynight_diffed)
3743 TimeTaker timer("expireMeshes()");
3745 core::map<v2s16, MapSector*>::Iterator si;
3746 si = m_sectors.getIterator();
3747 for(; si.atEnd() == false; si++)
3749 MapSector *sector = si.getNode()->getValue();
3751 core::list< MapBlock * > sectorblocks;
3752 sector->getBlocks(sectorblocks);
3754 core::list< MapBlock * >::Iterator i;
3755 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3757 MapBlock *block = *i;
3759 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3765 JMutexAutoLock lock(block->mesh_mutex);
3766 if(block->mesh != NULL)
3768 /*block->mesh->drop();
3769 block->mesh = NULL;*/
3770 block->setMeshExpired(true);
3777 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3779 assert(mapType() == MAPTYPE_CLIENT);
3782 v3s16 p = blockpos + v3s16(0,0,0);
3783 MapBlock *b = getBlockNoCreate(p);
3784 b->updateMesh(daynight_ratio);
3785 //b->setMeshExpired(true);
3787 catch(InvalidPositionException &e){}
3790 v3s16 p = blockpos + v3s16(-1,0,0);
3791 MapBlock *b = getBlockNoCreate(p);
3792 b->updateMesh(daynight_ratio);
3793 //b->setMeshExpired(true);
3795 catch(InvalidPositionException &e){}
3797 v3s16 p = blockpos + v3s16(0,-1,0);
3798 MapBlock *b = getBlockNoCreate(p);
3799 b->updateMesh(daynight_ratio);
3800 //b->setMeshExpired(true);
3802 catch(InvalidPositionException &e){}
3804 v3s16 p = blockpos + v3s16(0,0,-1);
3805 MapBlock *b = getBlockNoCreate(p);
3806 b->updateMesh(daynight_ratio);
3807 //b->setMeshExpired(true);
3809 catch(InvalidPositionException &e){}
3814 Update mesh of block in which the node is, and if the node is at the
3815 leading edge, update the appropriate leading blocks too.
3817 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3825 v3s16 blockposes[4];
3826 for(u32 i=0; i<4; i++)
3828 v3s16 np = nodepos + dirs[i];
3829 blockposes[i] = getNodeBlockPos(np);
3830 // Don't update mesh of block if it has been done already
3831 bool already_updated = false;
3832 for(u32 j=0; j<i; j++)
3834 if(blockposes[j] == blockposes[i])
3836 already_updated = true;
3843 MapBlock *b = getBlockNoCreate(blockposes[i]);
3844 b->updateMesh(daynight_ratio);
3849 void ClientMap::PrintInfo(std::ostream &out)
3860 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3865 MapVoxelManipulator::~MapVoxelManipulator()
3867 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3871 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3873 TimeTaker timer1("emerge", &emerge_time);
3875 // Units of these are MapBlocks
3876 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3877 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3879 VoxelArea block_area_nodes
3880 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3882 addArea(block_area_nodes);
3884 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3885 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3886 for(s32 x=p_min.X; x<=p_max.X; x++)
3889 core::map<v3s16, bool>::Node *n;
3890 n = m_loaded_blocks.find(p);
3894 bool block_data_inexistent = false;
3897 TimeTaker timer1("emerge load", &emerge_load_time);
3899 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3900 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3903 dstream<<std::endl;*/
3905 MapBlock *block = m_map->getBlockNoCreate(p);
3906 if(block->isDummy())
3907 block_data_inexistent = true;
3909 block->copyTo(*this);
3911 catch(InvalidPositionException &e)
3913 block_data_inexistent = true;
3916 if(block_data_inexistent)
3918 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3919 // Fill with VOXELFLAG_INEXISTENT
3920 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3921 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3923 s32 i = m_area.index(a.MinEdge.X,y,z);
3924 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3928 m_loaded_blocks.insert(p, !block_data_inexistent);
3931 //dstream<<"emerge done"<<std::endl;
3935 SUGG: Add an option to only update eg. water and air nodes.
3936 This will make it interfere less with important stuff if
3939 void MapVoxelManipulator::blitBack
3940 (core::map<v3s16, MapBlock*> & modified_blocks)
3942 if(m_area.getExtent() == v3s16(0,0,0))
3945 //TimeTaker timer1("blitBack");
3947 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3948 <<m_loaded_blocks.size()<<std::endl;*/
3951 Initialize block cache
3953 v3s16 blockpos_last;
3954 MapBlock *block = NULL;
3955 bool block_checked_in_modified = false;
3957 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3958 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3959 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3963 u8 f = m_flags[m_area.index(p)];
3964 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3967 MapNode &n = m_data[m_area.index(p)];
3969 v3s16 blockpos = getNodeBlockPos(p);
3974 if(block == NULL || blockpos != blockpos_last){
3975 block = m_map->getBlockNoCreate(blockpos);
3976 blockpos_last = blockpos;
3977 block_checked_in_modified = false;
3980 // Calculate relative position in block
3981 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3983 // Don't continue if nothing has changed here
3984 if(block->getNode(relpos) == n)
3987 //m_map->setNode(m_area.MinEdge + p, n);
3988 block->setNode(relpos, n);
3991 Make sure block is in modified_blocks
3993 if(block_checked_in_modified == false)
3995 modified_blocks[blockpos] = block;
3996 block_checked_in_modified = true;
3999 catch(InvalidPositionException &e)
4005 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4006 MapVoxelManipulator(map),
4007 m_create_area(false)
4011 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4015 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4017 // Just create the area so that it can be pointed to
4018 VoxelManipulator::emerge(a, caller_id);
4021 void ManualMapVoxelManipulator::initialEmerge(
4022 v3s16 blockpos_min, v3s16 blockpos_max)
4024 TimeTaker timer1("initialEmerge", &emerge_time);
4026 // Units of these are MapBlocks
4027 v3s16 p_min = blockpos_min;
4028 v3s16 p_max = blockpos_max;
4030 VoxelArea block_area_nodes
4031 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4033 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4036 dstream<<"initialEmerge: area: ";
4037 block_area_nodes.print(dstream);
4038 dstream<<" ("<<size_MB<<"MB)";
4042 addArea(block_area_nodes);
4044 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4045 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4046 for(s32 x=p_min.X; x<=p_max.X; x++)
4049 core::map<v3s16, bool>::Node *n;
4050 n = m_loaded_blocks.find(p);
4054 bool block_data_inexistent = false;
4057 TimeTaker timer1("emerge load", &emerge_load_time);
4059 MapBlock *block = m_map->getBlockNoCreate(p);
4060 if(block->isDummy())
4061 block_data_inexistent = true;
4063 block->copyTo(*this);
4065 catch(InvalidPositionException &e)
4067 block_data_inexistent = true;
4070 if(block_data_inexistent)
4073 Mark area inexistent
4075 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4076 // Fill with VOXELFLAG_INEXISTENT
4077 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4078 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4080 s32 i = m_area.index(a.MinEdge.X,y,z);
4081 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4085 m_loaded_blocks.insert(p, !block_data_inexistent);
4089 void ManualMapVoxelManipulator::blitBackAll(
4090 core::map<v3s16, MapBlock*> * modified_blocks)
4092 if(m_area.getExtent() == v3s16(0,0,0))
4096 Copy data of all blocks
4098 for(core::map<v3s16, bool>::Iterator
4099 i = m_loaded_blocks.getIterator();
4100 i.atEnd() == false; i++)
4102 v3s16 p = i.getNode()->getKey();
4103 bool existed = i.getNode()->getValue();
4104 if(existed == false)
4106 // The Great Bug was found using this
4107 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4108 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4112 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4115 dstream<<"WARNING: "<<__FUNCTION_NAME
4116 <<": got NULL block "
4117 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4122 block->copyFrom(*this);
4125 modified_blocks->insert(p, block);