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 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.d) || n2.d == CONTENT_AIR)
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
1545 NEIGHBOR_SAME_LEVEL,
1548 struct NodeNeighbor {
1554 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1556 DSTACK(__FUNCTION_NAME);
1557 //TimeTaker timer("transformLiquids()");
1560 u32 initial_size = m_transforming_liquid.size();
1562 /*if(initial_size != 0)
1563 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1565 while(m_transforming_liquid.size() != 0)
1568 Get a queued transforming liquid node
1570 v3s16 p0 = m_transforming_liquid.pop_front();
1572 MapNode n0 = getNodeNoEx(p0);
1575 Collect information about current node
1577 s8 liquid_level = -1;
1579 LiquidType liquid_type = content_features(n0.d).liquid_type;
1580 switch (liquid_type) {
1583 liquid_kind = content_features(n0.d).liquid_alternative_flowing;
1585 case LIQUID_FLOWING:
1586 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1590 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1591 // continue with the next node.
1592 if (n0.d != CONTENT_AIR)
1594 liquid_kind = CONTENT_AIR;
1599 Collect information about the environment
1602 v3s16( 0, 1, 0), // top
1603 v3s16( 0,-1, 0), // bottom
1604 v3s16( 1, 0, 0), // right
1605 v3s16(-1, 0, 0), // left
1606 v3s16( 0, 0, 1), // back
1607 v3s16( 0, 0,-1), // front
1609 NodeNeighbor sources[6]; // surrounding sources
1610 int num_sources = 0;
1611 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1613 NodeNeighbor airs[6]; // surrounding air
1615 NodeNeighbor neutrals[6]; // nodes that are solid, another kind of liquid
1616 int num_neutrals = 0;
1617 bool flowing_down = false;
1618 for (u16 i = 0; i < 6; i++) {
1619 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1622 nt = NEIGHBOR_UPPER;
1625 nt = NEIGHBOR_LOWER;
1628 v3s16 npos = p0 + dirs[i];
1629 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1630 switch (content_features(nb.n.d).liquid_type) {
1632 if (nb.n.d == CONTENT_AIR) {
1633 airs[num_airs++] = nb;
1634 // if the current nodes happens to be a flowing node, it will start to flow down here.
1635 if (nb.t == NEIGHBOR_LOWER)
1636 flowing_down = true;
1638 neutrals[num_neutrals++] = nb;
1642 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1643 if (liquid_kind == CONTENT_AIR)
1644 liquid_kind = content_features(nb.n.d).liquid_alternative_flowing;
1645 if (content_features(nb.n.d).liquid_alternative_flowing !=liquid_kind) {
1646 neutrals[num_neutrals++] = nb;
1648 sources[num_sources++] = nb;
1651 case LIQUID_FLOWING:
1652 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1653 // (while ignoring flowing liquids at the lowest level, which cannot flow into this node)
1654 if (liquid_kind == CONTENT_AIR)
1655 liquid_kind = content_features(nb.n.d).liquid_alternative_flowing;
1656 if (content_features(nb.n.d).liquid_alternative_flowing != liquid_kind) {
1657 neutrals[num_neutrals++] = nb;
1659 // order flowing neighbors by liquid level descending
1661 while (insert_at < num_flows && ((flows[insert_at].n.param2 & LIQUID_LEVEL_MASK) >
1662 (nb.n.param2 & LIQUID_LEVEL_MASK))) {
1665 for (u16 j = insert_at; j < num_flows; j++)
1666 flows[j + 1] = flows[j];
1667 flows[insert_at] = nb;
1669 if (nb.t == NEIGHBOR_LOWER)
1670 flowing_down = true;
1677 decide on the type (and possibly level) of the current node
1679 u8 new_node_content;
1680 s8 new_node_level = -1;
1681 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1682 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1683 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1684 // it's perfectly safe to use liquid_kind here to determine the new node content.
1685 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1686 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1687 // liquid_kind is set properly, see above
1688 new_node_content = liquid_kind;
1691 // no surrounding sources, so get the maximum level that can flow into this node
1692 for (u16 i = 0; i < num_flows; i++) {
1693 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1694 switch (flows[i].t) {
1695 case NEIGHBOR_UPPER:
1696 if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
1698 if (nb_liquid_level + WATER_DROP_BOOST < 7)
1699 new_node_level = nb_liquid_level + WATER_DROP_BOOST;
1702 case NEIGHBOR_LOWER:
1704 case NEIGHBOR_SAME_LEVEL:
1705 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1706 nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) {
1707 new_node_level = nb_liquid_level - 1;
1712 // don't flow as far in open terrain - if there isn't at least one adjacent solid block,
1713 // substract another unit from the resulting water level.
1714 if (!flowing_down && new_node_level >= 1) {
1715 bool at_wall = false;
1716 for (u16 i = 0; i < num_neutrals; i++) {
1717 if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) {
1723 new_node_level -= 1;
1726 if (new_node_level >= 0)
1727 new_node_content = liquid_kind;
1729 new_node_content = CONTENT_AIR;
1733 check if anything has changed. if not, just continue with the next node.
1735 if (new_node_content == n0.d && (content_features(n0.d).liquid_type != LIQUID_FLOWING ||
1736 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1737 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1743 update the current node
1745 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1746 n0.d = new_node_content;
1747 if (content_features(n0.d).liquid_type == LIQUID_FLOWING) {
1748 // set level to last 3 bits, flowing down bit to 4th bit
1749 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1754 v3s16 blockpos = getNodeBlockPos(p0);
1755 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1757 modified_blocks.insert(blockpos, block);
1760 enqueue neighbors for update if neccessary
1762 switch (content_features(n0.d).liquid_type) {
1764 // make sure source flows into all neighboring nodes
1765 for (u16 i = 0; i < num_flows; i++)
1766 if (flows[i].t != NEIGHBOR_UPPER)
1767 m_transforming_liquid.push_back(flows[i].p);
1768 for (u16 i = 0; i < num_airs; i++)
1769 if (airs[i].t != NEIGHBOR_UPPER)
1770 m_transforming_liquid.push_back(airs[i].p);
1773 // this flow has turned to air; neighboring flows might need to do the same
1774 for (u16 i = 0; i < num_flows; i++)
1775 m_transforming_liquid.push_back(flows[i].p);
1777 case LIQUID_FLOWING:
1778 for (u16 i = 0; i < num_flows; i++) {
1779 /*u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1780 // liquid_level is still the ORIGINAL level of this node.
1781 if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
1782 flow_down_enabled))*/
1783 m_transforming_liquid.push_back(flows[i].p);
1785 for (u16 i = 0; i < num_airs; i++) {
1786 if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
1787 m_transforming_liquid.push_back(airs[i].p);
1793 //if(loopcount >= 100000)
1794 if(loopcount >= initial_size * 10) {
1798 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1801 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1803 v3s16 blockpos = getNodeBlockPos(p);
1804 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1805 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1808 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1812 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1816 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1818 v3s16 blockpos = getNodeBlockPos(p);
1819 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1820 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1823 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1827 block->m_node_metadata.set(p_rel, meta);
1830 void Map::removeNodeMetadata(v3s16 p)
1832 v3s16 blockpos = getNodeBlockPos(p);
1833 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1834 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1837 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1841 block->m_node_metadata.remove(p_rel);
1844 void Map::nodeMetadataStep(float dtime,
1845 core::map<v3s16, MapBlock*> &changed_blocks)
1849 Currently there is no way to ensure that all the necessary
1850 blocks are loaded when this is run. (They might get unloaded)
1851 NOTE: ^- Actually, that might not be so. In a quick test it
1852 reloaded a block with a furnace when I walked back to it from
1855 core::map<v2s16, MapSector*>::Iterator si;
1856 si = m_sectors.getIterator();
1857 for(; si.atEnd() == false; si++)
1859 MapSector *sector = si.getNode()->getValue();
1860 core::list< MapBlock * > sectorblocks;
1861 sector->getBlocks(sectorblocks);
1862 core::list< MapBlock * >::Iterator i;
1863 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1865 MapBlock *block = *i;
1866 bool changed = block->m_node_metadata.step(dtime);
1868 changed_blocks[block->getPos()] = block;
1877 ServerMap::ServerMap(std::string savedir):
1880 m_map_metadata_changed(true)
1882 dstream<<__FUNCTION_NAME<<std::endl;
1884 //m_chunksize = 8; // Takes a few seconds
1886 m_seed = (((u64)(myrand()%0xffff)<<0)
1887 + ((u64)(myrand()%0xffff)<<16)
1888 + ((u64)(myrand()%0xffff)<<32)
1889 + ((u64)(myrand()%0xffff)<<48));
1892 Experimental and debug stuff
1899 Try to load map; if not found, create a new one.
1902 m_savedir = savedir;
1903 m_map_saving_enabled = false;
1907 // If directory exists, check contents and load if possible
1908 if(fs::PathExists(m_savedir))
1910 // If directory is empty, it is safe to save into it.
1911 if(fs::GetDirListing(m_savedir).size() == 0)
1913 dstream<<DTIME<<"Server: Empty save directory is valid."
1915 m_map_saving_enabled = true;
1920 // Load map metadata (seed, chunksize)
1923 catch(FileNotGoodException &e){
1924 dstream<<DTIME<<"WARNING: Could not load map metadata"
1925 //<<" Disabling chunk-based generator."
1931 // Load chunk metadata
1934 catch(FileNotGoodException &e){
1935 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1936 <<" Disabling chunk-based generator."
1941 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1942 "metadata and sector (0,0) from "<<savedir<<
1943 ", assuming valid save directory."
1946 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1947 <<"and chunk metadata from "<<savedir
1948 <<", assuming valid save directory."
1951 m_map_saving_enabled = true;
1952 // Map loaded, not creating new one
1956 // If directory doesn't exist, it is safe to save to it
1958 m_map_saving_enabled = true;
1961 catch(std::exception &e)
1963 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1964 <<", exception: "<<e.what()<<std::endl;
1965 dstream<<"Please remove the map or fix it."<<std::endl;
1966 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1969 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1971 // Create zero sector
1972 emergeSector(v2s16(0,0));
1974 // Initially write whole map
1978 ServerMap::~ServerMap()
1980 dstream<<__FUNCTION_NAME<<std::endl;
1984 if(m_map_saving_enabled)
1986 // Save only changed parts
1988 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1992 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1995 catch(std::exception &e)
1997 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1998 <<", exception: "<<e.what()<<std::endl;
2005 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2006 for(; i.atEnd() == false; i++)
2008 MapChunk *chunk = i.getNode()->getValue();
2014 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2016 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2017 <<blockpos.Z<<")"<<std::endl;*/
2019 data->no_op = false;
2020 data->seed = m_seed;
2021 data->blockpos = blockpos;
2024 Create the whole area of this and the neighboring blocks
2027 //TimeTaker timer("initBlockMake() create area");
2029 for(s16 x=-1; x<=1; x++)
2030 for(s16 z=-1; z<=1; z++)
2032 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2033 // Sector metadata is loaded from disk if not already loaded.
2034 ServerMapSector *sector = createSector(sectorpos);
2037 for(s16 y=-1; y<=1; y++)
2039 MapBlock *block = createBlock(blockpos);
2041 // Lighting won't be calculated
2042 block->setLightingExpired(true);
2043 // Lighting will be calculated
2044 //block->setLightingExpired(false);
2047 Block gets sunlight if this is true.
2049 This should be set to true when the top side of a block
2050 is completely exposed to the sky.
2052 block->setIsUnderground(false);
2058 Now we have a big empty area.
2060 Make a ManualMapVoxelManipulator that contains this and the
2064 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2065 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2067 data->vmanip = new ManualMapVoxelManipulator(this);
2068 //data->vmanip->setMap(this);
2072 //TimeTaker timer("initBlockMake() initialEmerge");
2073 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2076 // Data is ready now.
2079 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2080 core::map<v3s16, MapBlock*> &changed_blocks)
2082 v3s16 blockpos = data->blockpos;
2083 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2084 <<blockpos.Z<<")"<<std::endl;*/
2088 dstream<<"finishBlockMake(): no-op"<<std::endl;
2092 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2094 /*dstream<<"Resulting vmanip:"<<std::endl;
2095 data->vmanip.print(dstream);*/
2098 Blit generated stuff to map
2099 NOTE: blitBackAll adds nearly everything to changed_blocks
2103 //TimeTaker timer("finishBlockMake() blitBackAll");
2104 data->vmanip->blitBackAll(&changed_blocks);
2107 if(enable_mapgen_debug_info)
2108 dstream<<"finishBlockMake: changed_blocks.size()="
2109 <<changed_blocks.size()<<std::endl;
2112 Copy transforming liquid information
2114 while(data->transforming_liquid.size() > 0)
2116 v3s16 p = data->transforming_liquid.pop_front();
2117 m_transforming_liquid.push_back(p);
2123 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2127 Set is_underground flag for lighting with sunlight
2130 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2133 Add sunlight to central block.
2134 This makes in-dark-spawning monsters to not flood the whole thing.
2135 Do not spread the light, though.
2137 /*core::map<v3s16, bool> light_sources;
2138 bool black_air_left = false;
2139 block->propagateSunlight(light_sources, true, &black_air_left);*/
2142 NOTE: Lighting and object adding shouldn't really be here, but
2143 lighting is a bit tricky to move properly to makeBlock.
2144 TODO: Do this the right way anyway, that is, move it to makeBlock.
2145 - There needs to be some way for makeBlock to report back if
2146 the lighting update is going further down because of the
2147 new block blocking light
2152 NOTE: This takes ~60ms, TODO: Investigate why
2155 TimeTaker t("finishBlockMake lighting update");
2157 core::map<v3s16, MapBlock*> lighting_update_blocks;
2159 lighting_update_blocks.insert(block->getPos(), block);
2161 // All modified blocks
2162 for(core::map<v3s16, MapBlock*>::Iterator
2163 i = changed_blocks.getIterator();
2164 i.atEnd() == false; i++)
2166 lighting_update_blocks.insert(i.getNode()->getKey(),
2167 i.getNode()->getValue());
2170 updateLighting(lighting_update_blocks, changed_blocks);
2172 if(enable_mapgen_debug_info == false)
2173 t.stop(true); // Hide output
2177 Add random objects to block
2179 mapgen::add_random_objects(block);
2182 Go through changed blocks
2184 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2185 i.atEnd() == false; i++)
2187 MapBlock *block = i.getNode()->getValue();
2190 Update day/night difference cache of the MapBlocks
2192 block->updateDayNightDiff();
2194 Set block as modified
2196 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2200 Set central block as generated
2202 block->setGenerated(true);
2205 Save changed parts of map
2206 NOTE: Will be saved later.
2210 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2211 <<blockpos.Z<<")"<<std::endl;*/
2216 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2218 DSTACKF("%s: p2d=(%d,%d)",
2223 Check if it exists already in memory
2225 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2230 Try to load it from disk (with blocks)
2232 //if(loadSectorFull(p2d) == true)
2235 Try to load metadata from disk
2237 if(loadSectorMeta(p2d) == true)
2239 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2242 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2243 throw InvalidPositionException("");
2249 Do not create over-limit
2251 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2252 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2253 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2254 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2255 throw InvalidPositionException("createSector(): pos. over limit");
2258 Generate blank sector
2261 sector = new ServerMapSector(this, p2d);
2263 // Sector position on map in nodes
2264 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2269 m_sectors.insert(p2d, sector);
2275 This is a quick-hand function for calling makeBlock().
2277 MapBlock * ServerMap::generateBlock(
2279 core::map<v3s16, MapBlock*> &modified_blocks
2282 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2284 /*dstream<<"generateBlock(): "
2285 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2288 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2290 TimeTaker timer("generateBlock");
2292 //MapBlock *block = original_dummy;
2294 v2s16 p2d(p.X, p.Z);
2295 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2298 Do not generate over-limit
2300 if(blockpos_over_limit(p))
2302 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2303 throw InvalidPositionException("generateBlock(): pos. over limit");
2307 Create block make data
2309 mapgen::BlockMakeData data;
2310 initBlockMake(&data, p);
2316 TimeTaker t("mapgen::make_block()");
2317 mapgen::make_block(&data);
2319 if(enable_mapgen_debug_info == false)
2320 t.stop(true); // Hide output
2324 Blit data back on map, update lighting, add mobs and whatever this does
2326 finishBlockMake(&data, modified_blocks);
2331 MapBlock *block = getBlockNoCreateNoEx(p);
2338 bool erroneus_content = false;
2339 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2340 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2341 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2344 MapNode n = block->getNode(p);
2345 if(n.d == CONTENT_IGNORE)
2347 dstream<<"CONTENT_IGNORE at "
2348 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2350 erroneus_content = true;
2354 if(erroneus_content)
2362 Generate a completely empty block
2364 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2365 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2367 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2373 n.d = CONTENT_STONE;
2374 block->setNode(v3s16(x0,y0,z0), n);
2379 if(enable_mapgen_debug_info == false)
2380 timer.stop(true); // Hide output
2385 MapBlock * ServerMap::createBlock(v3s16 p)
2387 DSTACKF("%s: p=(%d,%d,%d)",
2388 __FUNCTION_NAME, p.X, p.Y, p.Z);
2391 Do not create over-limit
2393 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2394 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2395 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2396 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2397 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2398 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2399 throw InvalidPositionException("createBlock(): pos. over limit");
2401 v2s16 p2d(p.X, p.Z);
2404 This will create or load a sector if not found in memory.
2405 If block exists on disk, it will be loaded.
2407 NOTE: On old save formats, this will be slow, as it generates
2408 lighting on blocks for them.
2410 ServerMapSector *sector;
2412 sector = (ServerMapSector*)createSector(p2d);
2413 assert(sector->getId() == MAPSECTOR_SERVER);
2415 catch(InvalidPositionException &e)
2417 dstream<<"createBlock: createSector() failed"<<std::endl;
2421 NOTE: This should not be done, or at least the exception
2422 should not be passed on as std::exception, because it
2423 won't be catched at all.
2425 /*catch(std::exception &e)
2427 dstream<<"createBlock: createSector() failed: "
2428 <<e.what()<<std::endl;
2433 Try to get a block from the sector
2436 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2439 if(block->isDummy())
2444 block = sector->createBlankBlock(block_y);
2448 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2450 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2452 p.X, p.Y, p.Z, allow_generate);
2455 MapBlock *block = getBlockNoCreateNoEx(p);
2461 MapBlock *block = loadBlock(p);
2468 core::map<v3s16, MapBlock*> modified_blocks;
2469 MapBlock *block = generateBlock(p, modified_blocks);
2473 event.type = MEET_OTHER;
2476 // Copy modified_blocks to event
2477 for(core::map<v3s16, MapBlock*>::Iterator
2478 i = modified_blocks.getIterator();
2479 i.atEnd()==false; i++)
2481 event.modified_blocks.insert(i.getNode()->getKey(), false);
2485 dispatchEvent(&event);
2496 Do not generate over-limit
2498 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2499 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2500 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2501 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2502 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2503 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2504 throw InvalidPositionException("emergeBlock(): pos. over limit");
2506 v2s16 p2d(p.X, p.Z);
2509 This will create or load a sector if not found in memory.
2510 If block exists on disk, it will be loaded.
2512 ServerMapSector *sector;
2514 sector = createSector(p2d);
2515 //sector = emergeSector(p2d, changed_blocks);
2517 catch(InvalidPositionException &e)
2519 dstream<<"emergeBlock: createSector() failed: "
2520 <<e.what()<<std::endl;
2521 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2523 <<"You could try to delete it."<<std::endl;
2526 catch(VersionMismatchException &e)
2528 dstream<<"emergeBlock: createSector() failed: "
2529 <<e.what()<<std::endl;
2530 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2532 <<"You could try to delete it."<<std::endl;
2537 Try to get a block from the sector
2540 bool does_not_exist = false;
2541 bool lighting_expired = false;
2542 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2544 // If not found, try loading from disk
2547 block = loadBlock(p);
2553 does_not_exist = true;
2555 else if(block->isDummy() == true)
2557 does_not_exist = true;
2559 else if(block->getLightingExpired())
2561 lighting_expired = true;
2566 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2571 If block was not found on disk and not going to generate a
2572 new one, make sure there is a dummy block in place.
2574 if(only_from_disk && (does_not_exist || lighting_expired))
2576 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2580 // Create dummy block
2581 block = new MapBlock(this, p, true);
2583 // Add block to sector
2584 sector->insertBlock(block);
2590 //dstream<<"Not found on disk, generating."<<std::endl;
2592 //TimeTaker("emergeBlock() generate");
2594 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2597 If the block doesn't exist, generate the block.
2601 block = generateBlock(p, block, sector, changed_blocks,
2602 lighting_invalidated_blocks);
2605 if(lighting_expired)
2607 lighting_invalidated_blocks.insert(p, block);
2612 Initially update sunlight
2615 core::map<v3s16, bool> light_sources;
2616 bool black_air_left = false;
2617 bool bottom_invalid =
2618 block->propagateSunlight(light_sources, true,
2621 // If sunlight didn't reach everywhere and part of block is
2622 // above ground, lighting has to be properly updated
2623 //if(black_air_left && some_part_underground)
2626 lighting_invalidated_blocks[block->getPos()] = block;
2631 lighting_invalidated_blocks[block->getPos()] = block;
2640 s16 ServerMap::findGroundLevel(v2s16 p2d)
2644 Uh, just do something random...
2646 // Find existing map from top to down
2649 v3s16 p(p2d.X, max, p2d.Y);
2650 for(; p.Y>min; p.Y--)
2652 MapNode n = getNodeNoEx(p);
2653 if(n.d != CONTENT_IGNORE)
2658 // If this node is not air, go to plan b
2659 if(getNodeNoEx(p).d != CONTENT_AIR)
2661 // Search existing walkable and return it
2662 for(; p.Y>min; p.Y--)
2664 MapNode n = getNodeNoEx(p);
2665 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2674 Determine from map generator noise functions
2677 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2680 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2681 //return (s16)level;
2684 void ServerMap::createDirs(std::string path)
2686 if(fs::CreateAllDirs(path) == false)
2688 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2689 <<"\""<<path<<"\""<<std::endl;
2690 throw BaseException("ServerMap failed to create directory");
2694 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2700 snprintf(cc, 9, "%.4x%.4x",
2701 (unsigned int)pos.X&0xffff,
2702 (unsigned int)pos.Y&0xffff);
2704 return m_savedir + "/sectors/" + cc;
2706 snprintf(cc, 9, "%.3x/%.3x",
2707 (unsigned int)pos.X&0xfff,
2708 (unsigned int)pos.Y&0xfff);
2710 return m_savedir + "/sectors2/" + cc;
2716 v2s16 ServerMap::getSectorPos(std::string dirname)
2720 size_t spos = dirname.rfind('/') + 1;
2721 assert(spos != std::string::npos);
2722 if(dirname.size() - spos == 8)
2725 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2727 else if(dirname.size() - spos == 3)
2730 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2731 // Sign-extend the 12 bit values up to 16 bits...
2732 if(x&0x800) x|=0xF000;
2733 if(y&0x800) y|=0xF000;
2740 v2s16 pos((s16)x, (s16)y);
2744 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2746 v2s16 p2d = getSectorPos(sectordir);
2748 if(blockfile.size() != 4){
2749 throw InvalidFilenameException("Invalid block filename");
2752 int r = sscanf(blockfile.c_str(), "%4x", &y);
2754 throw InvalidFilenameException("Invalid block filename");
2755 return v3s16(p2d.X, y, p2d.Y);
2758 std::string ServerMap::getBlockFilename(v3s16 p)
2761 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2765 void ServerMap::save(bool only_changed)
2767 DSTACK(__FUNCTION_NAME);
2768 if(m_map_saving_enabled == false)
2770 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2774 if(only_changed == false)
2775 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2778 if(only_changed == false || m_map_metadata_changed)
2783 u32 sector_meta_count = 0;
2784 u32 block_count = 0;
2785 u32 block_count_all = 0; // Number of blocks in memory
2787 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2788 for(; i.atEnd() == false; i++)
2790 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2791 assert(sector->getId() == MAPSECTOR_SERVER);
2793 if(sector->differs_from_disk || only_changed == false)
2795 saveSectorMeta(sector);
2796 sector_meta_count++;
2798 core::list<MapBlock*> blocks;
2799 sector->getBlocks(blocks);
2800 core::list<MapBlock*>::Iterator j;
2801 for(j=blocks.begin(); j!=blocks.end(); j++)
2803 MapBlock *block = *j;
2807 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2808 || only_changed == false)
2813 /*dstream<<"ServerMap: Written block ("
2814 <<block->getPos().X<<","
2815 <<block->getPos().Y<<","
2816 <<block->getPos().Z<<")"
2823 Only print if something happened or saved whole map
2825 if(only_changed == false || sector_meta_count != 0
2826 || block_count != 0)
2828 dstream<<DTIME<<"ServerMap: Written: "
2829 <<sector_meta_count<<" sector metadata files, "
2830 <<block_count<<" block files"
2831 <<", "<<block_count_all<<" blocks in memory."
2836 void ServerMap::saveMapMeta()
2838 DSTACK(__FUNCTION_NAME);
2840 dstream<<"INFO: ServerMap::saveMapMeta(): "
2844 createDirs(m_savedir);
2846 std::string fullpath = m_savedir + "/map_meta.txt";
2847 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2848 if(os.good() == false)
2850 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2851 <<"could not open"<<fullpath<<std::endl;
2852 throw FileNotGoodException("Cannot open chunk metadata");
2856 params.setU64("seed", m_seed);
2858 params.writeLines(os);
2860 os<<"[end_of_params]\n";
2862 m_map_metadata_changed = false;
2865 void ServerMap::loadMapMeta()
2867 DSTACK(__FUNCTION_NAME);
2869 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2872 std::string fullpath = m_savedir + "/map_meta.txt";
2873 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2874 if(is.good() == false)
2876 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2877 <<"could not open"<<fullpath<<std::endl;
2878 throw FileNotGoodException("Cannot open map metadata");
2886 throw SerializationError
2887 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2889 std::getline(is, line);
2890 std::string trimmedline = trim(line);
2891 if(trimmedline == "[end_of_params]")
2893 params.parseConfigLine(line);
2896 m_seed = params.getU64("seed");
2898 dstream<<"INFO: ServerMap::loadMapMeta(): "
2903 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2905 DSTACK(__FUNCTION_NAME);
2906 // Format used for writing
2907 u8 version = SER_FMT_VER_HIGHEST;
2909 v2s16 pos = sector->getPos();
2910 std::string dir = getSectorDir(pos);
2913 std::string fullpath = dir + "/meta";
2914 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2915 if(o.good() == false)
2916 throw FileNotGoodException("Cannot open sector metafile");
2918 sector->serialize(o, version);
2920 sector->differs_from_disk = false;
2923 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2925 DSTACK(__FUNCTION_NAME);
2927 v2s16 p2d = getSectorPos(sectordir);
2929 ServerMapSector *sector = NULL;
2931 std::string fullpath = sectordir + "/meta";
2932 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2933 if(is.good() == false)
2935 // If the directory exists anyway, it probably is in some old
2936 // format. Just go ahead and create the sector.
2937 if(fs::PathExists(sectordir))
2939 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2940 <<fullpath<<" doesn't exist but directory does."
2941 <<" Continuing with a sector with no metadata."
2943 sector = new ServerMapSector(this, p2d);
2944 m_sectors.insert(p2d, sector);
2948 throw FileNotGoodException("Cannot open sector metafile");
2953 sector = ServerMapSector::deSerialize
2954 (is, this, p2d, m_sectors);
2956 saveSectorMeta(sector);
2959 sector->differs_from_disk = false;
2964 bool ServerMap::loadSectorMeta(v2s16 p2d)
2966 DSTACK(__FUNCTION_NAME);
2968 MapSector *sector = NULL;
2970 // The directory layout we're going to load from.
2971 // 1 - original sectors/xxxxzzzz/
2972 // 2 - new sectors2/xxx/zzz/
2973 // If we load from anything but the latest structure, we will
2974 // immediately save to the new one, and remove the old.
2976 std::string sectordir1 = getSectorDir(p2d, 1);
2977 std::string sectordir;
2978 if(fs::PathExists(sectordir1))
2980 sectordir = sectordir1;
2985 sectordir = getSectorDir(p2d, 2);
2989 sector = loadSectorMeta(sectordir, loadlayout != 2);
2991 catch(InvalidFilenameException &e)
2995 catch(FileNotGoodException &e)
2999 catch(std::exception &e)
3008 bool ServerMap::loadSectorFull(v2s16 p2d)
3010 DSTACK(__FUNCTION_NAME);
3012 MapSector *sector = NULL;
3014 // The directory layout we're going to load from.
3015 // 1 - original sectors/xxxxzzzz/
3016 // 2 - new sectors2/xxx/zzz/
3017 // If we load from anything but the latest structure, we will
3018 // immediately save to the new one, and remove the old.
3020 std::string sectordir1 = getSectorDir(p2d, 1);
3021 std::string sectordir;
3022 if(fs::PathExists(sectordir1))
3024 sectordir = sectordir1;
3029 sectordir = getSectorDir(p2d, 2);
3033 sector = loadSectorMeta(sectordir, loadlayout != 2);
3035 catch(InvalidFilenameException &e)
3039 catch(FileNotGoodException &e)
3043 catch(std::exception &e)
3051 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3053 std::vector<fs::DirListNode>::iterator i2;
3054 for(i2=list2.begin(); i2!=list2.end(); i2++)
3060 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3062 catch(InvalidFilenameException &e)
3064 // This catches unknown crap in directory
3070 dstream<<"Sector converted to new layout - deleting "<<
3071 sectordir1<<std::endl;
3072 fs::RecursiveDelete(sectordir1);
3079 void ServerMap::saveBlock(MapBlock *block)
3081 DSTACK(__FUNCTION_NAME);
3083 Dummy blocks are not written
3085 if(block->isDummy())
3087 /*v3s16 p = block->getPos();
3088 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3089 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3093 // Format used for writing
3094 u8 version = SER_FMT_VER_HIGHEST;
3096 v3s16 p3d = block->getPos();
3098 v2s16 p2d(p3d.X, p3d.Z);
3099 std::string sectordir = getSectorDir(p2d);
3101 createDirs(sectordir);
3103 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3104 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3105 if(o.good() == false)
3106 throw FileNotGoodException("Cannot open block data");
3109 [0] u8 serialization version
3112 o.write((char*)&version, 1);
3115 block->serialize(o, version);
3117 // Write extra data stored on disk
3118 block->serializeDiskExtra(o, version);
3120 // We just wrote it to the disk so clear modified flag
3121 block->resetModified();
3124 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3126 DSTACK(__FUNCTION_NAME);
3128 std::string fullpath = sectordir+"/"+blockfile;
3131 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3132 if(is.good() == false)
3133 throw FileNotGoodException("Cannot open block file");
3135 v3s16 p3d = getBlockPos(sectordir, blockfile);
3136 v2s16 p2d(p3d.X, p3d.Z);
3138 assert(sector->getPos() == p2d);
3140 u8 version = SER_FMT_VER_INVALID;
3141 is.read((char*)&version, 1);
3144 throw SerializationError("ServerMap::loadBlock(): Failed"
3145 " to read MapBlock version");
3147 /*u32 block_size = MapBlock::serializedLength(version);
3148 SharedBuffer<u8> data(block_size);
3149 is.read((char*)*data, block_size);*/
3151 // This will always return a sector because we're the server
3152 //MapSector *sector = emergeSector(p2d);
3154 MapBlock *block = NULL;
3155 bool created_new = false;
3156 block = sector->getBlockNoCreateNoEx(p3d.Y);
3159 block = sector->createBlankBlockNoInsert(p3d.Y);
3164 block->deSerialize(is, version);
3166 // Read extra data stored on disk
3167 block->deSerializeDiskExtra(is, version);
3169 // If it's a new block, insert it to the map
3171 sector->insertBlock(block);
3174 Save blocks loaded in old format in new format
3177 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3182 // We just loaded it from the disk, so it's up-to-date.
3183 block->resetModified();
3186 catch(SerializationError &e)
3188 dstream<<"WARNING: Invalid block data on disk "
3189 <<"fullpath="<<fullpath
3190 <<" (SerializationError). "
3191 <<"what()="<<e.what()
3193 //" Ignoring. A new one will be generated.
3196 // TODO: Backup file; name is in fullpath.
3200 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3202 DSTACK(__FUNCTION_NAME);
3204 v2s16 p2d(blockpos.X, blockpos.Z);
3206 // The directory layout we're going to load from.
3207 // 1 - original sectors/xxxxzzzz/
3208 // 2 - new sectors2/xxx/zzz/
3209 // If we load from anything but the latest structure, we will
3210 // immediately save to the new one, and remove the old.
3212 std::string sectordir1 = getSectorDir(p2d, 1);
3213 std::string sectordir;
3214 if(fs::PathExists(sectordir1))
3216 sectordir = sectordir1;
3221 sectordir = getSectorDir(p2d, 2);
3225 Make sure sector is loaded
3227 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3231 sector = loadSectorMeta(sectordir, loadlayout != 2);
3233 catch(InvalidFilenameException &e)
3237 catch(FileNotGoodException &e)
3241 catch(std::exception &e)
3248 Make sure file exists
3251 std::string blockfilename = getBlockFilename(blockpos);
3252 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3258 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3259 return getBlockNoCreateNoEx(blockpos);
3262 void ServerMap::PrintInfo(std::ostream &out)
3273 ClientMap::ClientMap(
3275 MapDrawControl &control,
3276 scene::ISceneNode* parent,
3277 scene::ISceneManager* mgr,
3281 scene::ISceneNode(parent, mgr, id),
3284 m_camera_position(0,0,0),
3285 m_camera_direction(0,0,1)
3287 m_camera_mutex.Init();
3288 assert(m_camera_mutex.IsInitialized());
3290 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3291 BS*1000000,BS*1000000,BS*1000000);
3294 ClientMap::~ClientMap()
3296 /*JMutexAutoLock lock(mesh_mutex);
3305 MapSector * ClientMap::emergeSector(v2s16 p2d)
3307 DSTACK(__FUNCTION_NAME);
3308 // Check that it doesn't exist already
3310 return getSectorNoGenerate(p2d);
3312 catch(InvalidPositionException &e)
3317 ClientMapSector *sector = new ClientMapSector(this, p2d);
3320 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3321 m_sectors.insert(p2d, sector);
3328 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3330 DSTACK(__FUNCTION_NAME);
3331 ClientMapSector *sector = NULL;
3333 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3335 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3339 sector = (ClientMapSector*)n->getValue();
3340 assert(sector->getId() == MAPSECTOR_CLIENT);
3344 sector = new ClientMapSector(this, p2d);
3346 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3347 m_sectors.insert(p2d, sector);
3351 sector->deSerialize(is);
3355 void ClientMap::OnRegisterSceneNode()
3359 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3360 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3363 ISceneNode::OnRegisterSceneNode();
3366 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3368 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3369 DSTACK(__FUNCTION_NAME);
3371 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3374 This is called two times per frame, reset on the non-transparent one
3376 if(pass == scene::ESNRP_SOLID)
3378 m_last_drawn_sectors.clear();
3382 Get time for measuring timeout.
3384 Measuring time is very useful for long delays when the
3385 machine is swapping a lot.
3387 int time1 = time(0);
3389 //u32 daynight_ratio = m_client->getDayNightRatio();
3391 m_camera_mutex.Lock();
3392 v3f camera_position = m_camera_position;
3393 v3f camera_direction = m_camera_direction;
3394 m_camera_mutex.Unlock();
3397 Get all blocks and draw all visible ones
3400 v3s16 cam_pos_nodes(
3401 camera_position.X / BS,
3402 camera_position.Y / BS,
3403 camera_position.Z / BS);
3405 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3407 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3408 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3410 // Take a fair amount as we will be dropping more out later
3412 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3413 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3414 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3416 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3417 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3418 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3420 u32 vertex_count = 0;
3422 // For limiting number of mesh updates per frame
3423 u32 mesh_update_count = 0;
3425 u32 blocks_would_have_drawn = 0;
3426 u32 blocks_drawn = 0;
3428 int timecheck_counter = 0;
3429 core::map<v2s16, MapSector*>::Iterator si;
3430 si = m_sectors.getIterator();
3431 for(; si.atEnd() == false; si++)
3434 timecheck_counter++;
3435 if(timecheck_counter > 50)
3437 timecheck_counter = 0;
3438 int time2 = time(0);
3439 if(time2 > time1 + 4)
3441 dstream<<"ClientMap::renderMap(): "
3442 "Rendering takes ages, returning."
3449 MapSector *sector = si.getNode()->getValue();
3450 v2s16 sp = sector->getPos();
3452 if(m_control.range_all == false)
3454 if(sp.X < p_blocks_min.X
3455 || sp.X > p_blocks_max.X
3456 || sp.Y < p_blocks_min.Z
3457 || sp.Y > p_blocks_max.Z)
3461 core::list< MapBlock * > sectorblocks;
3462 sector->getBlocks(sectorblocks);
3468 u32 sector_blocks_drawn = 0;
3470 core::list< MapBlock * >::Iterator i;
3471 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3473 MapBlock *block = *i;
3476 Compare block position to camera position, skip
3477 if not seen on display
3480 float range = 100000 * BS;
3481 if(m_control.range_all == false)
3482 range = m_control.wanted_range * BS;
3485 if(isBlockInSight(block->getPos(), camera_position,
3486 camera_direction, range, &d) == false)
3491 // Okay, this block will be drawn. Reset usage timer.
3492 block->resetUsageTimer();
3494 // This is ugly (spherical distance limit?)
3495 /*if(m_control.range_all == false &&
3496 d - 0.5*BS*MAP_BLOCKSIZE > range)
3501 Update expired mesh (used for day/night change)
3503 It doesn't work exactly like it should now with the
3504 tasked mesh update but whatever.
3507 bool mesh_expired = false;
3510 JMutexAutoLock lock(block->mesh_mutex);
3512 mesh_expired = block->getMeshExpired();
3514 // Mesh has not been expired and there is no mesh:
3515 // block has no content
3516 if(block->mesh == NULL && mesh_expired == false)
3520 f32 faraway = BS*50;
3521 //f32 faraway = m_control.wanted_range * BS;
3524 This has to be done with the mesh_mutex unlocked
3526 // Pretty random but this should work somewhat nicely
3527 if(mesh_expired && (
3528 (mesh_update_count < 3
3529 && (d < faraway || mesh_update_count < 2)
3532 (m_control.range_all && mesh_update_count < 20)
3535 /*if(mesh_expired && mesh_update_count < 6
3536 && (d < faraway || mesh_update_count < 3))*/
3538 mesh_update_count++;
3540 // Mesh has been expired: generate new mesh
3541 //block->updateMesh(daynight_ratio);
3542 m_client->addUpdateMeshTask(block->getPos());
3544 mesh_expired = false;
3549 Draw the faces of the block
3552 JMutexAutoLock lock(block->mesh_mutex);
3554 scene::SMesh *mesh = block->mesh;
3559 blocks_would_have_drawn++;
3560 if(blocks_drawn >= m_control.wanted_max_blocks
3561 && m_control.range_all == false
3562 && d > m_control.wanted_min_range * BS)
3566 sector_blocks_drawn++;
3568 u32 c = mesh->getMeshBufferCount();
3570 for(u32 i=0; i<c; i++)
3572 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3573 const video::SMaterial& material = buf->getMaterial();
3574 video::IMaterialRenderer* rnd =
3575 driver->getMaterialRenderer(material.MaterialType);
3576 bool transparent = (rnd && rnd->isTransparent());
3577 // Render transparent on transparent pass and likewise.
3578 if(transparent == is_transparent_pass)
3581 This *shouldn't* hurt too much because Irrlicht
3582 doesn't change opengl textures if the old
3583 material is set again.
3585 driver->setMaterial(buf->getMaterial());
3586 driver->drawMeshBuffer(buf);
3587 vertex_count += buf->getVertexCount();
3591 } // foreach sectorblocks
3593 if(sector_blocks_drawn != 0)
3595 m_last_drawn_sectors[sp] = true;
3599 m_control.blocks_drawn = blocks_drawn;
3600 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3602 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3603 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3606 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3607 core::map<v3s16, MapBlock*> *affected_blocks)
3609 bool changed = false;
3611 Add it to all blocks touching it
3614 v3s16(0,0,0), // this
3615 v3s16(0,0,1), // back
3616 v3s16(0,1,0), // top
3617 v3s16(1,0,0), // right
3618 v3s16(0,0,-1), // front
3619 v3s16(0,-1,0), // bottom
3620 v3s16(-1,0,0), // left
3622 for(u16 i=0; i<7; i++)
3624 v3s16 p2 = p + dirs[i];
3625 // Block position of neighbor (or requested) node
3626 v3s16 blockpos = getNodeBlockPos(p2);
3627 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3628 if(blockref == NULL)
3630 // Relative position of requested node
3631 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3632 if(blockref->setTempMod(relpos, mod))
3637 if(changed && affected_blocks!=NULL)
3639 for(u16 i=0; i<7; i++)
3641 v3s16 p2 = p + dirs[i];
3642 // Block position of neighbor (or requested) node
3643 v3s16 blockpos = getNodeBlockPos(p2);
3644 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3645 if(blockref == NULL)
3647 affected_blocks->insert(blockpos, blockref);
3653 bool ClientMap::clearTempMod(v3s16 p,
3654 core::map<v3s16, MapBlock*> *affected_blocks)
3656 bool changed = false;
3658 v3s16(0,0,0), // this
3659 v3s16(0,0,1), // back
3660 v3s16(0,1,0), // top
3661 v3s16(1,0,0), // right
3662 v3s16(0,0,-1), // front
3663 v3s16(0,-1,0), // bottom
3664 v3s16(-1,0,0), // left
3666 for(u16 i=0; i<7; i++)
3668 v3s16 p2 = p + dirs[i];
3669 // Block position of neighbor (or requested) node
3670 v3s16 blockpos = getNodeBlockPos(p2);
3671 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3672 if(blockref == NULL)
3674 // Relative position of requested node
3675 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3676 if(blockref->clearTempMod(relpos))
3681 if(changed && affected_blocks!=NULL)
3683 for(u16 i=0; i<7; i++)
3685 v3s16 p2 = p + dirs[i];
3686 // Block position of neighbor (or requested) node
3687 v3s16 blockpos = getNodeBlockPos(p2);
3688 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3689 if(blockref == NULL)
3691 affected_blocks->insert(blockpos, blockref);
3697 void ClientMap::expireMeshes(bool only_daynight_diffed)
3699 TimeTaker timer("expireMeshes()");
3701 core::map<v2s16, MapSector*>::Iterator si;
3702 si = m_sectors.getIterator();
3703 for(; si.atEnd() == false; si++)
3705 MapSector *sector = si.getNode()->getValue();
3707 core::list< MapBlock * > sectorblocks;
3708 sector->getBlocks(sectorblocks);
3710 core::list< MapBlock * >::Iterator i;
3711 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3713 MapBlock *block = *i;
3715 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3721 JMutexAutoLock lock(block->mesh_mutex);
3722 if(block->mesh != NULL)
3724 /*block->mesh->drop();
3725 block->mesh = NULL;*/
3726 block->setMeshExpired(true);
3733 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3735 assert(mapType() == MAPTYPE_CLIENT);
3738 v3s16 p = blockpos + v3s16(0,0,0);
3739 MapBlock *b = getBlockNoCreate(p);
3740 b->updateMesh(daynight_ratio);
3741 //b->setMeshExpired(true);
3743 catch(InvalidPositionException &e){}
3746 v3s16 p = blockpos + v3s16(-1,0,0);
3747 MapBlock *b = getBlockNoCreate(p);
3748 b->updateMesh(daynight_ratio);
3749 //b->setMeshExpired(true);
3751 catch(InvalidPositionException &e){}
3753 v3s16 p = blockpos + v3s16(0,-1,0);
3754 MapBlock *b = getBlockNoCreate(p);
3755 b->updateMesh(daynight_ratio);
3756 //b->setMeshExpired(true);
3758 catch(InvalidPositionException &e){}
3760 v3s16 p = blockpos + v3s16(0,0,-1);
3761 MapBlock *b = getBlockNoCreate(p);
3762 b->updateMesh(daynight_ratio);
3763 //b->setMeshExpired(true);
3765 catch(InvalidPositionException &e){}
3770 Update mesh of block in which the node is, and if the node is at the
3771 leading edge, update the appropriate leading blocks too.
3773 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3781 v3s16 blockposes[4];
3782 for(u32 i=0; i<4; i++)
3784 v3s16 np = nodepos + dirs[i];
3785 blockposes[i] = getNodeBlockPos(np);
3786 // Don't update mesh of block if it has been done already
3787 bool already_updated = false;
3788 for(u32 j=0; j<i; j++)
3790 if(blockposes[j] == blockposes[i])
3792 already_updated = true;
3799 MapBlock *b = getBlockNoCreate(blockposes[i]);
3800 b->updateMesh(daynight_ratio);
3805 void ClientMap::PrintInfo(std::ostream &out)
3816 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3821 MapVoxelManipulator::~MapVoxelManipulator()
3823 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3827 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3829 TimeTaker timer1("emerge", &emerge_time);
3831 // Units of these are MapBlocks
3832 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3833 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3835 VoxelArea block_area_nodes
3836 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3838 addArea(block_area_nodes);
3840 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3841 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3842 for(s32 x=p_min.X; x<=p_max.X; x++)
3845 core::map<v3s16, bool>::Node *n;
3846 n = m_loaded_blocks.find(p);
3850 bool block_data_inexistent = false;
3853 TimeTaker timer1("emerge load", &emerge_load_time);
3855 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3856 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3859 dstream<<std::endl;*/
3861 MapBlock *block = m_map->getBlockNoCreate(p);
3862 if(block->isDummy())
3863 block_data_inexistent = true;
3865 block->copyTo(*this);
3867 catch(InvalidPositionException &e)
3869 block_data_inexistent = true;
3872 if(block_data_inexistent)
3874 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3875 // Fill with VOXELFLAG_INEXISTENT
3876 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3877 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3879 s32 i = m_area.index(a.MinEdge.X,y,z);
3880 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3884 m_loaded_blocks.insert(p, !block_data_inexistent);
3887 //dstream<<"emerge done"<<std::endl;
3891 SUGG: Add an option to only update eg. water and air nodes.
3892 This will make it interfere less with important stuff if
3895 void MapVoxelManipulator::blitBack
3896 (core::map<v3s16, MapBlock*> & modified_blocks)
3898 if(m_area.getExtent() == v3s16(0,0,0))
3901 //TimeTaker timer1("blitBack");
3903 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3904 <<m_loaded_blocks.size()<<std::endl;*/
3907 Initialize block cache
3909 v3s16 blockpos_last;
3910 MapBlock *block = NULL;
3911 bool block_checked_in_modified = false;
3913 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3914 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3915 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3919 u8 f = m_flags[m_area.index(p)];
3920 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3923 MapNode &n = m_data[m_area.index(p)];
3925 v3s16 blockpos = getNodeBlockPos(p);
3930 if(block == NULL || blockpos != blockpos_last){
3931 block = m_map->getBlockNoCreate(blockpos);
3932 blockpos_last = blockpos;
3933 block_checked_in_modified = false;
3936 // Calculate relative position in block
3937 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3939 // Don't continue if nothing has changed here
3940 if(block->getNode(relpos) == n)
3943 //m_map->setNode(m_area.MinEdge + p, n);
3944 block->setNode(relpos, n);
3947 Make sure block is in modified_blocks
3949 if(block_checked_in_modified == false)
3951 modified_blocks[blockpos] = block;
3952 block_checked_in_modified = true;
3955 catch(InvalidPositionException &e)
3961 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3962 MapVoxelManipulator(map),
3963 m_create_area(false)
3967 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3971 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3973 // Just create the area so that it can be pointed to
3974 VoxelManipulator::emerge(a, caller_id);
3977 void ManualMapVoxelManipulator::initialEmerge(
3978 v3s16 blockpos_min, v3s16 blockpos_max)
3980 TimeTaker timer1("initialEmerge", &emerge_time);
3982 // Units of these are MapBlocks
3983 v3s16 p_min = blockpos_min;
3984 v3s16 p_max = blockpos_max;
3986 VoxelArea block_area_nodes
3987 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3989 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3992 dstream<<"initialEmerge: area: ";
3993 block_area_nodes.print(dstream);
3994 dstream<<" ("<<size_MB<<"MB)";
3998 addArea(block_area_nodes);
4000 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4001 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4002 for(s32 x=p_min.X; x<=p_max.X; x++)
4005 core::map<v3s16, bool>::Node *n;
4006 n = m_loaded_blocks.find(p);
4010 bool block_data_inexistent = false;
4013 TimeTaker timer1("emerge load", &emerge_load_time);
4015 MapBlock *block = m_map->getBlockNoCreate(p);
4016 if(block->isDummy())
4017 block_data_inexistent = true;
4019 block->copyTo(*this);
4021 catch(InvalidPositionException &e)
4023 block_data_inexistent = true;
4026 if(block_data_inexistent)
4029 Mark area inexistent
4031 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4032 // Fill with VOXELFLAG_INEXISTENT
4033 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4034 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4036 s32 i = m_area.index(a.MinEdge.X,y,z);
4037 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4041 m_loaded_blocks.insert(p, !block_data_inexistent);
4045 void ManualMapVoxelManipulator::blitBackAll(
4046 core::map<v3s16, MapBlock*> * modified_blocks)
4048 if(m_area.getExtent() == v3s16(0,0,0))
4052 Copy data of all blocks
4054 for(core::map<v3s16, bool>::Iterator
4055 i = m_loaded_blocks.getIterator();
4056 i.atEnd() == false; i++)
4058 bool existed = i.getNode()->getValue();
4059 if(existed == false)
4061 v3s16 p = i.getNode()->getKey();
4062 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4065 dstream<<"WARNING: "<<__FUNCTION_NAME
4066 <<": got NULL block "
4067 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4072 block->copyFrom(*this);
4075 modified_blocks->insert(p, block);