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) || n2.d == CONTENT_AIR)
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<10; i++)
1258 v3s16 p2 = p + dirs[i];
1260 MapNode n2 = getNode(p2);
1261 if(content_liquid(n2.d) || n2.d == CONTENT_AIR)
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
1543 NEIGHBOR_SAME_LEVEL,
1546 struct NodeNeighbor {
1552 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1554 DSTACK(__FUNCTION_NAME);
1555 //TimeTaker timer("transformLiquids()");
1558 u32 initial_size = m_transforming_liquid.size();
1560 /*if(initial_size != 0)
1561 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1563 while(m_transforming_liquid.size() != 0)
1566 Get a queued transforming liquid node
1568 v3s16 p0 = m_transforming_liquid.pop_front();
1570 MapNode n0 = getNodeNoEx(p0);
1573 Collect information about current node
1575 s8 liquid_level = -1;
1577 LiquidType liquid_type = content_features(n0.d).liquid_type;
1578 switch (liquid_type) {
1581 liquid_kind = content_features(n0.d).liquid_alternative_flowing;
1583 case LIQUID_FLOWING:
1584 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1588 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1589 // continue with the next node.
1590 if (n0.d != CONTENT_AIR)
1592 liquid_kind = CONTENT_AIR;
1597 Collect information about the environment
1600 v3s16( 0, 1, 0), // top
1601 v3s16( 0,-1, 0), // bottom
1602 v3s16( 1, 0, 0), // right
1603 v3s16(-1, 0, 0), // left
1604 v3s16( 0, 0, 1), // back
1605 v3s16( 0, 0,-1) // front
1607 NodeNeighbor sources[6]; // surrounding sources
1608 int num_sources = 0;
1609 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1611 NodeNeighbor airs[6]; // surrounding air
1613 NodeNeighbor neutrals[6]; // nodes that are solid, another kind of liquid
1614 int num_neutrals = 0;
1615 bool flowing_down = false;
1616 for (u16 i = 0; i < 6; i++) {
1617 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1620 nt = NEIGHBOR_UPPER;
1623 nt = NEIGHBOR_LOWER;
1626 v3s16 npos = p0 + dirs[i];
1627 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1628 switch (content_features(nb.n.d).liquid_type) {
1630 if (nb.n.d == CONTENT_AIR) {
1631 airs[num_airs++] = nb;
1632 // if the current nodes happens to be a flowing node, it will start to flow down here.
1633 if (nb.t == NEIGHBOR_LOWER)
1634 flowing_down = true;
1636 neutrals[num_neutrals++] = nb;
1640 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1641 if (liquid_kind == CONTENT_AIR)
1642 liquid_kind = content_features(nb.n.d).liquid_alternative_flowing;
1643 if (content_features(nb.n.d).liquid_alternative_flowing !=liquid_kind) {
1644 neutrals[num_neutrals++] = nb;
1646 sources[num_sources++] = nb;
1649 case LIQUID_FLOWING:
1650 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1651 // (while ignoring flowing liquids at the lowest level, which cannot flow into this node)
1652 if (liquid_kind == CONTENT_AIR && ((nb.n.param2 & LIQUID_LEVEL_MASK) > 0))
1653 liquid_kind = content_features(nb.n.d).liquid_alternative_flowing;
1654 if (content_features(nb.n.d).liquid_alternative_flowing != liquid_kind) {
1655 neutrals[num_neutrals++] = nb;
1657 // order flowing neighbors by liquid level descending
1659 while (insert_at < num_flows && ((flows[insert_at].n.param2 & LIQUID_LEVEL_MASK) >
1660 (nb.n.param2 & LIQUID_LEVEL_MASK))) {
1663 flows[insert_at] = nb;
1665 if (nb.t == NEIGHBOR_LOWER)
1666 flowing_down = true;
1673 decide on the type (and possibly level) of the current node
1675 u8 new_node_content;
1676 s8 new_node_level = -1;
1677 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1678 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1679 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1680 // it's perfectly safe to use liquid_kind here to determine the new node content.
1681 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1682 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1683 // liquid_kind is set properly, see above
1684 new_node_content = liquid_kind;
1687 // no surrounding sources, so get the maximum level that can flow into this node
1688 for (u16 i = 0; i < num_flows; i++) {
1689 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1690 switch (flows[i].t) {
1691 case NEIGHBOR_UPPER:
1692 if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
1694 if (nb_liquid_level + WATER_DROP_BOOST < 7)
1695 new_node_level = nb_liquid_level + WATER_DROP_BOOST;
1698 case NEIGHBOR_LOWER:
1700 case NEIGHBOR_SAME_LEVEL:
1701 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1702 nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) {
1703 new_node_level = nb_liquid_level - 1;
1708 if (new_node_level >= 0)
1709 new_node_content = liquid_kind;
1711 new_node_content = CONTENT_AIR;
1715 check if anything has changed. if not, just continue with the next node.
1717 if (new_node_content == n0.d && (content_features(n0.d).liquid_type != LIQUID_FLOWING ||
1718 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level) &&
1719 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1724 update the current node
1726 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1727 n0.d = new_node_content;
1728 if (content_features(n0.d).liquid_type == LIQUID_FLOWING) {
1729 // set level to last 3 bits, flowing down bit to 4th bit
1730 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1735 v3s16 blockpos = getNodeBlockPos(p0);
1736 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1738 modified_blocks.insert(blockpos, block);
1740 switch (content_features(n0.d).liquid_type) {
1742 // make sure source flows into all neighboring nodes
1743 for (u16 i = 0; i < num_flows; i++)
1744 if (flows[i].t != NEIGHBOR_UPPER)
1745 m_transforming_liquid.push_back(flows[i].p);
1746 for (u16 i = 0; i < num_airs; i++)
1747 if (airs[i].t != NEIGHBOR_UPPER)
1748 m_transforming_liquid.push_back(airs[i].p);
1751 // this flow has turned to air; neighboring flows might need to do the same
1752 for (u16 i = 0; i < num_flows; i++)
1753 m_transforming_liquid.push_back(flows[i].p);
1755 case LIQUID_FLOWING:
1756 for (u16 i = 0; i < num_flows; i++) {
1757 u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1758 // liquid_level is still the ORIGINAL level of this node.
1759 if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
1761 m_transforming_liquid.push_back(flows[i].p);
1763 for (u16 i = 0; i < num_airs; i++) {
1764 if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
1765 m_transforming_liquid.push_back(airs[i].p);
1771 //if(loopcount >= 100000)
1772 if(loopcount >= initial_size * 1) {
1776 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1779 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1781 v3s16 blockpos = getNodeBlockPos(p);
1782 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1783 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1786 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1790 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1794 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1796 v3s16 blockpos = getNodeBlockPos(p);
1797 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1798 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1801 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1805 block->m_node_metadata.set(p_rel, meta);
1808 void Map::removeNodeMetadata(v3s16 p)
1810 v3s16 blockpos = getNodeBlockPos(p);
1811 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1812 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1815 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1819 block->m_node_metadata.remove(p_rel);
1822 void Map::nodeMetadataStep(float dtime,
1823 core::map<v3s16, MapBlock*> &changed_blocks)
1827 Currently there is no way to ensure that all the necessary
1828 blocks are loaded when this is run. (They might get unloaded)
1829 NOTE: ^- Actually, that might not be so. In a quick test it
1830 reloaded a block with a furnace when I walked back to it from
1833 core::map<v2s16, MapSector*>::Iterator si;
1834 si = m_sectors.getIterator();
1835 for(; si.atEnd() == false; si++)
1837 MapSector *sector = si.getNode()->getValue();
1838 core::list< MapBlock * > sectorblocks;
1839 sector->getBlocks(sectorblocks);
1840 core::list< MapBlock * >::Iterator i;
1841 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1843 MapBlock *block = *i;
1844 bool changed = block->m_node_metadata.step(dtime);
1846 changed_blocks[block->getPos()] = block;
1855 ServerMap::ServerMap(std::string savedir):
1858 m_map_metadata_changed(true)
1860 dstream<<__FUNCTION_NAME<<std::endl;
1862 //m_chunksize = 8; // Takes a few seconds
1864 m_seed = (((u64)(myrand()%0xffff)<<0)
1865 + ((u64)(myrand()%0xffff)<<16)
1866 + ((u64)(myrand()%0xffff)<<32)
1867 + ((u64)(myrand()%0xffff)<<48));
1870 Experimental and debug stuff
1877 Try to load map; if not found, create a new one.
1880 m_savedir = savedir;
1881 m_map_saving_enabled = false;
1885 // If directory exists, check contents and load if possible
1886 if(fs::PathExists(m_savedir))
1888 // If directory is empty, it is safe to save into it.
1889 if(fs::GetDirListing(m_savedir).size() == 0)
1891 dstream<<DTIME<<"Server: Empty save directory is valid."
1893 m_map_saving_enabled = true;
1898 // Load map metadata (seed, chunksize)
1901 catch(FileNotGoodException &e){
1902 dstream<<DTIME<<"WARNING: Could not load map metadata"
1903 //<<" Disabling chunk-based generator."
1909 // Load chunk metadata
1912 catch(FileNotGoodException &e){
1913 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1914 <<" Disabling chunk-based generator."
1919 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1920 "metadata and sector (0,0) from "<<savedir<<
1921 ", assuming valid save directory."
1924 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1925 <<"and chunk metadata from "<<savedir
1926 <<", assuming valid save directory."
1929 m_map_saving_enabled = true;
1930 // Map loaded, not creating new one
1934 // If directory doesn't exist, it is safe to save to it
1936 m_map_saving_enabled = true;
1939 catch(std::exception &e)
1941 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1942 <<", exception: "<<e.what()<<std::endl;
1943 dstream<<"Please remove the map or fix it."<<std::endl;
1944 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1947 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1949 // Create zero sector
1950 emergeSector(v2s16(0,0));
1952 // Initially write whole map
1956 ServerMap::~ServerMap()
1958 dstream<<__FUNCTION_NAME<<std::endl;
1962 if(m_map_saving_enabled)
1964 // Save only changed parts
1966 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1970 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1973 catch(std::exception &e)
1975 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1976 <<", exception: "<<e.what()<<std::endl;
1983 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1984 for(; i.atEnd() == false; i++)
1986 MapChunk *chunk = i.getNode()->getValue();
1992 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
1994 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
1995 <<blockpos.Z<<")"<<std::endl;*/
1997 data->no_op = false;
1998 data->seed = m_seed;
1999 data->blockpos = blockpos;
2002 Create the whole area of this and the neighboring blocks
2005 //TimeTaker timer("initBlockMake() create area");
2007 for(s16 x=-1; x<=1; x++)
2008 for(s16 z=-1; z<=1; z++)
2010 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2011 // Sector metadata is loaded from disk if not already loaded.
2012 ServerMapSector *sector = createSector(sectorpos);
2015 for(s16 y=-1; y<=1; y++)
2017 MapBlock *block = createBlock(blockpos);
2019 // Lighting won't be calculated
2020 block->setLightingExpired(true);
2021 // Lighting will be calculated
2022 //block->setLightingExpired(false);
2025 Block gets sunlight if this is true.
2027 This should be set to true when the top side of a block
2028 is completely exposed to the sky.
2030 block->setIsUnderground(false);
2036 Now we have a big empty area.
2038 Make a ManualMapVoxelManipulator that contains this and the
2042 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2043 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2045 data->vmanip = new ManualMapVoxelManipulator(this);
2046 //data->vmanip->setMap(this);
2050 //TimeTaker timer("initBlockMake() initialEmerge");
2051 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2054 // Data is ready now.
2057 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2058 core::map<v3s16, MapBlock*> &changed_blocks)
2060 v3s16 blockpos = data->blockpos;
2061 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2062 <<blockpos.Z<<")"<<std::endl;*/
2066 dstream<<"finishBlockMake(): no-op"<<std::endl;
2070 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2072 /*dstream<<"Resulting vmanip:"<<std::endl;
2073 data->vmanip.print(dstream);*/
2076 Blit generated stuff to map
2077 NOTE: blitBackAll adds nearly everything to changed_blocks
2081 //TimeTaker timer("finishBlockMake() blitBackAll");
2082 data->vmanip->blitBackAll(&changed_blocks);
2085 if(enable_mapgen_debug_info)
2086 dstream<<"finishBlockMake: changed_blocks.size()="
2087 <<changed_blocks.size()<<std::endl;
2090 Copy transforming liquid information
2092 while(data->transforming_liquid.size() > 0)
2094 v3s16 p = data->transforming_liquid.pop_front();
2095 m_transforming_liquid.push_back(p);
2101 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2105 Set is_underground flag for lighting with sunlight
2108 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2111 Add sunlight to central block.
2112 This makes in-dark-spawning monsters to not flood the whole thing.
2113 Do not spread the light, though.
2115 /*core::map<v3s16, bool> light_sources;
2116 bool black_air_left = false;
2117 block->propagateSunlight(light_sources, true, &black_air_left);*/
2120 NOTE: Lighting and object adding shouldn't really be here, but
2121 lighting is a bit tricky to move properly to makeBlock.
2122 TODO: Do this the right way anyway, that is, move it to makeBlock.
2123 - There needs to be some way for makeBlock to report back if
2124 the lighting update is going further down because of the
2125 new block blocking light
2130 NOTE: This takes ~60ms, TODO: Investigate why
2133 TimeTaker t("finishBlockMake lighting update");
2135 core::map<v3s16, MapBlock*> lighting_update_blocks;
2137 lighting_update_blocks.insert(block->getPos(), block);
2139 // All modified blocks
2140 for(core::map<v3s16, MapBlock*>::Iterator
2141 i = changed_blocks.getIterator();
2142 i.atEnd() == false; i++)
2144 lighting_update_blocks.insert(i.getNode()->getKey(),
2145 i.getNode()->getValue());
2148 updateLighting(lighting_update_blocks, changed_blocks);
2150 if(enable_mapgen_debug_info == false)
2151 t.stop(true); // Hide output
2155 Add random objects to block
2157 mapgen::add_random_objects(block);
2160 Go through changed blocks
2162 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2163 i.atEnd() == false; i++)
2165 MapBlock *block = i.getNode()->getValue();
2168 Update day/night difference cache of the MapBlocks
2170 block->updateDayNightDiff();
2172 Set block as modified
2174 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2178 Set central block as generated
2180 block->setGenerated(true);
2183 Save changed parts of map
2184 NOTE: Will be saved later.
2188 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2189 <<blockpos.Z<<")"<<std::endl;*/
2194 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2196 DSTACKF("%s: p2d=(%d,%d)",
2201 Check if it exists already in memory
2203 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2208 Try to load it from disk (with blocks)
2210 //if(loadSectorFull(p2d) == true)
2213 Try to load metadata from disk
2215 if(loadSectorMeta(p2d) == true)
2217 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2220 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2221 throw InvalidPositionException("");
2227 Do not create over-limit
2229 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2230 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2231 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2232 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2233 throw InvalidPositionException("createSector(): pos. over limit");
2236 Generate blank sector
2239 sector = new ServerMapSector(this, p2d);
2241 // Sector position on map in nodes
2242 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2247 m_sectors.insert(p2d, sector);
2253 This is a quick-hand function for calling makeBlock().
2255 MapBlock * ServerMap::generateBlock(
2257 core::map<v3s16, MapBlock*> &modified_blocks
2260 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2262 /*dstream<<"generateBlock(): "
2263 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2266 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2268 TimeTaker timer("generateBlock");
2270 //MapBlock *block = original_dummy;
2272 v2s16 p2d(p.X, p.Z);
2273 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2276 Do not generate over-limit
2278 if(blockpos_over_limit(p))
2280 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2281 throw InvalidPositionException("generateBlock(): pos. over limit");
2285 Create block make data
2287 mapgen::BlockMakeData data;
2288 initBlockMake(&data, p);
2294 TimeTaker t("mapgen::make_block()");
2295 mapgen::make_block(&data);
2297 if(enable_mapgen_debug_info == false)
2298 t.stop(true); // Hide output
2302 Blit data back on map, update lighting, add mobs and whatever this does
2304 finishBlockMake(&data, modified_blocks);
2309 MapBlock *block = getBlockNoCreateNoEx(p);
2316 bool erroneus_content = false;
2317 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2318 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2319 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2322 MapNode n = block->getNode(p);
2323 if(n.d == CONTENT_IGNORE)
2325 dstream<<"CONTENT_IGNORE at "
2326 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2328 erroneus_content = true;
2332 if(erroneus_content)
2340 Generate a completely empty block
2342 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2343 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2345 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2351 n.d = CONTENT_STONE;
2352 block->setNode(v3s16(x0,y0,z0), n);
2357 if(enable_mapgen_debug_info == false)
2358 timer.stop(true); // Hide output
2363 MapBlock * ServerMap::createBlock(v3s16 p)
2365 DSTACKF("%s: p=(%d,%d,%d)",
2366 __FUNCTION_NAME, p.X, p.Y, p.Z);
2369 Do not create over-limit
2371 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2372 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2373 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2374 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2375 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2376 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2377 throw InvalidPositionException("createBlock(): pos. over limit");
2379 v2s16 p2d(p.X, p.Z);
2382 This will create or load a sector if not found in memory.
2383 If block exists on disk, it will be loaded.
2385 NOTE: On old save formats, this will be slow, as it generates
2386 lighting on blocks for them.
2388 ServerMapSector *sector;
2390 sector = (ServerMapSector*)createSector(p2d);
2391 assert(sector->getId() == MAPSECTOR_SERVER);
2393 catch(InvalidPositionException &e)
2395 dstream<<"createBlock: createSector() failed"<<std::endl;
2399 NOTE: This should not be done, or at least the exception
2400 should not be passed on as std::exception, because it
2401 won't be catched at all.
2403 /*catch(std::exception &e)
2405 dstream<<"createBlock: createSector() failed: "
2406 <<e.what()<<std::endl;
2411 Try to get a block from the sector
2414 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2417 if(block->isDummy())
2422 block = sector->createBlankBlock(block_y);
2426 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2428 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2430 p.X, p.Y, p.Z, allow_generate);
2433 MapBlock *block = getBlockNoCreateNoEx(p);
2439 MapBlock *block = loadBlock(p);
2446 core::map<v3s16, MapBlock*> modified_blocks;
2447 MapBlock *block = generateBlock(p, modified_blocks);
2451 event.type = MEET_OTHER;
2454 // Copy modified_blocks to event
2455 for(core::map<v3s16, MapBlock*>::Iterator
2456 i = modified_blocks.getIterator();
2457 i.atEnd()==false; i++)
2459 event.modified_blocks.insert(i.getNode()->getKey(), false);
2463 dispatchEvent(&event);
2474 Do not generate over-limit
2476 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2477 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2478 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2479 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2480 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2481 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2482 throw InvalidPositionException("emergeBlock(): pos. over limit");
2484 v2s16 p2d(p.X, p.Z);
2487 This will create or load a sector if not found in memory.
2488 If block exists on disk, it will be loaded.
2490 ServerMapSector *sector;
2492 sector = createSector(p2d);
2493 //sector = emergeSector(p2d, changed_blocks);
2495 catch(InvalidPositionException &e)
2497 dstream<<"emergeBlock: createSector() failed: "
2498 <<e.what()<<std::endl;
2499 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2501 <<"You could try to delete it."<<std::endl;
2504 catch(VersionMismatchException &e)
2506 dstream<<"emergeBlock: createSector() failed: "
2507 <<e.what()<<std::endl;
2508 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2510 <<"You could try to delete it."<<std::endl;
2515 Try to get a block from the sector
2518 bool does_not_exist = false;
2519 bool lighting_expired = false;
2520 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2522 // If not found, try loading from disk
2525 block = loadBlock(p);
2531 does_not_exist = true;
2533 else if(block->isDummy() == true)
2535 does_not_exist = true;
2537 else if(block->getLightingExpired())
2539 lighting_expired = true;
2544 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2549 If block was not found on disk and not going to generate a
2550 new one, make sure there is a dummy block in place.
2552 if(only_from_disk && (does_not_exist || lighting_expired))
2554 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2558 // Create dummy block
2559 block = new MapBlock(this, p, true);
2561 // Add block to sector
2562 sector->insertBlock(block);
2568 //dstream<<"Not found on disk, generating."<<std::endl;
2570 //TimeTaker("emergeBlock() generate");
2572 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2575 If the block doesn't exist, generate the block.
2579 block = generateBlock(p, block, sector, changed_blocks,
2580 lighting_invalidated_blocks);
2583 if(lighting_expired)
2585 lighting_invalidated_blocks.insert(p, block);
2590 Initially update sunlight
2593 core::map<v3s16, bool> light_sources;
2594 bool black_air_left = false;
2595 bool bottom_invalid =
2596 block->propagateSunlight(light_sources, true,
2599 // If sunlight didn't reach everywhere and part of block is
2600 // above ground, lighting has to be properly updated
2601 //if(black_air_left && some_part_underground)
2604 lighting_invalidated_blocks[block->getPos()] = block;
2609 lighting_invalidated_blocks[block->getPos()] = block;
2618 s16 ServerMap::findGroundLevel(v2s16 p2d)
2622 Uh, just do something random...
2624 // Find existing map from top to down
2627 v3s16 p(p2d.X, max, p2d.Y);
2628 for(; p.Y>min; p.Y--)
2630 MapNode n = getNodeNoEx(p);
2631 if(n.d != CONTENT_IGNORE)
2636 // If this node is not air, go to plan b
2637 if(getNodeNoEx(p).d != CONTENT_AIR)
2639 // Search existing walkable and return it
2640 for(; p.Y>min; p.Y--)
2642 MapNode n = getNodeNoEx(p);
2643 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2652 Determine from map generator noise functions
2655 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2658 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2659 //return (s16)level;
2662 void ServerMap::createDirs(std::string path)
2664 if(fs::CreateAllDirs(path) == false)
2666 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2667 <<"\""<<path<<"\""<<std::endl;
2668 throw BaseException("ServerMap failed to create directory");
2672 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2678 snprintf(cc, 9, "%.4x%.4x",
2679 (unsigned int)pos.X&0xffff,
2680 (unsigned int)pos.Y&0xffff);
2682 return m_savedir + "/sectors/" + cc;
2684 snprintf(cc, 9, "%.3x/%.3x",
2685 (unsigned int)pos.X&0xfff,
2686 (unsigned int)pos.Y&0xfff);
2688 return m_savedir + "/sectors2/" + cc;
2694 v2s16 ServerMap::getSectorPos(std::string dirname)
2698 size_t spos = dirname.rfind('/') + 1;
2699 assert(spos != std::string::npos);
2700 if(dirname.size() - spos == 8)
2703 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2705 else if(dirname.size() - spos == 3)
2708 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2709 // Sign-extend the 12 bit values up to 16 bits...
2710 if(x&0x800) x|=0xF000;
2711 if(y&0x800) y|=0xF000;
2718 v2s16 pos((s16)x, (s16)y);
2722 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2724 v2s16 p2d = getSectorPos(sectordir);
2726 if(blockfile.size() != 4){
2727 throw InvalidFilenameException("Invalid block filename");
2730 int r = sscanf(blockfile.c_str(), "%4x", &y);
2732 throw InvalidFilenameException("Invalid block filename");
2733 return v3s16(p2d.X, y, p2d.Y);
2736 std::string ServerMap::getBlockFilename(v3s16 p)
2739 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2743 void ServerMap::save(bool only_changed)
2745 DSTACK(__FUNCTION_NAME);
2746 if(m_map_saving_enabled == false)
2748 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2752 if(only_changed == false)
2753 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2756 if(only_changed == false || m_map_metadata_changed)
2761 u32 sector_meta_count = 0;
2762 u32 block_count = 0;
2763 u32 block_count_all = 0; // Number of blocks in memory
2765 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2766 for(; i.atEnd() == false; i++)
2768 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2769 assert(sector->getId() == MAPSECTOR_SERVER);
2771 if(sector->differs_from_disk || only_changed == false)
2773 saveSectorMeta(sector);
2774 sector_meta_count++;
2776 core::list<MapBlock*> blocks;
2777 sector->getBlocks(blocks);
2778 core::list<MapBlock*>::Iterator j;
2779 for(j=blocks.begin(); j!=blocks.end(); j++)
2781 MapBlock *block = *j;
2785 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2786 || only_changed == false)
2791 /*dstream<<"ServerMap: Written block ("
2792 <<block->getPos().X<<","
2793 <<block->getPos().Y<<","
2794 <<block->getPos().Z<<")"
2801 Only print if something happened or saved whole map
2803 if(only_changed == false || sector_meta_count != 0
2804 || block_count != 0)
2806 dstream<<DTIME<<"ServerMap: Written: "
2807 <<sector_meta_count<<" sector metadata files, "
2808 <<block_count<<" block files"
2809 <<", "<<block_count_all<<" blocks in memory."
2814 void ServerMap::saveMapMeta()
2816 DSTACK(__FUNCTION_NAME);
2818 dstream<<"INFO: ServerMap::saveMapMeta(): "
2822 createDirs(m_savedir);
2824 std::string fullpath = m_savedir + "/map_meta.txt";
2825 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2826 if(os.good() == false)
2828 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2829 <<"could not open"<<fullpath<<std::endl;
2830 throw FileNotGoodException("Cannot open chunk metadata");
2834 params.setU64("seed", m_seed);
2836 params.writeLines(os);
2838 os<<"[end_of_params]\n";
2840 m_map_metadata_changed = false;
2843 void ServerMap::loadMapMeta()
2845 DSTACK(__FUNCTION_NAME);
2847 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2850 std::string fullpath = m_savedir + "/map_meta.txt";
2851 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2852 if(is.good() == false)
2854 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2855 <<"could not open"<<fullpath<<std::endl;
2856 throw FileNotGoodException("Cannot open map metadata");
2864 throw SerializationError
2865 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2867 std::getline(is, line);
2868 std::string trimmedline = trim(line);
2869 if(trimmedline == "[end_of_params]")
2871 params.parseConfigLine(line);
2874 m_seed = params.getU64("seed");
2876 dstream<<"INFO: ServerMap::loadMapMeta(): "
2881 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2883 DSTACK(__FUNCTION_NAME);
2884 // Format used for writing
2885 u8 version = SER_FMT_VER_HIGHEST;
2887 v2s16 pos = sector->getPos();
2888 std::string dir = getSectorDir(pos);
2891 std::string fullpath = dir + "/meta";
2892 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2893 if(o.good() == false)
2894 throw FileNotGoodException("Cannot open sector metafile");
2896 sector->serialize(o, version);
2898 sector->differs_from_disk = false;
2901 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2903 DSTACK(__FUNCTION_NAME);
2905 v2s16 p2d = getSectorPos(sectordir);
2907 ServerMapSector *sector = NULL;
2909 std::string fullpath = sectordir + "/meta";
2910 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2911 if(is.good() == false)
2913 // If the directory exists anyway, it probably is in some old
2914 // format. Just go ahead and create the sector.
2915 if(fs::PathExists(sectordir))
2917 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2918 <<fullpath<<" doesn't exist but directory does."
2919 <<" Continuing with a sector with no metadata."
2921 sector = new ServerMapSector(this, p2d);
2922 m_sectors.insert(p2d, sector);
2926 throw FileNotGoodException("Cannot open sector metafile");
2931 sector = ServerMapSector::deSerialize
2932 (is, this, p2d, m_sectors);
2934 saveSectorMeta(sector);
2937 sector->differs_from_disk = false;
2942 bool ServerMap::loadSectorMeta(v2s16 p2d)
2944 DSTACK(__FUNCTION_NAME);
2946 MapSector *sector = NULL;
2948 // The directory layout we're going to load from.
2949 // 1 - original sectors/xxxxzzzz/
2950 // 2 - new sectors2/xxx/zzz/
2951 // If we load from anything but the latest structure, we will
2952 // immediately save to the new one, and remove the old.
2954 std::string sectordir1 = getSectorDir(p2d, 1);
2955 std::string sectordir;
2956 if(fs::PathExists(sectordir1))
2958 sectordir = sectordir1;
2963 sectordir = getSectorDir(p2d, 2);
2967 sector = loadSectorMeta(sectordir, loadlayout != 2);
2969 catch(InvalidFilenameException &e)
2973 catch(FileNotGoodException &e)
2977 catch(std::exception &e)
2986 bool ServerMap::loadSectorFull(v2s16 p2d)
2988 DSTACK(__FUNCTION_NAME);
2990 MapSector *sector = NULL;
2992 // The directory layout we're going to load from.
2993 // 1 - original sectors/xxxxzzzz/
2994 // 2 - new sectors2/xxx/zzz/
2995 // If we load from anything but the latest structure, we will
2996 // immediately save to the new one, and remove the old.
2998 std::string sectordir1 = getSectorDir(p2d, 1);
2999 std::string sectordir;
3000 if(fs::PathExists(sectordir1))
3002 sectordir = sectordir1;
3007 sectordir = getSectorDir(p2d, 2);
3011 sector = loadSectorMeta(sectordir, loadlayout != 2);
3013 catch(InvalidFilenameException &e)
3017 catch(FileNotGoodException &e)
3021 catch(std::exception &e)
3029 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3031 std::vector<fs::DirListNode>::iterator i2;
3032 for(i2=list2.begin(); i2!=list2.end(); i2++)
3038 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3040 catch(InvalidFilenameException &e)
3042 // This catches unknown crap in directory
3048 dstream<<"Sector converted to new layout - deleting "<<
3049 sectordir1<<std::endl;
3050 fs::RecursiveDelete(sectordir1);
3057 void ServerMap::saveBlock(MapBlock *block)
3059 DSTACK(__FUNCTION_NAME);
3061 Dummy blocks are not written
3063 if(block->isDummy())
3065 /*v3s16 p = block->getPos();
3066 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3067 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3071 // Format used for writing
3072 u8 version = SER_FMT_VER_HIGHEST;
3074 v3s16 p3d = block->getPos();
3076 v2s16 p2d(p3d.X, p3d.Z);
3077 std::string sectordir = getSectorDir(p2d);
3079 createDirs(sectordir);
3081 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3082 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3083 if(o.good() == false)
3084 throw FileNotGoodException("Cannot open block data");
3087 [0] u8 serialization version
3090 o.write((char*)&version, 1);
3093 block->serialize(o, version);
3095 // Write extra data stored on disk
3096 block->serializeDiskExtra(o, version);
3098 // We just wrote it to the disk so clear modified flag
3099 block->resetModified();
3102 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3104 DSTACK(__FUNCTION_NAME);
3106 std::string fullpath = sectordir+"/"+blockfile;
3109 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3110 if(is.good() == false)
3111 throw FileNotGoodException("Cannot open block file");
3113 v3s16 p3d = getBlockPos(sectordir, blockfile);
3114 v2s16 p2d(p3d.X, p3d.Z);
3116 assert(sector->getPos() == p2d);
3118 u8 version = SER_FMT_VER_INVALID;
3119 is.read((char*)&version, 1);
3122 throw SerializationError("ServerMap::loadBlock(): Failed"
3123 " to read MapBlock version");
3125 /*u32 block_size = MapBlock::serializedLength(version);
3126 SharedBuffer<u8> data(block_size);
3127 is.read((char*)*data, block_size);*/
3129 // This will always return a sector because we're the server
3130 //MapSector *sector = emergeSector(p2d);
3132 MapBlock *block = NULL;
3133 bool created_new = false;
3134 block = sector->getBlockNoCreateNoEx(p3d.Y);
3137 block = sector->createBlankBlockNoInsert(p3d.Y);
3142 block->deSerialize(is, version);
3144 // Read extra data stored on disk
3145 block->deSerializeDiskExtra(is, version);
3147 // If it's a new block, insert it to the map
3149 sector->insertBlock(block);
3152 Save blocks loaded in old format in new format
3155 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3160 // We just loaded it from the disk, so it's up-to-date.
3161 block->resetModified();
3164 catch(SerializationError &e)
3166 dstream<<"WARNING: Invalid block data on disk "
3167 <<"fullpath="<<fullpath
3168 <<" (SerializationError). "
3169 <<"what()="<<e.what()
3171 //" Ignoring. A new one will be generated.
3174 // TODO: Backup file; name is in fullpath.
3178 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3180 DSTACK(__FUNCTION_NAME);
3182 v2s16 p2d(blockpos.X, blockpos.Z);
3184 // The directory layout we're going to load from.
3185 // 1 - original sectors/xxxxzzzz/
3186 // 2 - new sectors2/xxx/zzz/
3187 // If we load from anything but the latest structure, we will
3188 // immediately save to the new one, and remove the old.
3190 std::string sectordir1 = getSectorDir(p2d, 1);
3191 std::string sectordir;
3192 if(fs::PathExists(sectordir1))
3194 sectordir = sectordir1;
3199 sectordir = getSectorDir(p2d, 2);
3203 Make sure sector is loaded
3205 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3209 sector = loadSectorMeta(sectordir, loadlayout != 2);
3211 catch(InvalidFilenameException &e)
3215 catch(FileNotGoodException &e)
3219 catch(std::exception &e)
3226 Make sure file exists
3229 std::string blockfilename = getBlockFilename(blockpos);
3230 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3236 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3237 return getBlockNoCreateNoEx(blockpos);
3240 void ServerMap::PrintInfo(std::ostream &out)
3251 ClientMap::ClientMap(
3253 MapDrawControl &control,
3254 scene::ISceneNode* parent,
3255 scene::ISceneManager* mgr,
3259 scene::ISceneNode(parent, mgr, id),
3262 m_camera_position(0,0,0),
3263 m_camera_direction(0,0,1)
3265 m_camera_mutex.Init();
3266 assert(m_camera_mutex.IsInitialized());
3268 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3269 BS*1000000,BS*1000000,BS*1000000);
3272 ClientMap::~ClientMap()
3274 /*JMutexAutoLock lock(mesh_mutex);
3283 MapSector * ClientMap::emergeSector(v2s16 p2d)
3285 DSTACK(__FUNCTION_NAME);
3286 // Check that it doesn't exist already
3288 return getSectorNoGenerate(p2d);
3290 catch(InvalidPositionException &e)
3295 ClientMapSector *sector = new ClientMapSector(this, p2d);
3298 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3299 m_sectors.insert(p2d, sector);
3306 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3308 DSTACK(__FUNCTION_NAME);
3309 ClientMapSector *sector = NULL;
3311 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3313 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3317 sector = (ClientMapSector*)n->getValue();
3318 assert(sector->getId() == MAPSECTOR_CLIENT);
3322 sector = new ClientMapSector(this, p2d);
3324 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3325 m_sectors.insert(p2d, sector);
3329 sector->deSerialize(is);
3333 void ClientMap::OnRegisterSceneNode()
3337 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3338 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3341 ISceneNode::OnRegisterSceneNode();
3344 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3346 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3347 DSTACK(__FUNCTION_NAME);
3349 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3352 This is called two times per frame, reset on the non-transparent one
3354 if(pass == scene::ESNRP_SOLID)
3356 m_last_drawn_sectors.clear();
3360 Get time for measuring timeout.
3362 Measuring time is very useful for long delays when the
3363 machine is swapping a lot.
3365 int time1 = time(0);
3367 //u32 daynight_ratio = m_client->getDayNightRatio();
3369 m_camera_mutex.Lock();
3370 v3f camera_position = m_camera_position;
3371 v3f camera_direction = m_camera_direction;
3372 m_camera_mutex.Unlock();
3375 Get all blocks and draw all visible ones
3378 v3s16 cam_pos_nodes(
3379 camera_position.X / BS,
3380 camera_position.Y / BS,
3381 camera_position.Z / BS);
3383 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3385 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3386 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3388 // Take a fair amount as we will be dropping more out later
3390 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3391 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3392 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3394 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3395 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3396 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3398 u32 vertex_count = 0;
3400 // For limiting number of mesh updates per frame
3401 u32 mesh_update_count = 0;
3403 u32 blocks_would_have_drawn = 0;
3404 u32 blocks_drawn = 0;
3406 int timecheck_counter = 0;
3407 core::map<v2s16, MapSector*>::Iterator si;
3408 si = m_sectors.getIterator();
3409 for(; si.atEnd() == false; si++)
3412 timecheck_counter++;
3413 if(timecheck_counter > 50)
3415 timecheck_counter = 0;
3416 int time2 = time(0);
3417 if(time2 > time1 + 4)
3419 dstream<<"ClientMap::renderMap(): "
3420 "Rendering takes ages, returning."
3427 MapSector *sector = si.getNode()->getValue();
3428 v2s16 sp = sector->getPos();
3430 if(m_control.range_all == false)
3432 if(sp.X < p_blocks_min.X
3433 || sp.X > p_blocks_max.X
3434 || sp.Y < p_blocks_min.Z
3435 || sp.Y > p_blocks_max.Z)
3439 core::list< MapBlock * > sectorblocks;
3440 sector->getBlocks(sectorblocks);
3446 u32 sector_blocks_drawn = 0;
3448 core::list< MapBlock * >::Iterator i;
3449 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3451 MapBlock *block = *i;
3454 Compare block position to camera position, skip
3455 if not seen on display
3458 float range = 100000 * BS;
3459 if(m_control.range_all == false)
3460 range = m_control.wanted_range * BS;
3463 if(isBlockInSight(block->getPos(), camera_position,
3464 camera_direction, range, &d) == false)
3469 // Okay, this block will be drawn. Reset usage timer.
3470 block->resetUsageTimer();
3472 // This is ugly (spherical distance limit?)
3473 /*if(m_control.range_all == false &&
3474 d - 0.5*BS*MAP_BLOCKSIZE > range)
3479 Update expired mesh (used for day/night change)
3481 It doesn't work exactly like it should now with the
3482 tasked mesh update but whatever.
3485 bool mesh_expired = false;
3488 JMutexAutoLock lock(block->mesh_mutex);
3490 mesh_expired = block->getMeshExpired();
3492 // Mesh has not been expired and there is no mesh:
3493 // block has no content
3494 if(block->mesh == NULL && mesh_expired == false)
3498 f32 faraway = BS*50;
3499 //f32 faraway = m_control.wanted_range * BS;
3502 This has to be done with the mesh_mutex unlocked
3504 // Pretty random but this should work somewhat nicely
3505 if(mesh_expired && (
3506 (mesh_update_count < 3
3507 && (d < faraway || mesh_update_count < 2)
3510 (m_control.range_all && mesh_update_count < 20)
3513 /*if(mesh_expired && mesh_update_count < 6
3514 && (d < faraway || mesh_update_count < 3))*/
3516 mesh_update_count++;
3518 // Mesh has been expired: generate new mesh
3519 //block->updateMesh(daynight_ratio);
3520 m_client->addUpdateMeshTask(block->getPos());
3522 mesh_expired = false;
3527 Draw the faces of the block
3530 JMutexAutoLock lock(block->mesh_mutex);
3532 scene::SMesh *mesh = block->mesh;
3537 blocks_would_have_drawn++;
3538 if(blocks_drawn >= m_control.wanted_max_blocks
3539 && m_control.range_all == false
3540 && d > m_control.wanted_min_range * BS)
3544 sector_blocks_drawn++;
3546 u32 c = mesh->getMeshBufferCount();
3548 for(u32 i=0; i<c; i++)
3550 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3551 const video::SMaterial& material = buf->getMaterial();
3552 video::IMaterialRenderer* rnd =
3553 driver->getMaterialRenderer(material.MaterialType);
3554 bool transparent = (rnd && rnd->isTransparent());
3555 // Render transparent on transparent pass and likewise.
3556 if(transparent == is_transparent_pass)
3559 This *shouldn't* hurt too much because Irrlicht
3560 doesn't change opengl textures if the old
3561 material is set again.
3563 driver->setMaterial(buf->getMaterial());
3564 driver->drawMeshBuffer(buf);
3565 vertex_count += buf->getVertexCount();
3569 } // foreach sectorblocks
3571 if(sector_blocks_drawn != 0)
3573 m_last_drawn_sectors[sp] = true;
3577 m_control.blocks_drawn = blocks_drawn;
3578 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3580 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3581 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3584 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3585 core::map<v3s16, MapBlock*> *affected_blocks)
3587 bool changed = false;
3589 Add it to all blocks touching it
3592 v3s16(0,0,0), // this
3593 v3s16(0,0,1), // back
3594 v3s16(0,1,0), // top
3595 v3s16(1,0,0), // right
3596 v3s16(0,0,-1), // front
3597 v3s16(0,-1,0), // bottom
3598 v3s16(-1,0,0), // left
3600 for(u16 i=0; i<7; i++)
3602 v3s16 p2 = p + dirs[i];
3603 // Block position of neighbor (or requested) node
3604 v3s16 blockpos = getNodeBlockPos(p2);
3605 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3606 if(blockref == NULL)
3608 // Relative position of requested node
3609 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3610 if(blockref->setTempMod(relpos, mod))
3615 if(changed && affected_blocks!=NULL)
3617 for(u16 i=0; i<7; i++)
3619 v3s16 p2 = p + dirs[i];
3620 // Block position of neighbor (or requested) node
3621 v3s16 blockpos = getNodeBlockPos(p2);
3622 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3623 if(blockref == NULL)
3625 affected_blocks->insert(blockpos, blockref);
3631 bool ClientMap::clearTempMod(v3s16 p,
3632 core::map<v3s16, MapBlock*> *affected_blocks)
3634 bool changed = false;
3636 v3s16(0,0,0), // this
3637 v3s16(0,0,1), // back
3638 v3s16(0,1,0), // top
3639 v3s16(1,0,0), // right
3640 v3s16(0,0,-1), // front
3641 v3s16(0,-1,0), // bottom
3642 v3s16(-1,0,0), // left
3644 for(u16 i=0; i<7; i++)
3646 v3s16 p2 = p + dirs[i];
3647 // Block position of neighbor (or requested) node
3648 v3s16 blockpos = getNodeBlockPos(p2);
3649 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3650 if(blockref == NULL)
3652 // Relative position of requested node
3653 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3654 if(blockref->clearTempMod(relpos))
3659 if(changed && affected_blocks!=NULL)
3661 for(u16 i=0; i<7; i++)
3663 v3s16 p2 = p + dirs[i];
3664 // Block position of neighbor (or requested) node
3665 v3s16 blockpos = getNodeBlockPos(p2);
3666 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3667 if(blockref == NULL)
3669 affected_blocks->insert(blockpos, blockref);
3675 void ClientMap::expireMeshes(bool only_daynight_diffed)
3677 TimeTaker timer("expireMeshes()");
3679 core::map<v2s16, MapSector*>::Iterator si;
3680 si = m_sectors.getIterator();
3681 for(; si.atEnd() == false; si++)
3683 MapSector *sector = si.getNode()->getValue();
3685 core::list< MapBlock * > sectorblocks;
3686 sector->getBlocks(sectorblocks);
3688 core::list< MapBlock * >::Iterator i;
3689 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3691 MapBlock *block = *i;
3693 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3699 JMutexAutoLock lock(block->mesh_mutex);
3700 if(block->mesh != NULL)
3702 /*block->mesh->drop();
3703 block->mesh = NULL;*/
3704 block->setMeshExpired(true);
3711 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3713 assert(mapType() == MAPTYPE_CLIENT);
3716 v3s16 p = blockpos + v3s16(0,0,0);
3717 MapBlock *b = getBlockNoCreate(p);
3718 b->updateMesh(daynight_ratio);
3719 //b->setMeshExpired(true);
3721 catch(InvalidPositionException &e){}
3724 v3s16 p = blockpos + v3s16(-1,0,0);
3725 MapBlock *b = getBlockNoCreate(p);
3726 b->updateMesh(daynight_ratio);
3727 //b->setMeshExpired(true);
3729 catch(InvalidPositionException &e){}
3731 v3s16 p = blockpos + v3s16(0,-1,0);
3732 MapBlock *b = getBlockNoCreate(p);
3733 b->updateMesh(daynight_ratio);
3734 //b->setMeshExpired(true);
3736 catch(InvalidPositionException &e){}
3738 v3s16 p = blockpos + v3s16(0,0,-1);
3739 MapBlock *b = getBlockNoCreate(p);
3740 b->updateMesh(daynight_ratio);
3741 //b->setMeshExpired(true);
3743 catch(InvalidPositionException &e){}
3748 Update mesh of block in which the node is, and if the node is at the
3749 leading edge, update the appropriate leading blocks too.
3751 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3759 v3s16 blockposes[4];
3760 for(u32 i=0; i<4; i++)
3762 v3s16 np = nodepos + dirs[i];
3763 blockposes[i] = getNodeBlockPos(np);
3764 // Don't update mesh of block if it has been done already
3765 bool already_updated = false;
3766 for(u32 j=0; j<i; j++)
3768 if(blockposes[j] == blockposes[i])
3770 already_updated = true;
3777 MapBlock *b = getBlockNoCreate(blockposes[i]);
3778 b->updateMesh(daynight_ratio);
3783 void ClientMap::PrintInfo(std::ostream &out)
3794 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3799 MapVoxelManipulator::~MapVoxelManipulator()
3801 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3805 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3807 TimeTaker timer1("emerge", &emerge_time);
3809 // Units of these are MapBlocks
3810 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3811 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3813 VoxelArea block_area_nodes
3814 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3816 addArea(block_area_nodes);
3818 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3819 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3820 for(s32 x=p_min.X; x<=p_max.X; x++)
3823 core::map<v3s16, bool>::Node *n;
3824 n = m_loaded_blocks.find(p);
3828 bool block_data_inexistent = false;
3831 TimeTaker timer1("emerge load", &emerge_load_time);
3833 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3834 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3837 dstream<<std::endl;*/
3839 MapBlock *block = m_map->getBlockNoCreate(p);
3840 if(block->isDummy())
3841 block_data_inexistent = true;
3843 block->copyTo(*this);
3845 catch(InvalidPositionException &e)
3847 block_data_inexistent = true;
3850 if(block_data_inexistent)
3852 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3853 // Fill with VOXELFLAG_INEXISTENT
3854 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3855 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3857 s32 i = m_area.index(a.MinEdge.X,y,z);
3858 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3862 m_loaded_blocks.insert(p, !block_data_inexistent);
3865 //dstream<<"emerge done"<<std::endl;
3869 SUGG: Add an option to only update eg. water and air nodes.
3870 This will make it interfere less with important stuff if
3873 void MapVoxelManipulator::blitBack
3874 (core::map<v3s16, MapBlock*> & modified_blocks)
3876 if(m_area.getExtent() == v3s16(0,0,0))
3879 //TimeTaker timer1("blitBack");
3881 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3882 <<m_loaded_blocks.size()<<std::endl;*/
3885 Initialize block cache
3887 v3s16 blockpos_last;
3888 MapBlock *block = NULL;
3889 bool block_checked_in_modified = false;
3891 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3892 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3893 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3897 u8 f = m_flags[m_area.index(p)];
3898 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3901 MapNode &n = m_data[m_area.index(p)];
3903 v3s16 blockpos = getNodeBlockPos(p);
3908 if(block == NULL || blockpos != blockpos_last){
3909 block = m_map->getBlockNoCreate(blockpos);
3910 blockpos_last = blockpos;
3911 block_checked_in_modified = false;
3914 // Calculate relative position in block
3915 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3917 // Don't continue if nothing has changed here
3918 if(block->getNode(relpos) == n)
3921 //m_map->setNode(m_area.MinEdge + p, n);
3922 block->setNode(relpos, n);
3925 Make sure block is in modified_blocks
3927 if(block_checked_in_modified == false)
3929 modified_blocks[blockpos] = block;
3930 block_checked_in_modified = true;
3933 catch(InvalidPositionException &e)
3939 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3940 MapVoxelManipulator(map),
3941 m_create_area(false)
3945 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3949 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3951 // Just create the area so that it can be pointed to
3952 VoxelManipulator::emerge(a, caller_id);
3955 void ManualMapVoxelManipulator::initialEmerge(
3956 v3s16 blockpos_min, v3s16 blockpos_max)
3958 TimeTaker timer1("initialEmerge", &emerge_time);
3960 // Units of these are MapBlocks
3961 v3s16 p_min = blockpos_min;
3962 v3s16 p_max = blockpos_max;
3964 VoxelArea block_area_nodes
3965 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3967 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3970 dstream<<"initialEmerge: area: ";
3971 block_area_nodes.print(dstream);
3972 dstream<<" ("<<size_MB<<"MB)";
3976 addArea(block_area_nodes);
3978 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3979 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3980 for(s32 x=p_min.X; x<=p_max.X; x++)
3983 core::map<v3s16, bool>::Node *n;
3984 n = m_loaded_blocks.find(p);
3988 bool block_data_inexistent = false;
3991 TimeTaker timer1("emerge load", &emerge_load_time);
3993 MapBlock *block = m_map->getBlockNoCreate(p);
3994 if(block->isDummy())
3995 block_data_inexistent = true;
3997 block->copyTo(*this);
3999 catch(InvalidPositionException &e)
4001 block_data_inexistent = true;
4004 if(block_data_inexistent)
4007 Mark area inexistent
4009 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4010 // Fill with VOXELFLAG_INEXISTENT
4011 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4012 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4014 s32 i = m_area.index(a.MinEdge.X,y,z);
4015 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4019 m_loaded_blocks.insert(p, !block_data_inexistent);
4023 void ManualMapVoxelManipulator::blitBackAll(
4024 core::map<v3s16, MapBlock*> * modified_blocks)
4026 if(m_area.getExtent() == v3s16(0,0,0))
4030 Copy data of all blocks
4032 for(core::map<v3s16, bool>::Iterator
4033 i = m_loaded_blocks.getIterator();
4034 i.atEnd() == false; i++)
4036 bool existed = i.getNode()->getValue();
4037 if(existed == false)
4039 v3s16 p = i.getNode()->getKey();
4040 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4043 dstream<<"WARNING: "<<__FUNCTION_NAME
4044 <<": got NULL block "
4045 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4050 block->copyFrom(*this);
4053 modified_blocks->insert(p, block);