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.getContent() == CONTENT_MUD)
635 n.setContent(CONTENT_GRASS);
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).walkable == true)
926 MapNode bottomnode = getNode(bottompos);
928 if(bottomnode.getContent() == CONTENT_GRASS
929 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
931 bottomnode.setContent(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.getContent() == CONTENT_MUD && node_under_sunlight)
948 n.setContent(CONTENT_GRASS);
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).sunlight_propagates)
991 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
995 Set the node on the map
1004 NodeMetadata *meta_proto = content_features(n).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).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.getContent()))
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 content_t 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.setContent(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 and this node to transform queue.
1244 (it's vital for the node itself to get updated last.)
1247 v3s16(0,0,1), // back
1248 v3s16(0,1,0), // top
1249 v3s16(1,0,0), // right
1250 v3s16(0,0,-1), // front
1251 v3s16(0,-1,0), // bottom
1252 v3s16(-1,0,0), // left
1253 v3s16(0,0,0), // self
1255 for(u16 i=0; i<7; i++)
1260 v3s16 p2 = p + dirs[i];
1262 MapNode n2 = getNode(p2);
1263 if(content_liquid(n2.getContent()))
1265 m_transforming_liquid.push_back(p2);
1268 }catch(InvalidPositionException &e)
1274 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1277 event.type = MEET_ADDNODE;
1281 bool succeeded = true;
1283 core::map<v3s16, MapBlock*> modified_blocks;
1284 addNodeAndUpdate(p, n, modified_blocks);
1286 // Copy modified_blocks to event
1287 for(core::map<v3s16, MapBlock*>::Iterator
1288 i = modified_blocks.getIterator();
1289 i.atEnd()==false; i++)
1291 event.modified_blocks.insert(i.getNode()->getKey(), false);
1294 catch(InvalidPositionException &e){
1298 dispatchEvent(&event);
1303 bool Map::removeNodeWithEvent(v3s16 p)
1306 event.type = MEET_REMOVENODE;
1309 bool succeeded = true;
1311 core::map<v3s16, MapBlock*> modified_blocks;
1312 removeNodeAndUpdate(p, modified_blocks);
1314 // Copy modified_blocks to event
1315 for(core::map<v3s16, MapBlock*>::Iterator
1316 i = modified_blocks.getIterator();
1317 i.atEnd()==false; i++)
1319 event.modified_blocks.insert(i.getNode()->getKey(), false);
1322 catch(InvalidPositionException &e){
1326 dispatchEvent(&event);
1331 bool Map::dayNightDiffed(v3s16 blockpos)
1334 v3s16 p = blockpos + v3s16(0,0,0);
1335 MapBlock *b = getBlockNoCreate(p);
1336 if(b->dayNightDiffed())
1339 catch(InvalidPositionException &e){}
1342 v3s16 p = blockpos + v3s16(-1,0,0);
1343 MapBlock *b = getBlockNoCreate(p);
1344 if(b->dayNightDiffed())
1347 catch(InvalidPositionException &e){}
1349 v3s16 p = blockpos + v3s16(0,-1,0);
1350 MapBlock *b = getBlockNoCreate(p);
1351 if(b->dayNightDiffed())
1354 catch(InvalidPositionException &e){}
1356 v3s16 p = blockpos + v3s16(0,0,-1);
1357 MapBlock *b = getBlockNoCreate(p);
1358 if(b->dayNightDiffed())
1361 catch(InvalidPositionException &e){}
1364 v3s16 p = blockpos + v3s16(1,0,0);
1365 MapBlock *b = getBlockNoCreate(p);
1366 if(b->dayNightDiffed())
1369 catch(InvalidPositionException &e){}
1371 v3s16 p = blockpos + v3s16(0,1,0);
1372 MapBlock *b = getBlockNoCreate(p);
1373 if(b->dayNightDiffed())
1376 catch(InvalidPositionException &e){}
1378 v3s16 p = blockpos + v3s16(0,0,1);
1379 MapBlock *b = getBlockNoCreate(p);
1380 if(b->dayNightDiffed())
1383 catch(InvalidPositionException &e){}
1389 Updates usage timers
1391 void Map::timerUpdate(float dtime, float unload_timeout,
1392 core::list<v3s16> *unloaded_blocks)
1394 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1396 core::list<v2s16> sector_deletion_queue;
1397 u32 deleted_blocks_count = 0;
1398 u32 saved_blocks_count = 0;
1400 core::map<v2s16, MapSector*>::Iterator si;
1402 si = m_sectors.getIterator();
1403 for(; si.atEnd() == false; si++)
1405 MapSector *sector = si.getNode()->getValue();
1407 bool all_blocks_deleted = true;
1409 core::list<MapBlock*> blocks;
1410 sector->getBlocks(blocks);
1411 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1412 i != blocks.end(); i++)
1414 MapBlock *block = (*i);
1416 block->incrementUsageTimer(dtime);
1418 if(block->getUsageTimer() > unload_timeout)
1420 v3s16 p = block->getPos();
1423 if(block->getModified() != MOD_STATE_CLEAN
1424 && save_before_unloading)
1427 saved_blocks_count++;
1430 // Delete from memory
1431 sector->deleteBlock(block);
1434 unloaded_blocks->push_back(p);
1436 deleted_blocks_count++;
1440 all_blocks_deleted = false;
1444 if(all_blocks_deleted)
1446 sector_deletion_queue.push_back(si.getNode()->getKey());
1450 // Finally delete the empty sectors
1451 deleteSectors(sector_deletion_queue);
1453 if(deleted_blocks_count != 0)
1455 PrintInfo(dstream); // ServerMap/ClientMap:
1456 dstream<<"Unloaded "<<deleted_blocks_count
1457 <<" blocks from memory";
1458 if(save_before_unloading)
1459 dstream<<", of which "<<saved_blocks_count<<" were written";
1460 dstream<<"."<<std::endl;
1464 void Map::deleteSectors(core::list<v2s16> &list)
1466 core::list<v2s16>::Iterator j;
1467 for(j=list.begin(); j!=list.end(); j++)
1469 MapSector *sector = m_sectors[*j];
1470 // If sector is in sector cache, remove it from there
1471 if(m_sector_cache == sector)
1472 m_sector_cache = NULL;
1473 // Remove from map and delete
1474 m_sectors.remove(*j);
1480 void Map::unloadUnusedData(float timeout,
1481 core::list<v3s16> *deleted_blocks)
1483 core::list<v2s16> sector_deletion_queue;
1484 u32 deleted_blocks_count = 0;
1485 u32 saved_blocks_count = 0;
1487 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1488 for(; si.atEnd() == false; si++)
1490 MapSector *sector = si.getNode()->getValue();
1492 bool all_blocks_deleted = true;
1494 core::list<MapBlock*> blocks;
1495 sector->getBlocks(blocks);
1496 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1497 i != blocks.end(); i++)
1499 MapBlock *block = (*i);
1501 if(block->getUsageTimer() > timeout)
1504 if(block->getModified() != MOD_STATE_CLEAN)
1507 saved_blocks_count++;
1509 // Delete from memory
1510 sector->deleteBlock(block);
1511 deleted_blocks_count++;
1515 all_blocks_deleted = false;
1519 if(all_blocks_deleted)
1521 sector_deletion_queue.push_back(si.getNode()->getKey());
1525 deleteSectors(sector_deletion_queue);
1527 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1528 <<", of which "<<saved_blocks_count<<" were wr."
1531 //return sector_deletion_queue.getSize();
1532 //return deleted_blocks_count;
1536 void Map::PrintInfo(std::ostream &out)
1541 #define WATER_DROP_BOOST 4
1543 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1545 DSTACK(__FUNCTION_NAME);
1546 //TimeTaker timer("transformLiquids()");
1549 u32 initial_size = m_transforming_liquid.size();
1551 /*if(initial_size != 0)
1552 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1554 while(m_transforming_liquid.size() != 0)
1557 Get a queued transforming liquid node
1559 v3s16 p0 = m_transforming_liquid.pop_front();
1561 MapNode n0 = getNodeNoEx(p0);
1563 // Don't deal with non-liquids
1564 if(content_liquid(n0.getContent()) == false)
1567 bool is_source = !content_flowing_liquid(n0.getContent());
1569 u8 liquid_level = 8;
1570 if(is_source == false)
1571 liquid_level = n0.param2 & 0x0f;
1573 // Turn possible source into non-source
1574 u8 nonsource_c = make_liquid_flowing(n0.getContent());
1577 If not source, check that some node flows into this one
1578 and what is the level of liquid in this one
1580 if(is_source == false)
1582 s8 new_liquid_level_max = -1;
1584 v3s16 dirs_from[5] = {
1585 v3s16(0,1,0), // top
1586 v3s16(0,0,1), // back
1587 v3s16(1,0,0), // right
1588 v3s16(0,0,-1), // front
1589 v3s16(-1,0,0), // left
1591 for(u16 i=0; i<5; i++)
1593 bool from_top = (i==0);
1595 v3s16 p2 = p0 + dirs_from[i];
1596 MapNode n2 = getNodeNoEx(p2);
1598 if(content_liquid(n2.getContent()))
1600 u8 n2_nonsource_c = make_liquid_flowing(n2.getContent());
1601 // Check that the liquids are the same type
1602 if(n2_nonsource_c != nonsource_c)
1604 dstream<<"WARNING: Not handling: different liquids"
1605 " collide"<<std::endl;
1608 bool n2_is_source = !content_flowing_liquid(n2.getContent());
1609 s8 n2_liquid_level = 8;
1610 if(n2_is_source == false)
1611 n2_liquid_level = n2.param2 & 0x07;
1613 s8 new_liquid_level = -1;
1616 //new_liquid_level = 7;
1617 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1618 new_liquid_level = 7;
1620 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1622 else if(n2_liquid_level > 0)
1624 new_liquid_level = n2_liquid_level - 1;
1627 if(new_liquid_level > new_liquid_level_max)
1628 new_liquid_level_max = new_liquid_level;
1633 If liquid level should be something else, update it and
1634 add all the neighboring water nodes to the transform queue.
1636 if(new_liquid_level_max != liquid_level)
1638 if(new_liquid_level_max == -1)
1640 // Remove water alltoghether
1641 n0.setContent(CONTENT_AIR);
1647 n0.param2 = new_liquid_level_max;
1651 // Block has been modified
1653 v3s16 blockpos = getNodeBlockPos(p0);
1654 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1656 modified_blocks.insert(blockpos, block);
1660 Add neighboring non-source liquid nodes to transform queue.
1663 v3s16(0,0,1), // back
1664 v3s16(0,1,0), // top
1665 v3s16(1,0,0), // right
1666 v3s16(0,0,-1), // front
1667 v3s16(0,-1,0), // bottom
1668 v3s16(-1,0,0), // left
1670 for(u16 i=0; i<6; i++)
1672 v3s16 p2 = p0 + dirs[i];
1674 MapNode n2 = getNodeNoEx(p2);
1675 if(content_flowing_liquid(n2.getContent()))
1677 m_transforming_liquid.push_back(p2);
1683 // Get a new one from queue if the node has turned into non-water
1684 if(content_liquid(n0.getContent()) == false)
1688 Flow water from this node
1690 v3s16 dirs_to[5] = {
1691 v3s16(0,-1,0), // bottom
1692 v3s16(0,0,1), // back
1693 v3s16(1,0,0), // right
1694 v3s16(0,0,-1), // front
1695 v3s16(-1,0,0), // left
1697 for(u16 i=0; i<5; i++)
1699 bool to_bottom = (i == 0);
1701 // If liquid is at lowest possible height, it's not going
1702 // anywhere except down
1703 if(liquid_level == 0 && to_bottom == false)
1706 u8 liquid_next_level = 0;
1707 // If going to bottom
1710 //liquid_next_level = 7;
1711 if(liquid_level >= 7 - WATER_DROP_BOOST)
1712 liquid_next_level = 7;
1714 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1717 liquid_next_level = liquid_level - 1;
1719 bool n2_changed = false;
1720 bool flowed = false;
1722 v3s16 p2 = p0 + dirs_to[i];
1724 MapNode n2 = getNodeNoEx(p2);
1725 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1727 if(content_liquid(n2.getContent()))
1729 u8 n2_nonsource_c = make_liquid_flowing(n2.getContent());
1730 // Check that the liquids are the same type
1731 if(n2_nonsource_c != nonsource_c)
1733 dstream<<"WARNING: Not handling: different liquids"
1734 " collide"<<std::endl;
1737 bool n2_is_source = !content_flowing_liquid(n2.getContent());
1738 u8 n2_liquid_level = 8;
1739 if(n2_is_source == false)
1740 n2_liquid_level = n2.param2 & 0x07;
1749 // Just flow into the source, nothing changes.
1750 // n2_changed is not set because destination didn't change
1755 if(liquid_next_level > liquid_level)
1757 n2.param2 = liquid_next_level;
1765 else if(n2.getContent() == CONTENT_AIR)
1767 n2.setContent(nonsource_c);
1768 n2.param2 = liquid_next_level;
1775 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1779 m_transforming_liquid.push_back(p2);
1781 v3s16 blockpos = getNodeBlockPos(p2);
1782 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1784 modified_blocks.insert(blockpos, block);
1787 // If n2_changed to bottom, don't flow anywhere else
1788 if(to_bottom && flowed && !is_source)
1793 //if(loopcount >= 100000)
1794 if(loopcount >= initial_size * 1)
1797 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1800 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1802 v3s16 blockpos = getNodeBlockPos(p);
1803 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1804 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1807 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1811 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1815 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1817 v3s16 blockpos = getNodeBlockPos(p);
1818 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1819 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1822 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1826 block->m_node_metadata.set(p_rel, meta);
1829 void Map::removeNodeMetadata(v3s16 p)
1831 v3s16 blockpos = getNodeBlockPos(p);
1832 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1833 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1836 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1840 block->m_node_metadata.remove(p_rel);
1843 void Map::nodeMetadataStep(float dtime,
1844 core::map<v3s16, MapBlock*> &changed_blocks)
1848 Currently there is no way to ensure that all the necessary
1849 blocks are loaded when this is run. (They might get unloaded)
1850 NOTE: ^- Actually, that might not be so. In a quick test it
1851 reloaded a block with a furnace when I walked back to it from
1854 core::map<v2s16, MapSector*>::Iterator si;
1855 si = m_sectors.getIterator();
1856 for(; si.atEnd() == false; si++)
1858 MapSector *sector = si.getNode()->getValue();
1859 core::list< MapBlock * > sectorblocks;
1860 sector->getBlocks(sectorblocks);
1861 core::list< MapBlock * >::Iterator i;
1862 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1864 MapBlock *block = *i;
1865 bool changed = block->m_node_metadata.step(dtime);
1867 changed_blocks[block->getPos()] = block;
1876 ServerMap::ServerMap(std::string savedir):
1879 m_map_metadata_changed(true)
1881 dstream<<__FUNCTION_NAME<<std::endl;
1883 //m_chunksize = 8; // Takes a few seconds
1885 m_seed = (((u64)(myrand()%0xffff)<<0)
1886 + ((u64)(myrand()%0xffff)<<16)
1887 + ((u64)(myrand()%0xffff)<<32)
1888 + ((u64)(myrand()%0xffff)<<48));
1891 Experimental and debug stuff
1898 Try to load map; if not found, create a new one.
1901 m_savedir = savedir;
1902 m_map_saving_enabled = false;
1906 // If directory exists, check contents and load if possible
1907 if(fs::PathExists(m_savedir))
1909 // If directory is empty, it is safe to save into it.
1910 if(fs::GetDirListing(m_savedir).size() == 0)
1912 dstream<<DTIME<<"Server: Empty save directory is valid."
1914 m_map_saving_enabled = true;
1919 // Load map metadata (seed, chunksize)
1922 catch(FileNotGoodException &e){
1923 dstream<<DTIME<<"WARNING: Could not load map metadata"
1924 //<<" Disabling chunk-based generator."
1930 // Load chunk metadata
1933 catch(FileNotGoodException &e){
1934 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1935 <<" Disabling chunk-based generator."
1940 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1941 "metadata and sector (0,0) from "<<savedir<<
1942 ", assuming valid save directory."
1945 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1946 <<"and chunk metadata from "<<savedir
1947 <<", assuming valid save directory."
1950 m_map_saving_enabled = true;
1951 // Map loaded, not creating new one
1955 // If directory doesn't exist, it is safe to save to it
1957 m_map_saving_enabled = true;
1960 catch(std::exception &e)
1962 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1963 <<", exception: "<<e.what()<<std::endl;
1964 dstream<<"Please remove the map or fix it."<<std::endl;
1965 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1968 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1970 // Create zero sector
1971 emergeSector(v2s16(0,0));
1973 // Initially write whole map
1977 ServerMap::~ServerMap()
1979 dstream<<__FUNCTION_NAME<<std::endl;
1983 if(m_map_saving_enabled)
1985 // Save only changed parts
1987 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1991 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1994 catch(std::exception &e)
1996 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1997 <<", exception: "<<e.what()<<std::endl;
2004 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2005 for(; i.atEnd() == false; i++)
2007 MapChunk *chunk = i.getNode()->getValue();
2013 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2015 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2016 if(enable_mapgen_debug_info)
2017 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2018 <<blockpos.Z<<")"<<std::endl;
2020 // Do nothing if not inside limits (+-1 because of neighbors)
2021 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2022 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2028 data->no_op = false;
2029 data->seed = m_seed;
2030 data->blockpos = blockpos;
2033 Create the whole area of this and the neighboring blocks
2036 //TimeTaker timer("initBlockMake() create area");
2038 for(s16 x=-1; x<=1; x++)
2039 for(s16 z=-1; z<=1; z++)
2041 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2042 // Sector metadata is loaded from disk if not already loaded.
2043 ServerMapSector *sector = createSector(sectorpos);
2046 for(s16 y=-1; y<=1; y++)
2048 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2049 //MapBlock *block = createBlock(p);
2050 // 1) get from memory, 2) load from disk
2051 MapBlock *block = emergeBlock(p, false);
2052 // 3) create a blank one
2055 block = createBlock(p);
2058 Block gets sunlight if this is true.
2060 Refer to the map generator heuristics.
2062 bool ug = mapgen::block_is_underground(data->seed, p);
2063 block->setIsUnderground(ug);
2066 // Lighting will not be valid after make_chunk is called
2067 block->setLightingExpired(true);
2068 // Lighting will be calculated
2069 //block->setLightingExpired(false);
2075 Now we have a big empty area.
2077 Make a ManualMapVoxelManipulator that contains this and the
2081 // The area that contains this block and it's neighbors
2082 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2083 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2085 data->vmanip = new ManualMapVoxelManipulator(this);
2086 //data->vmanip->setMap(this);
2090 //TimeTaker timer("initBlockMake() initialEmerge");
2091 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2094 // Data is ready now.
2097 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2098 core::map<v3s16, MapBlock*> &changed_blocks)
2100 v3s16 blockpos = data->blockpos;
2101 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2102 <<blockpos.Z<<")"<<std::endl;*/
2106 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2110 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2112 /*dstream<<"Resulting vmanip:"<<std::endl;
2113 data->vmanip.print(dstream);*/
2116 Blit generated stuff to map
2117 NOTE: blitBackAll adds nearly everything to changed_blocks
2121 //TimeTaker timer("finishBlockMake() blitBackAll");
2122 data->vmanip->blitBackAll(&changed_blocks);
2125 if(enable_mapgen_debug_info)
2126 dstream<<"finishBlockMake: changed_blocks.size()="
2127 <<changed_blocks.size()<<std::endl;
2130 Copy transforming liquid information
2132 while(data->transforming_liquid.size() > 0)
2134 v3s16 p = data->transforming_liquid.pop_front();
2135 m_transforming_liquid.push_back(p);
2141 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2145 Set is_underground flag for lighting with sunlight.
2147 Refer to map generator heuristics.
2149 NOTE: This is done in initChunkMake
2151 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2155 Add sunlight to central block.
2156 This makes in-dark-spawning monsters to not flood the whole thing.
2157 Do not spread the light, though.
2159 /*core::map<v3s16, bool> light_sources;
2160 bool black_air_left = false;
2161 block->propagateSunlight(light_sources, true, &black_air_left);*/
2164 NOTE: Lighting and object adding shouldn't really be here, but
2165 lighting is a bit tricky to move properly to makeBlock.
2166 TODO: Do this the right way anyway, that is, move it to makeBlock.
2167 - There needs to be some way for makeBlock to report back if
2168 the lighting update is going further down because of the
2169 new block blocking light
2174 NOTE: This takes ~60ms, TODO: Investigate why
2177 TimeTaker t("finishBlockMake lighting update");
2179 core::map<v3s16, MapBlock*> lighting_update_blocks;
2182 lighting_update_blocks.insert(block->getPos(), block);
2187 v3s16 p = block->getPos()+v3s16(x,1,z);
2188 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2192 // All modified blocks
2193 // NOTE: Should this be done? If this is not done, then the lighting
2194 // of the others will be updated in a different place, one by one, i
2195 // think... or they might not? Well, at least they are left marked as
2196 // "lighting expired"; it seems that is not handled at all anywhere,
2197 // so enabling this will slow it down A LOT because otherwise it
2198 // would not do this at all. This causes the black trees.
2199 for(core::map<v3s16, MapBlock*>::Iterator
2200 i = changed_blocks.getIterator();
2201 i.atEnd() == false; i++)
2203 lighting_update_blocks.insert(i.getNode()->getKey(),
2204 i.getNode()->getValue());
2206 /*// Also force-add all the upmost blocks for proper sunlight
2207 for(s16 x=-1; x<=1; x++)
2208 for(s16 z=-1; z<=1; z++)
2210 v3s16 p = block->getPos()+v3s16(x,1,z);
2211 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2214 updateLighting(lighting_update_blocks, changed_blocks);
2217 Set lighting to non-expired state in all of them.
2218 This is cheating, but it is not fast enough if all of them
2219 would actually be updated.
2221 for(s16 x=-1; x<=1; x++)
2222 for(s16 y=-1; y<=1; y++)
2223 for(s16 z=-1; z<=1; z++)
2225 v3s16 p = block->getPos()+v3s16(x,y,z);
2226 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2229 if(enable_mapgen_debug_info == false)
2230 t.stop(true); // Hide output
2234 Add random objects to block
2236 mapgen::add_random_objects(block);
2239 Go through changed blocks
2241 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2242 i.atEnd() == false; i++)
2244 MapBlock *block = i.getNode()->getValue();
2247 Update day/night difference cache of the MapBlocks
2249 block->updateDayNightDiff();
2251 Set block as modified
2253 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2257 Set central block as generated
2259 block->setGenerated(true);
2262 Save changed parts of map
2263 NOTE: Will be saved later.
2267 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2268 <<blockpos.Z<<")"<<std::endl;*/
2270 if(enable_mapgen_debug_info)
2273 Analyze resulting blocks
2275 for(s16 x=-1; x<=1; x++)
2276 for(s16 y=-1; y<=1; y++)
2277 for(s16 z=-1; z<=1; z++)
2279 v3s16 p = block->getPos()+v3s16(x,y,z);
2280 MapBlock *block = getBlockNoCreateNoEx(p);
2282 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2283 dstream<<"Generated "<<spos<<": "
2284 <<analyze_block(block)<<std::endl;
2292 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2294 DSTACKF("%s: p2d=(%d,%d)",
2299 Check if it exists already in memory
2301 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2306 Try to load it from disk (with blocks)
2308 //if(loadSectorFull(p2d) == true)
2311 Try to load metadata from disk
2313 if(loadSectorMeta(p2d) == true)
2315 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2318 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2319 throw InvalidPositionException("");
2325 Do not create over-limit
2327 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2328 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2329 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2330 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2331 throw InvalidPositionException("createSector(): pos. over limit");
2334 Generate blank sector
2337 sector = new ServerMapSector(this, p2d);
2339 // Sector position on map in nodes
2340 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2345 m_sectors.insert(p2d, sector);
2351 This is a quick-hand function for calling makeBlock().
2353 MapBlock * ServerMap::generateBlock(
2355 core::map<v3s16, MapBlock*> &modified_blocks
2358 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2360 /*dstream<<"generateBlock(): "
2361 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2364 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2366 TimeTaker timer("generateBlock");
2368 //MapBlock *block = original_dummy;
2370 v2s16 p2d(p.X, p.Z);
2371 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2374 Do not generate over-limit
2376 if(blockpos_over_limit(p))
2378 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2379 throw InvalidPositionException("generateBlock(): pos. over limit");
2383 Create block make data
2385 mapgen::BlockMakeData data;
2386 initBlockMake(&data, p);
2392 TimeTaker t("mapgen::make_block()");
2393 mapgen::make_block(&data);
2395 if(enable_mapgen_debug_info == false)
2396 t.stop(true); // Hide output
2400 Blit data back on map, update lighting, add mobs and whatever this does
2402 finishBlockMake(&data, modified_blocks);
2407 MapBlock *block = getBlockNoCreateNoEx(p);
2415 bool erroneus_content = false;
2416 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2417 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2418 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2421 MapNode n = block->getNode(p);
2422 if(n.getContent() == CONTENT_IGNORE)
2424 dstream<<"CONTENT_IGNORE at "
2425 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2427 erroneus_content = true;
2431 if(erroneus_content)
2440 Generate a completely empty block
2444 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2445 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2447 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2451 n.setContent(CONTENT_AIR);
2453 n.setContent(CONTENT_STONE);
2454 block->setNode(v3s16(x0,y0,z0), n);
2460 if(enable_mapgen_debug_info == false)
2461 timer.stop(true); // Hide output
2466 MapBlock * ServerMap::createBlock(v3s16 p)
2468 DSTACKF("%s: p=(%d,%d,%d)",
2469 __FUNCTION_NAME, p.X, p.Y, p.Z);
2472 Do not create over-limit
2474 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2475 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2476 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2477 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2478 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2479 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2480 throw InvalidPositionException("createBlock(): pos. over limit");
2482 v2s16 p2d(p.X, p.Z);
2485 This will create or load a sector if not found in memory.
2486 If block exists on disk, it will be loaded.
2488 NOTE: On old save formats, this will be slow, as it generates
2489 lighting on blocks for them.
2491 ServerMapSector *sector;
2493 sector = (ServerMapSector*)createSector(p2d);
2494 assert(sector->getId() == MAPSECTOR_SERVER);
2496 catch(InvalidPositionException &e)
2498 dstream<<"createBlock: createSector() failed"<<std::endl;
2502 NOTE: This should not be done, or at least the exception
2503 should not be passed on as std::exception, because it
2504 won't be catched at all.
2506 /*catch(std::exception &e)
2508 dstream<<"createBlock: createSector() failed: "
2509 <<e.what()<<std::endl;
2514 Try to get a block from the sector
2517 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2520 if(block->isDummy())
2525 block = sector->createBlankBlock(block_y);
2529 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2531 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2533 p.X, p.Y, p.Z, allow_generate);
2536 MapBlock *block = getBlockNoCreateNoEx(p);
2537 if(block && block->isDummy() == false)
2542 MapBlock *block = loadBlock(p);
2549 core::map<v3s16, MapBlock*> modified_blocks;
2550 MapBlock *block = generateBlock(p, modified_blocks);
2554 event.type = MEET_OTHER;
2557 // Copy modified_blocks to event
2558 for(core::map<v3s16, MapBlock*>::Iterator
2559 i = modified_blocks.getIterator();
2560 i.atEnd()==false; i++)
2562 event.modified_blocks.insert(i.getNode()->getKey(), false);
2566 dispatchEvent(&event);
2577 Do not generate over-limit
2579 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2580 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2581 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2582 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2583 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2584 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2585 throw InvalidPositionException("emergeBlock(): pos. over limit");
2587 v2s16 p2d(p.X, p.Z);
2590 This will create or load a sector if not found in memory.
2591 If block exists on disk, it will be loaded.
2593 ServerMapSector *sector;
2595 sector = createSector(p2d);
2596 //sector = emergeSector(p2d, changed_blocks);
2598 catch(InvalidPositionException &e)
2600 dstream<<"emergeBlock: createSector() failed: "
2601 <<e.what()<<std::endl;
2602 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2604 <<"You could try to delete it."<<std::endl;
2607 catch(VersionMismatchException &e)
2609 dstream<<"emergeBlock: createSector() failed: "
2610 <<e.what()<<std::endl;
2611 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2613 <<"You could try to delete it."<<std::endl;
2618 Try to get a block from the sector
2621 bool does_not_exist = false;
2622 bool lighting_expired = false;
2623 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2625 // If not found, try loading from disk
2628 block = loadBlock(p);
2634 does_not_exist = true;
2636 else if(block->isDummy() == true)
2638 does_not_exist = true;
2640 else if(block->getLightingExpired())
2642 lighting_expired = true;
2647 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2652 If block was not found on disk and not going to generate a
2653 new one, make sure there is a dummy block in place.
2655 if(only_from_disk && (does_not_exist || lighting_expired))
2657 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2661 // Create dummy block
2662 block = new MapBlock(this, p, true);
2664 // Add block to sector
2665 sector->insertBlock(block);
2671 //dstream<<"Not found on disk, generating."<<std::endl;
2673 //TimeTaker("emergeBlock() generate");
2675 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2678 If the block doesn't exist, generate the block.
2682 block = generateBlock(p, block, sector, changed_blocks,
2683 lighting_invalidated_blocks);
2686 if(lighting_expired)
2688 lighting_invalidated_blocks.insert(p, block);
2693 Initially update sunlight
2696 core::map<v3s16, bool> light_sources;
2697 bool black_air_left = false;
2698 bool bottom_invalid =
2699 block->propagateSunlight(light_sources, true,
2702 // If sunlight didn't reach everywhere and part of block is
2703 // above ground, lighting has to be properly updated
2704 //if(black_air_left && some_part_underground)
2707 lighting_invalidated_blocks[block->getPos()] = block;
2712 lighting_invalidated_blocks[block->getPos()] = block;
2721 s16 ServerMap::findGroundLevel(v2s16 p2d)
2725 Uh, just do something random...
2727 // Find existing map from top to down
2730 v3s16 p(p2d.X, max, p2d.Y);
2731 for(; p.Y>min; p.Y--)
2733 MapNode n = getNodeNoEx(p);
2734 if(n.getContent() != CONTENT_IGNORE)
2739 // If this node is not air, go to plan b
2740 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2742 // Search existing walkable and return it
2743 for(; p.Y>min; p.Y--)
2745 MapNode n = getNodeNoEx(p);
2746 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2755 Determine from map generator noise functions
2758 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2761 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2762 //return (s16)level;
2765 void ServerMap::createDirs(std::string path)
2767 if(fs::CreateAllDirs(path) == false)
2769 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2770 <<"\""<<path<<"\""<<std::endl;
2771 throw BaseException("ServerMap failed to create directory");
2775 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2781 snprintf(cc, 9, "%.4x%.4x",
2782 (unsigned int)pos.X&0xffff,
2783 (unsigned int)pos.Y&0xffff);
2785 return m_savedir + "/sectors/" + cc;
2787 snprintf(cc, 9, "%.3x/%.3x",
2788 (unsigned int)pos.X&0xfff,
2789 (unsigned int)pos.Y&0xfff);
2791 return m_savedir + "/sectors2/" + cc;
2797 v2s16 ServerMap::getSectorPos(std::string dirname)
2801 size_t spos = dirname.rfind('/') + 1;
2802 assert(spos != std::string::npos);
2803 if(dirname.size() - spos == 8)
2806 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2808 else if(dirname.size() - spos == 3)
2811 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2812 // Sign-extend the 12 bit values up to 16 bits...
2813 if(x&0x800) x|=0xF000;
2814 if(y&0x800) y|=0xF000;
2821 v2s16 pos((s16)x, (s16)y);
2825 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2827 v2s16 p2d = getSectorPos(sectordir);
2829 if(blockfile.size() != 4){
2830 throw InvalidFilenameException("Invalid block filename");
2833 int r = sscanf(blockfile.c_str(), "%4x", &y);
2835 throw InvalidFilenameException("Invalid block filename");
2836 return v3s16(p2d.X, y, p2d.Y);
2839 std::string ServerMap::getBlockFilename(v3s16 p)
2842 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2846 void ServerMap::save(bool only_changed)
2848 DSTACK(__FUNCTION_NAME);
2849 if(m_map_saving_enabled == false)
2851 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2855 if(only_changed == false)
2856 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2859 if(only_changed == false || m_map_metadata_changed)
2864 u32 sector_meta_count = 0;
2865 u32 block_count = 0;
2866 u32 block_count_all = 0; // Number of blocks in memory
2868 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2869 for(; i.atEnd() == false; i++)
2871 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2872 assert(sector->getId() == MAPSECTOR_SERVER);
2874 if(sector->differs_from_disk || only_changed == false)
2876 saveSectorMeta(sector);
2877 sector_meta_count++;
2879 core::list<MapBlock*> blocks;
2880 sector->getBlocks(blocks);
2881 core::list<MapBlock*>::Iterator j;
2882 for(j=blocks.begin(); j!=blocks.end(); j++)
2884 MapBlock *block = *j;
2888 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2889 || only_changed == false)
2894 /*dstream<<"ServerMap: Written block ("
2895 <<block->getPos().X<<","
2896 <<block->getPos().Y<<","
2897 <<block->getPos().Z<<")"
2904 Only print if something happened or saved whole map
2906 if(only_changed == false || sector_meta_count != 0
2907 || block_count != 0)
2909 dstream<<DTIME<<"ServerMap: Written: "
2910 <<sector_meta_count<<" sector metadata files, "
2911 <<block_count<<" block files"
2912 <<", "<<block_count_all<<" blocks in memory."
2917 void ServerMap::saveMapMeta()
2919 DSTACK(__FUNCTION_NAME);
2921 dstream<<"INFO: ServerMap::saveMapMeta(): "
2925 createDirs(m_savedir);
2927 std::string fullpath = m_savedir + "/map_meta.txt";
2928 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2929 if(os.good() == false)
2931 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2932 <<"could not open"<<fullpath<<std::endl;
2933 throw FileNotGoodException("Cannot open chunk metadata");
2937 params.setU64("seed", m_seed);
2939 params.writeLines(os);
2941 os<<"[end_of_params]\n";
2943 m_map_metadata_changed = false;
2946 void ServerMap::loadMapMeta()
2948 DSTACK(__FUNCTION_NAME);
2950 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2953 std::string fullpath = m_savedir + "/map_meta.txt";
2954 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2955 if(is.good() == false)
2957 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2958 <<"could not open"<<fullpath<<std::endl;
2959 throw FileNotGoodException("Cannot open map metadata");
2967 throw SerializationError
2968 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2970 std::getline(is, line);
2971 std::string trimmedline = trim(line);
2972 if(trimmedline == "[end_of_params]")
2974 params.parseConfigLine(line);
2977 m_seed = params.getU64("seed");
2979 dstream<<"INFO: ServerMap::loadMapMeta(): "
2984 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2986 DSTACK(__FUNCTION_NAME);
2987 // Format used for writing
2988 u8 version = SER_FMT_VER_HIGHEST;
2990 v2s16 pos = sector->getPos();
2991 std::string dir = getSectorDir(pos);
2994 std::string fullpath = dir + "/meta";
2995 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2996 if(o.good() == false)
2997 throw FileNotGoodException("Cannot open sector metafile");
2999 sector->serialize(o, version);
3001 sector->differs_from_disk = false;
3004 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3006 DSTACK(__FUNCTION_NAME);
3008 v2s16 p2d = getSectorPos(sectordir);
3010 ServerMapSector *sector = NULL;
3012 std::string fullpath = sectordir + "/meta";
3013 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3014 if(is.good() == false)
3016 // If the directory exists anyway, it probably is in some old
3017 // format. Just go ahead and create the sector.
3018 if(fs::PathExists(sectordir))
3020 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3021 <<fullpath<<" doesn't exist but directory does."
3022 <<" Continuing with a sector with no metadata."
3024 sector = new ServerMapSector(this, p2d);
3025 m_sectors.insert(p2d, sector);
3029 throw FileNotGoodException("Cannot open sector metafile");
3034 sector = ServerMapSector::deSerialize
3035 (is, this, p2d, m_sectors);
3037 saveSectorMeta(sector);
3040 sector->differs_from_disk = false;
3045 bool ServerMap::loadSectorMeta(v2s16 p2d)
3047 DSTACK(__FUNCTION_NAME);
3049 MapSector *sector = NULL;
3051 // The directory layout we're going to load from.
3052 // 1 - original sectors/xxxxzzzz/
3053 // 2 - new sectors2/xxx/zzz/
3054 // If we load from anything but the latest structure, we will
3055 // immediately save to the new one, and remove the old.
3057 std::string sectordir1 = getSectorDir(p2d, 1);
3058 std::string sectordir;
3059 if(fs::PathExists(sectordir1))
3061 sectordir = sectordir1;
3066 sectordir = getSectorDir(p2d, 2);
3070 sector = loadSectorMeta(sectordir, loadlayout != 2);
3072 catch(InvalidFilenameException &e)
3076 catch(FileNotGoodException &e)
3080 catch(std::exception &e)
3089 bool ServerMap::loadSectorFull(v2s16 p2d)
3091 DSTACK(__FUNCTION_NAME);
3093 MapSector *sector = NULL;
3095 // The directory layout we're going to load from.
3096 // 1 - original sectors/xxxxzzzz/
3097 // 2 - new sectors2/xxx/zzz/
3098 // If we load from anything but the latest structure, we will
3099 // immediately save to the new one, and remove the old.
3101 std::string sectordir1 = getSectorDir(p2d, 1);
3102 std::string sectordir;
3103 if(fs::PathExists(sectordir1))
3105 sectordir = sectordir1;
3110 sectordir = getSectorDir(p2d, 2);
3114 sector = loadSectorMeta(sectordir, loadlayout != 2);
3116 catch(InvalidFilenameException &e)
3120 catch(FileNotGoodException &e)
3124 catch(std::exception &e)
3132 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3134 std::vector<fs::DirListNode>::iterator i2;
3135 for(i2=list2.begin(); i2!=list2.end(); i2++)
3141 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3143 catch(InvalidFilenameException &e)
3145 // This catches unknown crap in directory
3151 dstream<<"Sector converted to new layout - deleting "<<
3152 sectordir1<<std::endl;
3153 fs::RecursiveDelete(sectordir1);
3160 void ServerMap::saveBlock(MapBlock *block)
3162 DSTACK(__FUNCTION_NAME);
3164 Dummy blocks are not written
3166 if(block->isDummy())
3168 /*v3s16 p = block->getPos();
3169 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3170 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3174 // Format used for writing
3175 u8 version = SER_FMT_VER_HIGHEST;
3177 v3s16 p3d = block->getPos();
3179 v2s16 p2d(p3d.X, p3d.Z);
3180 std::string sectordir = getSectorDir(p2d);
3182 createDirs(sectordir);
3184 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3185 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3186 if(o.good() == false)
3187 throw FileNotGoodException("Cannot open block data");
3190 [0] u8 serialization version
3193 o.write((char*)&version, 1);
3196 block->serialize(o, version);
3198 // Write extra data stored on disk
3199 block->serializeDiskExtra(o, version);
3201 // We just wrote it to the disk so clear modified flag
3202 block->resetModified();
3205 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3207 DSTACK(__FUNCTION_NAME);
3209 std::string fullpath = sectordir+"/"+blockfile;
3212 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3213 if(is.good() == false)
3214 throw FileNotGoodException("Cannot open block file");
3216 v3s16 p3d = getBlockPos(sectordir, blockfile);
3217 v2s16 p2d(p3d.X, p3d.Z);
3219 assert(sector->getPos() == p2d);
3221 u8 version = SER_FMT_VER_INVALID;
3222 is.read((char*)&version, 1);
3225 throw SerializationError("ServerMap::loadBlock(): Failed"
3226 " to read MapBlock version");
3228 /*u32 block_size = MapBlock::serializedLength(version);
3229 SharedBuffer<u8> data(block_size);
3230 is.read((char*)*data, block_size);*/
3232 // This will always return a sector because we're the server
3233 //MapSector *sector = emergeSector(p2d);
3235 MapBlock *block = NULL;
3236 bool created_new = false;
3237 block = sector->getBlockNoCreateNoEx(p3d.Y);
3240 block = sector->createBlankBlockNoInsert(p3d.Y);
3245 block->deSerialize(is, version);
3247 // Read extra data stored on disk
3248 block->deSerializeDiskExtra(is, version);
3250 // If it's a new block, insert it to the map
3252 sector->insertBlock(block);
3255 Save blocks loaded in old format in new format
3258 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3263 // We just loaded it from the disk, so it's up-to-date.
3264 block->resetModified();
3267 catch(SerializationError &e)
3269 dstream<<"WARNING: Invalid block data on disk "
3270 <<"fullpath="<<fullpath
3271 <<" (SerializationError). "
3272 <<"what()="<<e.what()
3274 //" Ignoring. A new one will be generated.
3277 // TODO: Backup file; name is in fullpath.
3281 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3283 DSTACK(__FUNCTION_NAME);
3285 v2s16 p2d(blockpos.X, blockpos.Z);
3287 // The directory layout we're going to load from.
3288 // 1 - original sectors/xxxxzzzz/
3289 // 2 - new sectors2/xxx/zzz/
3290 // If we load from anything but the latest structure, we will
3291 // immediately save to the new one, and remove the old.
3293 std::string sectordir1 = getSectorDir(p2d, 1);
3294 std::string sectordir;
3295 if(fs::PathExists(sectordir1))
3297 sectordir = sectordir1;
3302 sectordir = getSectorDir(p2d, 2);
3306 Make sure sector is loaded
3308 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3312 sector = loadSectorMeta(sectordir, loadlayout != 2);
3314 catch(InvalidFilenameException &e)
3318 catch(FileNotGoodException &e)
3322 catch(std::exception &e)
3329 Make sure file exists
3332 std::string blockfilename = getBlockFilename(blockpos);
3333 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3339 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3340 return getBlockNoCreateNoEx(blockpos);
3343 void ServerMap::PrintInfo(std::ostream &out)
3354 ClientMap::ClientMap(
3356 MapDrawControl &control,
3357 scene::ISceneNode* parent,
3358 scene::ISceneManager* mgr,
3362 scene::ISceneNode(parent, mgr, id),
3365 m_camera_position(0,0,0),
3366 m_camera_direction(0,0,1)
3368 m_camera_mutex.Init();
3369 assert(m_camera_mutex.IsInitialized());
3371 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3372 BS*1000000,BS*1000000,BS*1000000);
3375 ClientMap::~ClientMap()
3377 /*JMutexAutoLock lock(mesh_mutex);
3386 MapSector * ClientMap::emergeSector(v2s16 p2d)
3388 DSTACK(__FUNCTION_NAME);
3389 // Check that it doesn't exist already
3391 return getSectorNoGenerate(p2d);
3393 catch(InvalidPositionException &e)
3398 ClientMapSector *sector = new ClientMapSector(this, p2d);
3401 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3402 m_sectors.insert(p2d, sector);
3409 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3411 DSTACK(__FUNCTION_NAME);
3412 ClientMapSector *sector = NULL;
3414 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3416 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3420 sector = (ClientMapSector*)n->getValue();
3421 assert(sector->getId() == MAPSECTOR_CLIENT);
3425 sector = new ClientMapSector(this, p2d);
3427 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3428 m_sectors.insert(p2d, sector);
3432 sector->deSerialize(is);
3436 void ClientMap::OnRegisterSceneNode()
3440 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3441 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3444 ISceneNode::OnRegisterSceneNode();
3447 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3449 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3450 DSTACK(__FUNCTION_NAME);
3452 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3455 This is called two times per frame, reset on the non-transparent one
3457 if(pass == scene::ESNRP_SOLID)
3459 m_last_drawn_sectors.clear();
3463 Get time for measuring timeout.
3465 Measuring time is very useful for long delays when the
3466 machine is swapping a lot.
3468 int time1 = time(0);
3470 //u32 daynight_ratio = m_client->getDayNightRatio();
3472 m_camera_mutex.Lock();
3473 v3f camera_position = m_camera_position;
3474 v3f camera_direction = m_camera_direction;
3475 m_camera_mutex.Unlock();
3478 Get all blocks and draw all visible ones
3481 v3s16 cam_pos_nodes(
3482 camera_position.X / BS,
3483 camera_position.Y / BS,
3484 camera_position.Z / BS);
3486 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3488 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3489 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3491 // Take a fair amount as we will be dropping more out later
3493 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3494 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3495 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3497 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3498 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3499 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3501 u32 vertex_count = 0;
3503 // For limiting number of mesh updates per frame
3504 u32 mesh_update_count = 0;
3506 u32 blocks_would_have_drawn = 0;
3507 u32 blocks_drawn = 0;
3509 int timecheck_counter = 0;
3510 core::map<v2s16, MapSector*>::Iterator si;
3511 si = m_sectors.getIterator();
3512 for(; si.atEnd() == false; si++)
3515 timecheck_counter++;
3516 if(timecheck_counter > 50)
3518 timecheck_counter = 0;
3519 int time2 = time(0);
3520 if(time2 > time1 + 4)
3522 dstream<<"ClientMap::renderMap(): "
3523 "Rendering takes ages, returning."
3530 MapSector *sector = si.getNode()->getValue();
3531 v2s16 sp = sector->getPos();
3533 if(m_control.range_all == false)
3535 if(sp.X < p_blocks_min.X
3536 || sp.X > p_blocks_max.X
3537 || sp.Y < p_blocks_min.Z
3538 || sp.Y > p_blocks_max.Z)
3542 core::list< MapBlock * > sectorblocks;
3543 sector->getBlocks(sectorblocks);
3549 u32 sector_blocks_drawn = 0;
3551 core::list< MapBlock * >::Iterator i;
3552 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3554 MapBlock *block = *i;
3557 Compare block position to camera position, skip
3558 if not seen on display
3561 float range = 100000 * BS;
3562 if(m_control.range_all == false)
3563 range = m_control.wanted_range * BS;
3566 if(isBlockInSight(block->getPos(), camera_position,
3567 camera_direction, range, &d) == false)
3572 // Okay, this block will be drawn. Reset usage timer.
3573 block->resetUsageTimer();
3575 // This is ugly (spherical distance limit?)
3576 /*if(m_control.range_all == false &&
3577 d - 0.5*BS*MAP_BLOCKSIZE > range)
3582 Update expired mesh (used for day/night change)
3584 It doesn't work exactly like it should now with the
3585 tasked mesh update but whatever.
3588 bool mesh_expired = false;
3591 JMutexAutoLock lock(block->mesh_mutex);
3593 mesh_expired = block->getMeshExpired();
3595 // Mesh has not been expired and there is no mesh:
3596 // block has no content
3597 if(block->mesh == NULL && mesh_expired == false)
3601 f32 faraway = BS*50;
3602 //f32 faraway = m_control.wanted_range * BS;
3605 This has to be done with the mesh_mutex unlocked
3607 // Pretty random but this should work somewhat nicely
3608 if(mesh_expired && (
3609 (mesh_update_count < 3
3610 && (d < faraway || mesh_update_count < 2)
3613 (m_control.range_all && mesh_update_count < 20)
3616 /*if(mesh_expired && mesh_update_count < 6
3617 && (d < faraway || mesh_update_count < 3))*/
3619 mesh_update_count++;
3621 // Mesh has been expired: generate new mesh
3622 //block->updateMesh(daynight_ratio);
3623 m_client->addUpdateMeshTask(block->getPos());
3625 mesh_expired = false;
3630 Draw the faces of the block
3633 JMutexAutoLock lock(block->mesh_mutex);
3635 scene::SMesh *mesh = block->mesh;
3640 blocks_would_have_drawn++;
3641 if(blocks_drawn >= m_control.wanted_max_blocks
3642 && m_control.range_all == false
3643 && d > m_control.wanted_min_range * BS)
3647 sector_blocks_drawn++;
3649 u32 c = mesh->getMeshBufferCount();
3651 for(u32 i=0; i<c; i++)
3653 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3654 const video::SMaterial& material = buf->getMaterial();
3655 video::IMaterialRenderer* rnd =
3656 driver->getMaterialRenderer(material.MaterialType);
3657 bool transparent = (rnd && rnd->isTransparent());
3658 // Render transparent on transparent pass and likewise.
3659 if(transparent == is_transparent_pass)
3662 This *shouldn't* hurt too much because Irrlicht
3663 doesn't change opengl textures if the old
3664 material is set again.
3666 driver->setMaterial(buf->getMaterial());
3667 driver->drawMeshBuffer(buf);
3668 vertex_count += buf->getVertexCount();
3672 } // foreach sectorblocks
3674 if(sector_blocks_drawn != 0)
3676 m_last_drawn_sectors[sp] = true;
3680 m_control.blocks_drawn = blocks_drawn;
3681 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3683 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3684 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3687 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3688 core::map<v3s16, MapBlock*> *affected_blocks)
3690 bool changed = false;
3692 Add it to all blocks touching it
3695 v3s16(0,0,0), // this
3696 v3s16(0,0,1), // back
3697 v3s16(0,1,0), // top
3698 v3s16(1,0,0), // right
3699 v3s16(0,0,-1), // front
3700 v3s16(0,-1,0), // bottom
3701 v3s16(-1,0,0), // left
3703 for(u16 i=0; i<7; i++)
3705 v3s16 p2 = p + dirs[i];
3706 // Block position of neighbor (or requested) node
3707 v3s16 blockpos = getNodeBlockPos(p2);
3708 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3709 if(blockref == NULL)
3711 // Relative position of requested node
3712 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3713 if(blockref->setTempMod(relpos, mod))
3718 if(changed && affected_blocks!=NULL)
3720 for(u16 i=0; i<7; i++)
3722 v3s16 p2 = p + dirs[i];
3723 // Block position of neighbor (or requested) node
3724 v3s16 blockpos = getNodeBlockPos(p2);
3725 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3726 if(blockref == NULL)
3728 affected_blocks->insert(blockpos, blockref);
3734 bool ClientMap::clearTempMod(v3s16 p,
3735 core::map<v3s16, MapBlock*> *affected_blocks)
3737 bool changed = false;
3739 v3s16(0,0,0), // this
3740 v3s16(0,0,1), // back
3741 v3s16(0,1,0), // top
3742 v3s16(1,0,0), // right
3743 v3s16(0,0,-1), // front
3744 v3s16(0,-1,0), // bottom
3745 v3s16(-1,0,0), // left
3747 for(u16 i=0; i<7; i++)
3749 v3s16 p2 = p + dirs[i];
3750 // Block position of neighbor (or requested) node
3751 v3s16 blockpos = getNodeBlockPos(p2);
3752 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3753 if(blockref == NULL)
3755 // Relative position of requested node
3756 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3757 if(blockref->clearTempMod(relpos))
3762 if(changed && affected_blocks!=NULL)
3764 for(u16 i=0; i<7; i++)
3766 v3s16 p2 = p + dirs[i];
3767 // Block position of neighbor (or requested) node
3768 v3s16 blockpos = getNodeBlockPos(p2);
3769 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3770 if(blockref == NULL)
3772 affected_blocks->insert(blockpos, blockref);
3778 void ClientMap::expireMeshes(bool only_daynight_diffed)
3780 TimeTaker timer("expireMeshes()");
3782 core::map<v2s16, MapSector*>::Iterator si;
3783 si = m_sectors.getIterator();
3784 for(; si.atEnd() == false; si++)
3786 MapSector *sector = si.getNode()->getValue();
3788 core::list< MapBlock * > sectorblocks;
3789 sector->getBlocks(sectorblocks);
3791 core::list< MapBlock * >::Iterator i;
3792 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3794 MapBlock *block = *i;
3796 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3802 JMutexAutoLock lock(block->mesh_mutex);
3803 if(block->mesh != NULL)
3805 /*block->mesh->drop();
3806 block->mesh = NULL;*/
3807 block->setMeshExpired(true);
3814 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3816 assert(mapType() == MAPTYPE_CLIENT);
3819 v3s16 p = blockpos + v3s16(0,0,0);
3820 MapBlock *b = getBlockNoCreate(p);
3821 b->updateMesh(daynight_ratio);
3822 //b->setMeshExpired(true);
3824 catch(InvalidPositionException &e){}
3827 v3s16 p = blockpos + v3s16(-1,0,0);
3828 MapBlock *b = getBlockNoCreate(p);
3829 b->updateMesh(daynight_ratio);
3830 //b->setMeshExpired(true);
3832 catch(InvalidPositionException &e){}
3834 v3s16 p = blockpos + v3s16(0,-1,0);
3835 MapBlock *b = getBlockNoCreate(p);
3836 b->updateMesh(daynight_ratio);
3837 //b->setMeshExpired(true);
3839 catch(InvalidPositionException &e){}
3841 v3s16 p = blockpos + v3s16(0,0,-1);
3842 MapBlock *b = getBlockNoCreate(p);
3843 b->updateMesh(daynight_ratio);
3844 //b->setMeshExpired(true);
3846 catch(InvalidPositionException &e){}
3851 Update mesh of block in which the node is, and if the node is at the
3852 leading edge, update the appropriate leading blocks too.
3854 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3862 v3s16 blockposes[4];
3863 for(u32 i=0; i<4; i++)
3865 v3s16 np = nodepos + dirs[i];
3866 blockposes[i] = getNodeBlockPos(np);
3867 // Don't update mesh of block if it has been done already
3868 bool already_updated = false;
3869 for(u32 j=0; j<i; j++)
3871 if(blockposes[j] == blockposes[i])
3873 already_updated = true;
3880 MapBlock *b = getBlockNoCreate(blockposes[i]);
3881 b->updateMesh(daynight_ratio);
3886 void ClientMap::PrintInfo(std::ostream &out)
3897 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3902 MapVoxelManipulator::~MapVoxelManipulator()
3904 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3908 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3910 TimeTaker timer1("emerge", &emerge_time);
3912 // Units of these are MapBlocks
3913 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3914 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3916 VoxelArea block_area_nodes
3917 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3919 addArea(block_area_nodes);
3921 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3922 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3923 for(s32 x=p_min.X; x<=p_max.X; x++)
3926 core::map<v3s16, bool>::Node *n;
3927 n = m_loaded_blocks.find(p);
3931 bool block_data_inexistent = false;
3934 TimeTaker timer1("emerge load", &emerge_load_time);
3936 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3937 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3940 dstream<<std::endl;*/
3942 MapBlock *block = m_map->getBlockNoCreate(p);
3943 if(block->isDummy())
3944 block_data_inexistent = true;
3946 block->copyTo(*this);
3948 catch(InvalidPositionException &e)
3950 block_data_inexistent = true;
3953 if(block_data_inexistent)
3955 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3956 // Fill with VOXELFLAG_INEXISTENT
3957 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3958 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3960 s32 i = m_area.index(a.MinEdge.X,y,z);
3961 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3965 m_loaded_blocks.insert(p, !block_data_inexistent);
3968 //dstream<<"emerge done"<<std::endl;
3972 SUGG: Add an option to only update eg. water and air nodes.
3973 This will make it interfere less with important stuff if
3976 void MapVoxelManipulator::blitBack
3977 (core::map<v3s16, MapBlock*> & modified_blocks)
3979 if(m_area.getExtent() == v3s16(0,0,0))
3982 //TimeTaker timer1("blitBack");
3984 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3985 <<m_loaded_blocks.size()<<std::endl;*/
3988 Initialize block cache
3990 v3s16 blockpos_last;
3991 MapBlock *block = NULL;
3992 bool block_checked_in_modified = false;
3994 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3995 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3996 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4000 u8 f = m_flags[m_area.index(p)];
4001 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4004 MapNode &n = m_data[m_area.index(p)];
4006 v3s16 blockpos = getNodeBlockPos(p);
4011 if(block == NULL || blockpos != blockpos_last){
4012 block = m_map->getBlockNoCreate(blockpos);
4013 blockpos_last = blockpos;
4014 block_checked_in_modified = false;
4017 // Calculate relative position in block
4018 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4020 // Don't continue if nothing has changed here
4021 if(block->getNode(relpos) == n)
4024 //m_map->setNode(m_area.MinEdge + p, n);
4025 block->setNode(relpos, n);
4028 Make sure block is in modified_blocks
4030 if(block_checked_in_modified == false)
4032 modified_blocks[blockpos] = block;
4033 block_checked_in_modified = true;
4036 catch(InvalidPositionException &e)
4042 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4043 MapVoxelManipulator(map),
4044 m_create_area(false)
4048 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4052 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4054 // Just create the area so that it can be pointed to
4055 VoxelManipulator::emerge(a, caller_id);
4058 void ManualMapVoxelManipulator::initialEmerge(
4059 v3s16 blockpos_min, v3s16 blockpos_max)
4061 TimeTaker timer1("initialEmerge", &emerge_time);
4063 // Units of these are MapBlocks
4064 v3s16 p_min = blockpos_min;
4065 v3s16 p_max = blockpos_max;
4067 VoxelArea block_area_nodes
4068 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4070 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4073 dstream<<"initialEmerge: area: ";
4074 block_area_nodes.print(dstream);
4075 dstream<<" ("<<size_MB<<"MB)";
4079 addArea(block_area_nodes);
4081 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4082 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4083 for(s32 x=p_min.X; x<=p_max.X; x++)
4086 core::map<v3s16, bool>::Node *n;
4087 n = m_loaded_blocks.find(p);
4091 bool block_data_inexistent = false;
4094 TimeTaker timer1("emerge load", &emerge_load_time);
4096 MapBlock *block = m_map->getBlockNoCreate(p);
4097 if(block->isDummy())
4098 block_data_inexistent = true;
4100 block->copyTo(*this);
4102 catch(InvalidPositionException &e)
4104 block_data_inexistent = true;
4107 if(block_data_inexistent)
4110 Mark area inexistent
4112 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4113 // Fill with VOXELFLAG_INEXISTENT
4114 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4115 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4117 s32 i = m_area.index(a.MinEdge.X,y,z);
4118 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4122 m_loaded_blocks.insert(p, !block_data_inexistent);
4126 void ManualMapVoxelManipulator::blitBackAll(
4127 core::map<v3s16, MapBlock*> * modified_blocks)
4129 if(m_area.getExtent() == v3s16(0,0,0))
4133 Copy data of all blocks
4135 for(core::map<v3s16, bool>::Iterator
4136 i = m_loaded_blocks.getIterator();
4137 i.atEnd() == false; i++)
4139 v3s16 p = i.getNode()->getKey();
4140 bool existed = i.getNode()->getValue();
4141 if(existed == false)
4143 // The Great Bug was found using this
4144 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4145 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4149 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4152 dstream<<"WARNING: "<<__FUNCTION_NAME
4153 <<": got NULL block "
4154 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4159 block->copyFrom(*this);
4162 modified_blocks->insert(p, block);