3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
30 #include "nodemetadata.h"
36 SQLite format specification:
37 - Initially only replaces sectors/ and sectors2/
44 Map::Map(std::ostream &dout):
48 /*m_sector_mutex.Init();
49 assert(m_sector_mutex.IsInitialized());*/
57 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
58 for(; i.atEnd() == false; i++)
60 MapSector *sector = i.getNode()->getValue();
65 void Map::addEventReceiver(MapEventReceiver *event_receiver)
67 m_event_receivers.insert(event_receiver, false);
70 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
72 if(m_event_receivers.find(event_receiver) == NULL)
74 m_event_receivers.remove(event_receiver);
77 void Map::dispatchEvent(MapEditEvent *event)
79 for(core::map<MapEventReceiver*, bool>::Iterator
80 i = m_event_receivers.getIterator();
81 i.atEnd()==false; i++)
83 MapEventReceiver* event_receiver = i.getNode()->getKey();
84 event_receiver->onMapEditEvent(event);
88 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
90 if(m_sector_cache != NULL && p == m_sector_cache_p){
91 MapSector * sector = m_sector_cache;
95 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
100 MapSector *sector = n->getValue();
102 // Cache the last result
103 m_sector_cache_p = p;
104 m_sector_cache = sector;
109 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
111 return getSectorNoGenerateNoExNoLock(p);
114 MapSector * Map::getSectorNoGenerate(v2s16 p)
116 MapSector *sector = getSectorNoGenerateNoEx(p);
118 throw InvalidPositionException();
123 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerateNoEx(p2d);
129 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
133 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
135 MapBlock *block = getBlockNoCreateNoEx(p3d);
137 throw InvalidPositionException();
141 bool Map::isNodeUnderground(v3s16 p)
143 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock * block = getBlockNoCreate(blockpos);
146 return block->getIsUnderground();
148 catch(InvalidPositionException &e)
154 bool Map::isValidPosition(v3s16 p)
156 v3s16 blockpos = getNodeBlockPos(p);
157 MapBlock *block = getBlockNoCreate(blockpos);
158 return (block != NULL);
161 // Returns a CONTENT_IGNORE node if not found
162 MapNode Map::getNodeNoEx(v3s16 p)
164 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock *block = getBlockNoCreateNoEx(blockpos);
167 return MapNode(CONTENT_IGNORE);
168 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
169 return block->getNodeNoCheck(relpos);
172 // throws InvalidPositionException if not found
173 MapNode Map::getNode(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock *block = getBlockNoCreateNoEx(blockpos);
178 throw InvalidPositionException();
179 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
180 return block->getNodeNoCheck(relpos);
183 // throws InvalidPositionException if not found
184 void Map::setNode(v3s16 p, MapNode & n)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreate(blockpos);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 block->setNodeNoCheck(relpos, n);
194 Goes recursively through the neighbours of the node.
196 Alters only transparent nodes.
198 If the lighting of the neighbour is lower than the lighting of
199 the node was (before changing it to 0 at the step before), the
200 lighting of the neighbour is set to 0 and then the same stuff
201 repeats for the neighbour.
203 The ending nodes of the routine are stored in light_sources.
204 This is useful when a light is removed. In such case, this
205 routine can be called for the light node and then again for
206 light_sources to re-light the area without the removed light.
208 values of from_nodes are lighting values.
210 void Map::unspreadLight(enum LightBank bank,
211 core::map<v3s16, u8> & from_nodes,
212 core::map<v3s16, bool> & light_sources,
213 core::map<v3s16, MapBlock*> & modified_blocks)
216 v3s16(0,0,1), // back
218 v3s16(1,0,0), // right
219 v3s16(0,0,-1), // front
220 v3s16(0,-1,0), // bottom
221 v3s16(-1,0,0), // left
224 if(from_nodes.size() == 0)
227 u32 blockchangecount = 0;
229 core::map<v3s16, u8> unlighted_nodes;
230 core::map<v3s16, u8>::Iterator j;
231 j = from_nodes.getIterator();
234 Initialize block cache
237 MapBlock *block = NULL;
238 // Cache this a bit, too
239 bool block_checked_in_modified = false;
241 for(; j.atEnd() == false; j++)
243 v3s16 pos = j.getNode()->getKey();
244 v3s16 blockpos = getNodeBlockPos(pos);
246 // Only fetch a new block if the block position has changed
248 if(block == NULL || blockpos != blockpos_last){
249 block = getBlockNoCreate(blockpos);
250 blockpos_last = blockpos;
252 block_checked_in_modified = false;
256 catch(InvalidPositionException &e)
264 // Calculate relative position in block
265 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
267 // Get node straight from the block
268 MapNode n = block->getNode(relpos);
270 u8 oldlight = j.getNode()->getValue();
272 // Loop through 6 neighbors
273 for(u16 i=0; i<6; i++)
275 // Get the position of the neighbor node
276 v3s16 n2pos = pos + dirs[i];
278 // Get the block where the node is located
279 v3s16 blockpos = getNodeBlockPos(n2pos);
283 // Only fetch a new block if the block position has changed
285 if(block == NULL || blockpos != blockpos_last){
286 block = getBlockNoCreate(blockpos);
287 blockpos_last = blockpos;
289 block_checked_in_modified = false;
293 catch(InvalidPositionException &e)
298 // Calculate relative position in block
299 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
300 // Get node straight from the block
301 MapNode n2 = block->getNode(relpos);
303 bool changed = false;
305 //TODO: Optimize output by optimizing light_sources?
308 If the neighbor is dimmer than what was specified
309 as oldlight (the light of the previous node)
311 if(n2.getLight(bank) < oldlight)
314 And the neighbor is transparent and it has some light
316 if(n2.light_propagates() && n2.getLight(bank) != 0)
319 Set light to 0 and add to queue
322 u8 current_light = n2.getLight(bank);
323 n2.setLight(bank, 0);
324 block->setNode(relpos, n2);
326 unlighted_nodes.insert(n2pos, current_light);
330 Remove from light_sources if it is there
331 NOTE: This doesn't happen nearly at all
333 /*if(light_sources.find(n2pos))
335 std::cout<<"Removed from light_sources"<<std::endl;
336 light_sources.remove(n2pos);
341 if(light_sources.find(n2pos) != NULL)
342 light_sources.remove(n2pos);*/
345 light_sources.insert(n2pos, true);
348 // Add to modified_blocks
349 if(changed == true && block_checked_in_modified == false)
351 // If the block is not found in modified_blocks, add.
352 if(modified_blocks.find(blockpos) == NULL)
354 modified_blocks.insert(blockpos, block);
356 block_checked_in_modified = true;
359 catch(InvalidPositionException &e)
366 /*dstream<<"unspreadLight(): Changed block "
367 <<blockchangecount<<" times"
368 <<" for "<<from_nodes.size()<<" nodes"
371 if(unlighted_nodes.size() > 0)
372 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
376 A single-node wrapper of the above
378 void Map::unLightNeighbors(enum LightBank bank,
379 v3s16 pos, u8 lightwas,
380 core::map<v3s16, bool> & light_sources,
381 core::map<v3s16, MapBlock*> & modified_blocks)
383 core::map<v3s16, u8> from_nodes;
384 from_nodes.insert(pos, lightwas);
386 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
390 Lights neighbors of from_nodes, collects all them and then
393 void Map::spreadLight(enum LightBank bank,
394 core::map<v3s16, bool> & from_nodes,
395 core::map<v3s16, MapBlock*> & modified_blocks)
397 const v3s16 dirs[6] = {
398 v3s16(0,0,1), // back
400 v3s16(1,0,0), // right
401 v3s16(0,0,-1), // front
402 v3s16(0,-1,0), // bottom
403 v3s16(-1,0,0), // left
406 if(from_nodes.size() == 0)
409 u32 blockchangecount = 0;
411 core::map<v3s16, bool> lighted_nodes;
412 core::map<v3s16, bool>::Iterator j;
413 j = from_nodes.getIterator();
416 Initialize block cache
419 MapBlock *block = NULL;
420 // Cache this a bit, too
421 bool block_checked_in_modified = false;
423 for(; j.atEnd() == false; j++)
424 //for(; j != from_nodes.end(); j++)
426 v3s16 pos = j.getNode()->getKey();
428 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
429 v3s16 blockpos = getNodeBlockPos(pos);
431 // Only fetch a new block if the block position has changed
433 if(block == NULL || blockpos != blockpos_last){
434 block = getBlockNoCreate(blockpos);
435 blockpos_last = blockpos;
437 block_checked_in_modified = false;
441 catch(InvalidPositionException &e)
449 // Calculate relative position in block
450 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
452 // Get node straight from the block
453 MapNode n = block->getNode(relpos);
455 u8 oldlight = n.getLight(bank);
456 u8 newlight = diminish_light(oldlight);
458 // Loop through 6 neighbors
459 for(u16 i=0; i<6; i++){
460 // Get the position of the neighbor node
461 v3s16 n2pos = pos + dirs[i];
463 // Get the block where the node is located
464 v3s16 blockpos = getNodeBlockPos(n2pos);
468 // Only fetch a new block if the block position has changed
470 if(block == NULL || blockpos != blockpos_last){
471 block = getBlockNoCreate(blockpos);
472 blockpos_last = blockpos;
474 block_checked_in_modified = false;
478 catch(InvalidPositionException &e)
483 // Calculate relative position in block
484 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
485 // Get node straight from the block
486 MapNode n2 = block->getNode(relpos);
488 bool changed = false;
490 If the neighbor is brighter than the current node,
491 add to list (it will light up this node on its turn)
493 if(n2.getLight(bank) > undiminish_light(oldlight))
495 lighted_nodes.insert(n2pos, true);
496 //lighted_nodes.push_back(n2pos);
500 If the neighbor is dimmer than how much light this node
501 would spread on it, add to list
503 if(n2.getLight(bank) < newlight)
505 if(n2.light_propagates())
507 n2.setLight(bank, newlight);
508 block->setNode(relpos, n2);
509 lighted_nodes.insert(n2pos, true);
510 //lighted_nodes.push_back(n2pos);
515 // Add to modified_blocks
516 if(changed == true && block_checked_in_modified == false)
518 // If the block is not found in modified_blocks, add.
519 if(modified_blocks.find(blockpos) == NULL)
521 modified_blocks.insert(blockpos, block);
523 block_checked_in_modified = true;
526 catch(InvalidPositionException &e)
533 /*dstream<<"spreadLight(): Changed block "
534 <<blockchangecount<<" times"
535 <<" for "<<from_nodes.size()<<" nodes"
538 if(lighted_nodes.size() > 0)
539 spreadLight(bank, lighted_nodes, modified_blocks);
543 A single-node source variation of the above.
545 void Map::lightNeighbors(enum LightBank bank,
547 core::map<v3s16, MapBlock*> & modified_blocks)
549 core::map<v3s16, bool> from_nodes;
550 from_nodes.insert(pos, true);
551 spreadLight(bank, from_nodes, modified_blocks);
554 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
557 v3s16(0,0,1), // back
559 v3s16(1,0,0), // right
560 v3s16(0,0,-1), // front
561 v3s16(0,-1,0), // bottom
562 v3s16(-1,0,0), // left
565 u8 brightest_light = 0;
566 v3s16 brightest_pos(0,0,0);
567 bool found_something = false;
569 // Loop through 6 neighbors
570 for(u16 i=0; i<6; i++){
571 // Get the position of the neighbor node
572 v3s16 n2pos = p + dirs[i];
577 catch(InvalidPositionException &e)
581 if(n2.getLight(bank) > brightest_light || found_something == false){
582 brightest_light = n2.getLight(bank);
583 brightest_pos = n2pos;
584 found_something = true;
588 if(found_something == false)
589 throw InvalidPositionException();
591 return brightest_pos;
595 Propagates sunlight down from a node.
596 Starting point gets sunlight.
598 Returns the lowest y value of where the sunlight went.
600 Mud is turned into grass in where the sunlight stops.
602 s16 Map::propagateSunlight(v3s16 start,
603 core::map<v3s16, MapBlock*> & modified_blocks)
608 v3s16 pos(start.X, y, start.Z);
610 v3s16 blockpos = getNodeBlockPos(pos);
613 block = getBlockNoCreate(blockpos);
615 catch(InvalidPositionException &e)
620 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
621 MapNode n = block->getNode(relpos);
623 if(n.sunlight_propagates())
625 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
626 block->setNode(relpos, n);
628 modified_blocks.insert(blockpos, block);
632 /*// Turn mud into grass
633 if(n.getContent() == CONTENT_MUD)
635 n.setContent(CONTENT_GRASS);
636 block->setNode(relpos, n);
637 modified_blocks.insert(blockpos, block);
640 // Sunlight goes no further
647 void Map::updateLighting(enum LightBank bank,
648 core::map<v3s16, MapBlock*> & a_blocks,
649 core::map<v3s16, MapBlock*> & modified_blocks)
651 /*m_dout<<DTIME<<"Map::updateLighting(): "
652 <<a_blocks.size()<<" blocks."<<std::endl;*/
654 //TimeTaker timer("updateLighting");
658 //u32 count_was = modified_blocks.size();
660 core::map<v3s16, MapBlock*> blocks_to_update;
662 core::map<v3s16, bool> light_sources;
664 core::map<v3s16, u8> unlight_from;
666 core::map<v3s16, MapBlock*>::Iterator i;
667 i = a_blocks.getIterator();
668 for(; i.atEnd() == false; i++)
670 MapBlock *block = i.getNode()->getValue();
674 // Don't bother with dummy blocks.
678 v3s16 pos = block->getPos();
679 modified_blocks.insert(pos, block);
681 blocks_to_update.insert(pos, block);
684 Clear all light from block
686 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
687 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
688 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
693 MapNode n = block->getNode(v3s16(x,y,z));
694 u8 oldlight = n.getLight(bank);
696 block->setNode(v3s16(x,y,z), n);
698 // Collect borders for unlighting
699 if(x==0 || x == MAP_BLOCKSIZE-1
700 || y==0 || y == MAP_BLOCKSIZE-1
701 || z==0 || z == MAP_BLOCKSIZE-1)
703 v3s16 p_map = p + v3s16(
706 MAP_BLOCKSIZE*pos.Z);
707 unlight_from.insert(p_map, oldlight);
710 catch(InvalidPositionException &e)
713 This would happen when dealing with a
717 dstream<<"updateLighting(): InvalidPositionException"
722 if(bank == LIGHTBANK_DAY)
724 bool bottom_valid = block->propagateSunlight(light_sources);
726 // If bottom is valid, we're done.
730 else if(bank == LIGHTBANK_NIGHT)
732 // For night lighting, sunlight is not propagated
737 // Invalid lighting bank
741 /*dstream<<"Bottom for sunlight-propagated block ("
742 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
745 // Bottom sunlight is not valid; get the block and loop to it
749 block = getBlockNoCreate(pos);
751 catch(InvalidPositionException &e)
760 Enable this to disable proper lighting for speeding up map
761 generation for testing or whatever
764 //if(g_settings.get(""))
766 core::map<v3s16, MapBlock*>::Iterator i;
767 i = blocks_to_update.getIterator();
768 for(; i.atEnd() == false; i++)
770 MapBlock *block = i.getNode()->getValue();
771 v3s16 p = block->getPos();
772 block->setLightingExpired(false);
780 TimeTaker timer("unspreadLight");
781 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
786 u32 diff = modified_blocks.size() - count_was;
787 count_was = modified_blocks.size();
788 dstream<<"unspreadLight modified "<<diff<<std::endl;
792 TimeTaker timer("spreadLight");
793 spreadLight(bank, light_sources, modified_blocks);
798 u32 diff = modified_blocks.size() - count_was;
799 count_was = modified_blocks.size();
800 dstream<<"spreadLight modified "<<diff<<std::endl;
805 //MapVoxelManipulator vmanip(this);
807 // Make a manual voxel manipulator and load all the blocks
808 // that touch the requested blocks
809 ManualMapVoxelManipulator vmanip(this);
810 core::map<v3s16, MapBlock*>::Iterator i;
811 i = blocks_to_update.getIterator();
812 for(; i.atEnd() == false; i++)
814 MapBlock *block = i.getNode()->getValue();
815 v3s16 p = block->getPos();
817 // Add all surrounding blocks
818 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
821 Add all surrounding blocks that have up-to-date lighting
822 NOTE: This doesn't quite do the job (not everything
823 appropriate is lighted)
825 /*for(s16 z=-1; z<=1; z++)
826 for(s16 y=-1; y<=1; y++)
827 for(s16 x=-1; x<=1; x++)
830 MapBlock *block = getBlockNoCreateNoEx(p);
835 if(block->getLightingExpired())
837 vmanip.initialEmerge(p, p);
840 // Lighting of block will be updated completely
841 block->setLightingExpired(false);
845 //TimeTaker timer("unSpreadLight");
846 vmanip.unspreadLight(bank, unlight_from, light_sources);
849 //TimeTaker timer("spreadLight");
850 vmanip.spreadLight(bank, light_sources);
853 //TimeTaker timer("blitBack");
854 vmanip.blitBack(modified_blocks);
856 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
860 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
863 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
864 core::map<v3s16, MapBlock*> & modified_blocks)
866 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
867 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
870 Update information about whether day and night light differ
872 for(core::map<v3s16, MapBlock*>::Iterator
873 i = modified_blocks.getIterator();
874 i.atEnd() == false; i++)
876 MapBlock *block = i.getNode()->getValue();
877 block->updateDayNightDiff();
883 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
884 core::map<v3s16, MapBlock*> &modified_blocks)
887 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
888 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
891 From this node to nodes underneath:
892 If lighting is sunlight (1.0), unlight neighbours and
897 v3s16 toppos = p + v3s16(0,1,0);
898 v3s16 bottompos = p + v3s16(0,-1,0);
900 bool node_under_sunlight = true;
901 core::map<v3s16, bool> light_sources;
904 If there is a node at top and it doesn't have sunlight,
905 there has not been any sunlight going down.
907 Otherwise there probably is.
910 MapNode topnode = getNode(toppos);
912 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
913 node_under_sunlight = false;
915 catch(InvalidPositionException &e)
921 If the new node is solid and there is grass below, change it to mud
923 if(content_features(n).walkable == true)
926 MapNode bottomnode = getNode(bottompos);
928 if(bottomnode.getContent() == CONTENT_GRASS
929 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
931 bottomnode.setContent(CONTENT_MUD);
932 setNode(bottompos, bottomnode);
935 catch(InvalidPositionException &e)
943 If the new node is mud and it is under sunlight, change it
946 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
948 n.setContent(CONTENT_GRASS);
953 Remove all light that has come out of this node
956 enum LightBank banks[] =
961 for(s32 i=0; i<2; i++)
963 enum LightBank bank = banks[i];
965 u8 lightwas = getNode(p).getLight(bank);
967 // Add the block of the added node to modified_blocks
968 v3s16 blockpos = getNodeBlockPos(p);
969 MapBlock * block = getBlockNoCreate(blockpos);
970 assert(block != NULL);
971 modified_blocks.insert(blockpos, block);
973 assert(isValidPosition(p));
975 // Unlight neighbours of node.
976 // This means setting light of all consequent dimmer nodes
978 // This also collects the nodes at the border which will spread
979 // light again into this.
980 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
986 If node lets sunlight through and is under sunlight, it has
989 if(node_under_sunlight && content_features(n).sunlight_propagates)
991 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
995 Set the node on the map
1004 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1007 NodeMetadata *meta = meta_proto->clone();
1008 setNodeMetadata(p, meta);
1012 If node is under sunlight and doesn't let sunlight through,
1013 take all sunlighted nodes under it and clear light from them
1014 and from where the light has been spread.
1015 TODO: This could be optimized by mass-unlighting instead
1018 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1022 //m_dout<<DTIME<<"y="<<y<<std::endl;
1023 v3s16 n2pos(p.X, y, p.Z);
1027 n2 = getNode(n2pos);
1029 catch(InvalidPositionException &e)
1034 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1036 unLightNeighbors(LIGHTBANK_DAY,
1037 n2pos, n2.getLight(LIGHTBANK_DAY),
1038 light_sources, modified_blocks);
1039 n2.setLight(LIGHTBANK_DAY, 0);
1047 for(s32 i=0; i<2; i++)
1049 enum LightBank bank = banks[i];
1052 Spread light from all nodes that might be capable of doing so
1054 spreadLight(bank, light_sources, modified_blocks);
1058 Update information about whether day and night light differ
1060 for(core::map<v3s16, MapBlock*>::Iterator
1061 i = modified_blocks.getIterator();
1062 i.atEnd() == false; i++)
1064 MapBlock *block = i.getNode()->getValue();
1065 block->updateDayNightDiff();
1069 Add neighboring liquid nodes and the node itself if it is
1070 liquid (=water node was added) to transform queue.
1073 v3s16(0,0,0), // self
1074 v3s16(0,0,1), // back
1075 v3s16(0,1,0), // top
1076 v3s16(1,0,0), // right
1077 v3s16(0,0,-1), // front
1078 v3s16(0,-1,0), // bottom
1079 v3s16(-1,0,0), // left
1081 for(u16 i=0; i<7; i++)
1086 v3s16 p2 = p + dirs[i];
1088 MapNode n2 = getNode(p2);
1089 if(content_liquid(n2.getContent()) || n2.getContent() == 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 content_t replace_material = CONTENT_AIR;
1117 If there is a node at top and it doesn't have sunlight,
1118 there will be no sunlight going down.
1121 MapNode topnode = getNode(toppos);
1123 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1124 node_under_sunlight = false;
1126 catch(InvalidPositionException &e)
1130 core::map<v3s16, bool> light_sources;
1132 enum LightBank banks[] =
1137 for(s32 i=0; i<2; i++)
1139 enum LightBank bank = banks[i];
1142 Unlight neighbors (in case the node is a light source)
1144 unLightNeighbors(bank, p,
1145 getNode(p).getLight(bank),
1146 light_sources, modified_blocks);
1150 Remove node metadata
1153 removeNodeMetadata(p);
1157 This also clears the lighting.
1161 n.setContent(replace_material);
1164 for(s32 i=0; i<2; i++)
1166 enum LightBank bank = banks[i];
1169 Recalculate lighting
1171 spreadLight(bank, light_sources, modified_blocks);
1174 // Add the block of the removed node to modified_blocks
1175 v3s16 blockpos = getNodeBlockPos(p);
1176 MapBlock * block = getBlockNoCreate(blockpos);
1177 assert(block != NULL);
1178 modified_blocks.insert(blockpos, block);
1181 If the removed node was under sunlight, propagate the
1182 sunlight down from it and then light all neighbors
1183 of the propagated blocks.
1185 if(node_under_sunlight)
1187 s16 ybottom = propagateSunlight(p, modified_blocks);
1188 /*m_dout<<DTIME<<"Node was under sunlight. "
1189 "Propagating sunlight";
1190 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1192 for(; y >= ybottom; y--)
1194 v3s16 p2(p.X, y, p.Z);
1195 /*m_dout<<DTIME<<"lighting neighbors of node ("
1196 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1198 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1203 // Set the lighting of this node to 0
1204 // TODO: Is this needed? Lighting is cleared up there already.
1206 MapNode n = getNode(p);
1207 n.setLight(LIGHTBANK_DAY, 0);
1210 catch(InvalidPositionException &e)
1216 for(s32 i=0; i<2; i++)
1218 enum LightBank bank = banks[i];
1220 // Get the brightest neighbour node and propagate light from it
1221 v3s16 n2p = getBrightestNeighbour(bank, p);
1223 MapNode n2 = getNode(n2p);
1224 lightNeighbors(bank, n2p, modified_blocks);
1226 catch(InvalidPositionException &e)
1232 Update information about whether day and night light differ
1234 for(core::map<v3s16, MapBlock*>::Iterator
1235 i = modified_blocks.getIterator();
1236 i.atEnd() == false; i++)
1238 MapBlock *block = i.getNode()->getValue();
1239 block->updateDayNightDiff();
1243 Add neighboring liquid nodes and this node to transform queue.
1244 (it's vital for the node itself to get updated last.)
1247 v3s16(0,0,1), // back
1248 v3s16(0,1,0), // top
1249 v3s16(1,0,0), // right
1250 v3s16(0,0,-1), // front
1251 v3s16(0,-1,0), // bottom
1252 v3s16(-1,0,0), // left
1253 v3s16(0,0,0), // self
1255 for(u16 i=0; i<7; i++)
1260 v3s16 p2 = p + dirs[i];
1262 MapNode n2 = getNode(p2);
1263 if(content_liquid(n2.getContent()) || n2.getContent() == 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 // list of nodes that due to viscosity have not reached their max level height
1566 UniqueQueue<v3s16> must_reflow;
1568 while(m_transforming_liquid.size() != 0)
1570 // This should be done here so that it is done when continue is used
1571 if(loopcount >= initial_size * 3)
1576 Get a queued transforming liquid node
1578 v3s16 p0 = m_transforming_liquid.pop_front();
1580 MapNode n0 = getNodeNoEx(p0);
1583 Collect information about current node
1585 s8 liquid_level = -1;
1586 u8 liquid_kind = CONTENT_IGNORE;
1587 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1588 switch (liquid_type) {
1590 liquid_level = LIQUID_LEVEL_SOURCE;
1591 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1593 case LIQUID_FLOWING:
1594 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1595 liquid_kind = n0.getContent();
1598 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1599 // continue with the next node.
1600 if (n0.getContent() != CONTENT_AIR)
1602 liquid_kind = CONTENT_AIR;
1607 Collect information about the environment
1609 const v3s16 *dirs = g_6dirs;
1610 NodeNeighbor sources[6]; // surrounding sources
1611 int num_sources = 0;
1612 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1614 NodeNeighbor airs[6]; // surrounding air
1616 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1617 int num_neutrals = 0;
1618 bool flowing_down = false;
1619 for (u16 i = 0; i < 6; i++) {
1620 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1623 nt = NEIGHBOR_UPPER;
1626 nt = NEIGHBOR_LOWER;
1629 v3s16 npos = p0 + dirs[i];
1630 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1631 switch (content_features(nb.n.getContent()).liquid_type) {
1633 if (nb.n.getContent() == CONTENT_AIR) {
1634 airs[num_airs++] = nb;
1635 // if the current node is a water source the neighbor
1636 // should be enqueded for transformation regardless of whether the
1637 // current node changes or not.
1638 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1639 m_transforming_liquid.push_back(npos);
1640 // if the current node happens to be a flowing node, it will start to flow down here.
1641 if (nb.t == NEIGHBOR_LOWER) {
1642 flowing_down = true;
1645 neutrals[num_neutrals++] = nb;
1649 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1650 if (liquid_kind == CONTENT_AIR)
1651 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1652 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1653 neutrals[num_neutrals++] = nb;
1655 sources[num_sources++] = nb;
1658 case LIQUID_FLOWING:
1659 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1660 if (liquid_kind == CONTENT_AIR)
1661 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1662 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1663 neutrals[num_neutrals++] = nb;
1665 flows[num_flows++] = nb;
1666 if (nb.t == NEIGHBOR_LOWER)
1667 flowing_down = true;
1674 decide on the type (and possibly level) of the current node
1676 content_t new_node_content;
1677 s8 new_node_level = -1;
1678 s8 max_node_level = -1;
1679 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1680 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1681 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1682 // it's perfectly safe to use liquid_kind here to determine the new node content.
1683 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1684 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1685 // liquid_kind is set properly, see above
1686 new_node_content = liquid_kind;
1687 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1689 // no surrounding sources, so get the maximum level that can flow into this node
1690 for (u16 i = 0; i < num_flows; i++) {
1691 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1692 switch (flows[i].t) {
1693 case NEIGHBOR_UPPER:
1694 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1695 max_node_level = LIQUID_LEVEL_MAX;
1696 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1697 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1700 case NEIGHBOR_LOWER:
1702 case NEIGHBOR_SAME_LEVEL:
1703 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1704 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1705 max_node_level = nb_liquid_level - 1;
1711 if (max_node_level != liquid_level) {
1712 // amount to gain, limited by viscosity
1713 // must be at least 1 in absolute value
1714 s8 level_inc = max_node_level - liquid_level;
1715 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1716 if (level_inc < -viscosity || level_inc > viscosity)
1717 new_node_level = liquid_level + level_inc/viscosity;
1718 else if (level_inc < 0)
1719 new_node_level = liquid_level - 1;
1720 else if (level_inc > 0)
1721 new_node_level = liquid_level + 1;
1722 if (new_node_level != max_node_level)
1723 must_reflow.push_back(p0);
1725 new_node_level = max_node_level;
1727 if (new_node_level >= 0)
1728 new_node_content = liquid_kind;
1730 new_node_content = CONTENT_AIR;
1735 check if anything has changed. if not, just continue with the next node.
1737 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1738 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1739 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1745 update the current node
1747 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1748 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1749 // set level to last 3 bits, flowing down bit to 4th bit
1750 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1752 // set the liquid level and flow bit to 0
1753 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1755 n0.setContent(new_node_content);
1757 v3s16 blockpos = getNodeBlockPos(p0);
1758 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1760 modified_blocks.insert(blockpos, block);
1763 enqueue neighbors for update if neccessary
1765 switch (content_features(n0.getContent()).liquid_type) {
1767 case LIQUID_FLOWING:
1768 // make sure source flows into all neighboring nodes
1769 for (u16 i = 0; i < num_flows; i++)
1770 if (flows[i].t != NEIGHBOR_UPPER)
1771 m_transforming_liquid.push_back(flows[i].p);
1772 for (u16 i = 0; i < num_airs; i++)
1773 if (airs[i].t != NEIGHBOR_UPPER)
1774 m_transforming_liquid.push_back(airs[i].p);
1777 // this flow has turned to air; neighboring flows might need to do the same
1778 for (u16 i = 0; i < num_flows; i++)
1779 m_transforming_liquid.push_back(flows[i].p);
1783 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1784 while (must_reflow.size() > 0)
1785 m_transforming_liquid.push_back(must_reflow.pop_front());
1788 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1790 v3s16 blockpos = getNodeBlockPos(p);
1791 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1792 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1795 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1799 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1803 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1805 v3s16 blockpos = getNodeBlockPos(p);
1806 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1807 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1810 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1814 block->m_node_metadata.set(p_rel, meta);
1817 void Map::removeNodeMetadata(v3s16 p)
1819 v3s16 blockpos = getNodeBlockPos(p);
1820 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1821 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1824 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1828 block->m_node_metadata.remove(p_rel);
1831 void Map::nodeMetadataStep(float dtime,
1832 core::map<v3s16, MapBlock*> &changed_blocks)
1836 Currently there is no way to ensure that all the necessary
1837 blocks are loaded when this is run. (They might get unloaded)
1838 NOTE: ^- Actually, that might not be so. In a quick test it
1839 reloaded a block with a furnace when I walked back to it from
1842 core::map<v2s16, MapSector*>::Iterator si;
1843 si = m_sectors.getIterator();
1844 for(; si.atEnd() == false; si++)
1846 MapSector *sector = si.getNode()->getValue();
1847 core::list< MapBlock * > sectorblocks;
1848 sector->getBlocks(sectorblocks);
1849 core::list< MapBlock * >::Iterator i;
1850 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1852 MapBlock *block = *i;
1853 bool changed = block->m_node_metadata.step(dtime);
1855 changed_blocks[block->getPos()] = block;
1864 ServerMap::ServerMap(std::string savedir):
1867 m_map_metadata_changed(true)
1869 dstream<<__FUNCTION_NAME<<std::endl;
1871 //m_chunksize = 8; // Takes a few seconds
1873 m_seed = (((u64)(myrand()%0xffff)<<0)
1874 + ((u64)(myrand()%0xffff)<<16)
1875 + ((u64)(myrand()%0xffff)<<32)
1876 + ((u64)(myrand()%0xffff)<<48));
1879 Experimental and debug stuff
1886 Try to load map; if not found, create a new one.
1889 m_savedir = savedir;
1890 m_map_saving_enabled = false;
1894 // If directory exists, check contents and load if possible
1895 if(fs::PathExists(m_savedir))
1897 // If directory is empty, it is safe to save into it.
1898 if(fs::GetDirListing(m_savedir).size() == 0)
1900 dstream<<DTIME<<"Server: Empty save directory is valid."
1902 m_map_saving_enabled = true;
1907 // Load map metadata (seed, chunksize)
1910 catch(FileNotGoodException &e){
1911 dstream<<DTIME<<"WARNING: Could not load map metadata"
1912 //<<" Disabling chunk-based generator."
1918 // Load chunk metadata
1921 catch(FileNotGoodException &e){
1922 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1923 <<" Disabling chunk-based generator."
1928 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1929 "metadata and sector (0,0) from "<<savedir<<
1930 ", assuming valid save directory."
1933 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1934 <<"and chunk metadata from "<<savedir
1935 <<", assuming valid save directory."
1938 m_map_saving_enabled = true;
1939 // Map loaded, not creating new one
1943 // If directory doesn't exist, it is safe to save to it
1945 m_map_saving_enabled = true;
1948 catch(std::exception &e)
1950 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1951 <<", exception: "<<e.what()<<std::endl;
1952 dstream<<"Please remove the map or fix it."<<std::endl;
1953 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1956 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1958 // Create zero sector
1959 emergeSector(v2s16(0,0));
1961 // Initially write whole map
1965 ServerMap::~ServerMap()
1967 dstream<<__FUNCTION_NAME<<std::endl;
1971 if(m_map_saving_enabled)
1973 // Save only changed parts
1975 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1979 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1982 catch(std::exception &e)
1984 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1985 <<", exception: "<<e.what()<<std::endl;
1992 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1993 for(; i.atEnd() == false; i++)
1995 MapChunk *chunk = i.getNode()->getValue();
2001 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2003 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2004 if(enable_mapgen_debug_info)
2005 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2006 <<blockpos.Z<<")"<<std::endl;
2008 // Do nothing if not inside limits (+-1 because of neighbors)
2009 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2010 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2016 data->no_op = false;
2017 data->seed = m_seed;
2018 data->blockpos = blockpos;
2021 Create the whole area of this and the neighboring blocks
2024 //TimeTaker timer("initBlockMake() create area");
2026 for(s16 x=-1; x<=1; x++)
2027 for(s16 z=-1; z<=1; z++)
2029 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2030 // Sector metadata is loaded from disk if not already loaded.
2031 ServerMapSector *sector = createSector(sectorpos);
2034 for(s16 y=-1; y<=1; y++)
2036 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2037 //MapBlock *block = createBlock(p);
2038 // 1) get from memory, 2) load from disk
2039 MapBlock *block = emergeBlock(p, false);
2040 // 3) create a blank one
2043 block = createBlock(p);
2046 Block gets sunlight if this is true.
2048 Refer to the map generator heuristics.
2050 bool ug = mapgen::block_is_underground(data->seed, p);
2051 block->setIsUnderground(ug);
2054 // Lighting will not be valid after make_chunk is called
2055 block->setLightingExpired(true);
2056 // Lighting will be calculated
2057 //block->setLightingExpired(false);
2063 Now we have a big empty area.
2065 Make a ManualMapVoxelManipulator that contains this and the
2069 // The area that contains this block and it's neighbors
2070 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2071 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2073 data->vmanip = new ManualMapVoxelManipulator(this);
2074 //data->vmanip->setMap(this);
2078 //TimeTaker timer("initBlockMake() initialEmerge");
2079 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2082 // Data is ready now.
2085 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2086 core::map<v3s16, MapBlock*> &changed_blocks)
2088 v3s16 blockpos = data->blockpos;
2089 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2090 <<blockpos.Z<<")"<<std::endl;*/
2094 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2098 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2100 /*dstream<<"Resulting vmanip:"<<std::endl;
2101 data->vmanip.print(dstream);*/
2104 Blit generated stuff to map
2105 NOTE: blitBackAll adds nearly everything to changed_blocks
2109 //TimeTaker timer("finishBlockMake() blitBackAll");
2110 data->vmanip->blitBackAll(&changed_blocks);
2113 if(enable_mapgen_debug_info)
2114 dstream<<"finishBlockMake: changed_blocks.size()="
2115 <<changed_blocks.size()<<std::endl;
2118 Copy transforming liquid information
2120 while(data->transforming_liquid.size() > 0)
2122 v3s16 p = data->transforming_liquid.pop_front();
2123 m_transforming_liquid.push_back(p);
2129 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2133 Set is_underground flag for lighting with sunlight.
2135 Refer to map generator heuristics.
2137 NOTE: This is done in initChunkMake
2139 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2143 Add sunlight to central block.
2144 This makes in-dark-spawning monsters to not flood the whole thing.
2145 Do not spread the light, though.
2147 /*core::map<v3s16, bool> light_sources;
2148 bool black_air_left = false;
2149 block->propagateSunlight(light_sources, true, &black_air_left);*/
2152 NOTE: Lighting and object adding shouldn't really be here, but
2153 lighting is a bit tricky to move properly to makeBlock.
2154 TODO: Do this the right way anyway, that is, move it to makeBlock.
2155 - There needs to be some way for makeBlock to report back if
2156 the lighting update is going further down because of the
2157 new block blocking light
2162 NOTE: This takes ~60ms, TODO: Investigate why
2165 TimeTaker t("finishBlockMake lighting update");
2167 core::map<v3s16, MapBlock*> lighting_update_blocks;
2170 lighting_update_blocks.insert(block->getPos(), block);
2175 v3s16 p = block->getPos()+v3s16(x,1,z);
2176 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2180 // All modified blocks
2181 // NOTE: Should this be done? If this is not done, then the lighting
2182 // of the others will be updated in a different place, one by one, i
2183 // think... or they might not? Well, at least they are left marked as
2184 // "lighting expired"; it seems that is not handled at all anywhere,
2185 // so enabling this will slow it down A LOT because otherwise it
2186 // would not do this at all. This causes the black trees.
2187 for(core::map<v3s16, MapBlock*>::Iterator
2188 i = changed_blocks.getIterator();
2189 i.atEnd() == false; i++)
2191 lighting_update_blocks.insert(i.getNode()->getKey(),
2192 i.getNode()->getValue());
2194 /*// Also force-add all the upmost blocks for proper sunlight
2195 for(s16 x=-1; x<=1; x++)
2196 for(s16 z=-1; z<=1; z++)
2198 v3s16 p = block->getPos()+v3s16(x,1,z);
2199 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2202 updateLighting(lighting_update_blocks, changed_blocks);
2205 Set lighting to non-expired state in all of them.
2206 This is cheating, but it is not fast enough if all of them
2207 would actually be updated.
2209 for(s16 x=-1; x<=1; x++)
2210 for(s16 y=-1; y<=1; y++)
2211 for(s16 z=-1; z<=1; z++)
2213 v3s16 p = block->getPos()+v3s16(x,y,z);
2214 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2217 if(enable_mapgen_debug_info == false)
2218 t.stop(true); // Hide output
2222 Add random objects to block
2224 mapgen::add_random_objects(block);
2227 Go through changed blocks
2229 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2230 i.atEnd() == false; i++)
2232 MapBlock *block = i.getNode()->getValue();
2235 Update day/night difference cache of the MapBlocks
2237 block->updateDayNightDiff();
2239 Set block as modified
2241 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2245 Set central block as generated
2247 block->setGenerated(true);
2250 Save changed parts of map
2251 NOTE: Will be saved later.
2255 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2256 <<blockpos.Z<<")"<<std::endl;*/
2258 if(enable_mapgen_debug_info)
2261 Analyze resulting blocks
2263 for(s16 x=-1; x<=1; x++)
2264 for(s16 y=-1; y<=1; y++)
2265 for(s16 z=-1; z<=1; z++)
2267 v3s16 p = block->getPos()+v3s16(x,y,z);
2268 MapBlock *block = getBlockNoCreateNoEx(p);
2270 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2271 dstream<<"Generated "<<spos<<": "
2272 <<analyze_block(block)<<std::endl;
2280 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2282 DSTACKF("%s: p2d=(%d,%d)",
2287 Check if it exists already in memory
2289 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2294 Try to load it from disk (with blocks)
2296 //if(loadSectorFull(p2d) == true)
2299 Try to load metadata from disk
2301 if(loadSectorMeta(p2d) == true)
2303 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2306 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2307 throw InvalidPositionException("");
2313 Do not create over-limit
2315 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2316 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2317 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2318 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2319 throw InvalidPositionException("createSector(): pos. over limit");
2322 Generate blank sector
2325 sector = new ServerMapSector(this, p2d);
2327 // Sector position on map in nodes
2328 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2333 m_sectors.insert(p2d, sector);
2339 This is a quick-hand function for calling makeBlock().
2341 MapBlock * ServerMap::generateBlock(
2343 core::map<v3s16, MapBlock*> &modified_blocks
2346 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2348 /*dstream<<"generateBlock(): "
2349 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2352 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2354 TimeTaker timer("generateBlock");
2356 //MapBlock *block = original_dummy;
2358 v2s16 p2d(p.X, p.Z);
2359 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2362 Do not generate over-limit
2364 if(blockpos_over_limit(p))
2366 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2367 throw InvalidPositionException("generateBlock(): pos. over limit");
2371 Create block make data
2373 mapgen::BlockMakeData data;
2374 initBlockMake(&data, p);
2380 TimeTaker t("mapgen::make_block()");
2381 mapgen::make_block(&data);
2383 if(enable_mapgen_debug_info == false)
2384 t.stop(true); // Hide output
2388 Blit data back on map, update lighting, add mobs and whatever this does
2390 finishBlockMake(&data, modified_blocks);
2395 MapBlock *block = getBlockNoCreateNoEx(p);
2403 bool erroneus_content = false;
2404 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2405 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2406 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2409 MapNode n = block->getNode(p);
2410 if(n.getContent() == CONTENT_IGNORE)
2412 dstream<<"CONTENT_IGNORE at "
2413 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2415 erroneus_content = true;
2419 if(erroneus_content)
2428 Generate a completely empty block
2432 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2433 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2435 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2439 n.setContent(CONTENT_AIR);
2441 n.setContent(CONTENT_STONE);
2442 block->setNode(v3s16(x0,y0,z0), n);
2448 if(enable_mapgen_debug_info == false)
2449 timer.stop(true); // Hide output
2454 MapBlock * ServerMap::createBlock(v3s16 p)
2456 DSTACKF("%s: p=(%d,%d,%d)",
2457 __FUNCTION_NAME, p.X, p.Y, p.Z);
2460 Do not create over-limit
2462 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2463 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2464 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2465 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2466 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2467 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2468 throw InvalidPositionException("createBlock(): pos. over limit");
2470 v2s16 p2d(p.X, p.Z);
2473 This will create or load a sector if not found in memory.
2474 If block exists on disk, it will be loaded.
2476 NOTE: On old save formats, this will be slow, as it generates
2477 lighting on blocks for them.
2479 ServerMapSector *sector;
2481 sector = (ServerMapSector*)createSector(p2d);
2482 assert(sector->getId() == MAPSECTOR_SERVER);
2484 catch(InvalidPositionException &e)
2486 dstream<<"createBlock: createSector() failed"<<std::endl;
2490 NOTE: This should not be done, or at least the exception
2491 should not be passed on as std::exception, because it
2492 won't be catched at all.
2494 /*catch(std::exception &e)
2496 dstream<<"createBlock: createSector() failed: "
2497 <<e.what()<<std::endl;
2502 Try to get a block from the sector
2505 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2508 if(block->isDummy())
2513 block = sector->createBlankBlock(block_y);
2517 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2519 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2521 p.X, p.Y, p.Z, allow_generate);
2524 MapBlock *block = getBlockNoCreateNoEx(p);
2525 if(block && block->isDummy() == false)
2530 MapBlock *block = loadBlock(p);
2537 core::map<v3s16, MapBlock*> modified_blocks;
2538 MapBlock *block = generateBlock(p, modified_blocks);
2542 event.type = MEET_OTHER;
2545 // Copy modified_blocks to event
2546 for(core::map<v3s16, MapBlock*>::Iterator
2547 i = modified_blocks.getIterator();
2548 i.atEnd()==false; i++)
2550 event.modified_blocks.insert(i.getNode()->getKey(), false);
2554 dispatchEvent(&event);
2565 Do not generate over-limit
2567 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2568 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2569 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2570 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2571 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2572 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2573 throw InvalidPositionException("emergeBlock(): pos. over limit");
2575 v2s16 p2d(p.X, p.Z);
2578 This will create or load a sector if not found in memory.
2579 If block exists on disk, it will be loaded.
2581 ServerMapSector *sector;
2583 sector = createSector(p2d);
2584 //sector = emergeSector(p2d, changed_blocks);
2586 catch(InvalidPositionException &e)
2588 dstream<<"emergeBlock: createSector() failed: "
2589 <<e.what()<<std::endl;
2590 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2592 <<"You could try to delete it."<<std::endl;
2595 catch(VersionMismatchException &e)
2597 dstream<<"emergeBlock: createSector() failed: "
2598 <<e.what()<<std::endl;
2599 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2601 <<"You could try to delete it."<<std::endl;
2606 Try to get a block from the sector
2609 bool does_not_exist = false;
2610 bool lighting_expired = false;
2611 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2613 // If not found, try loading from disk
2616 block = loadBlock(p);
2622 does_not_exist = true;
2624 else if(block->isDummy() == true)
2626 does_not_exist = true;
2628 else if(block->getLightingExpired())
2630 lighting_expired = true;
2635 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2640 If block was not found on disk and not going to generate a
2641 new one, make sure there is a dummy block in place.
2643 if(only_from_disk && (does_not_exist || lighting_expired))
2645 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2649 // Create dummy block
2650 block = new MapBlock(this, p, true);
2652 // Add block to sector
2653 sector->insertBlock(block);
2659 //dstream<<"Not found on disk, generating."<<std::endl;
2661 //TimeTaker("emergeBlock() generate");
2663 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2666 If the block doesn't exist, generate the block.
2670 block = generateBlock(p, block, sector, changed_blocks,
2671 lighting_invalidated_blocks);
2674 if(lighting_expired)
2676 lighting_invalidated_blocks.insert(p, block);
2681 Initially update sunlight
2684 core::map<v3s16, bool> light_sources;
2685 bool black_air_left = false;
2686 bool bottom_invalid =
2687 block->propagateSunlight(light_sources, true,
2690 // If sunlight didn't reach everywhere and part of block is
2691 // above ground, lighting has to be properly updated
2692 //if(black_air_left && some_part_underground)
2695 lighting_invalidated_blocks[block->getPos()] = block;
2700 lighting_invalidated_blocks[block->getPos()] = block;
2709 s16 ServerMap::findGroundLevel(v2s16 p2d)
2713 Uh, just do something random...
2715 // Find existing map from top to down
2718 v3s16 p(p2d.X, max, p2d.Y);
2719 for(; p.Y>min; p.Y--)
2721 MapNode n = getNodeNoEx(p);
2722 if(n.getContent() != CONTENT_IGNORE)
2727 // If this node is not air, go to plan b
2728 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2730 // Search existing walkable and return it
2731 for(; p.Y>min; p.Y--)
2733 MapNode n = getNodeNoEx(p);
2734 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2743 Determine from map generator noise functions
2746 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2749 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2750 //return (s16)level;
2753 void ServerMap::createDirs(std::string path)
2755 if(fs::CreateAllDirs(path) == false)
2757 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2758 <<"\""<<path<<"\""<<std::endl;
2759 throw BaseException("ServerMap failed to create directory");
2763 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2769 snprintf(cc, 9, "%.4x%.4x",
2770 (unsigned int)pos.X&0xffff,
2771 (unsigned int)pos.Y&0xffff);
2773 return m_savedir + "/sectors/" + cc;
2775 snprintf(cc, 9, "%.3x/%.3x",
2776 (unsigned int)pos.X&0xfff,
2777 (unsigned int)pos.Y&0xfff);
2779 return m_savedir + "/sectors2/" + cc;
2785 v2s16 ServerMap::getSectorPos(std::string dirname)
2789 size_t spos = dirname.rfind('/') + 1;
2790 assert(spos != std::string::npos);
2791 if(dirname.size() - spos == 8)
2794 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2796 else if(dirname.size() - spos == 3)
2799 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2800 // Sign-extend the 12 bit values up to 16 bits...
2801 if(x&0x800) x|=0xF000;
2802 if(y&0x800) y|=0xF000;
2809 v2s16 pos((s16)x, (s16)y);
2813 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2815 v2s16 p2d = getSectorPos(sectordir);
2817 if(blockfile.size() != 4){
2818 throw InvalidFilenameException("Invalid block filename");
2821 int r = sscanf(blockfile.c_str(), "%4x", &y);
2823 throw InvalidFilenameException("Invalid block filename");
2824 return v3s16(p2d.X, y, p2d.Y);
2827 std::string ServerMap::getBlockFilename(v3s16 p)
2830 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2834 void ServerMap::save(bool only_changed)
2836 DSTACK(__FUNCTION_NAME);
2837 if(m_map_saving_enabled == false)
2839 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2843 if(only_changed == false)
2844 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2847 if(only_changed == false || m_map_metadata_changed)
2852 u32 sector_meta_count = 0;
2853 u32 block_count = 0;
2854 u32 block_count_all = 0; // Number of blocks in memory
2856 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2857 for(; i.atEnd() == false; i++)
2859 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2860 assert(sector->getId() == MAPSECTOR_SERVER);
2862 if(sector->differs_from_disk || only_changed == false)
2864 saveSectorMeta(sector);
2865 sector_meta_count++;
2867 core::list<MapBlock*> blocks;
2868 sector->getBlocks(blocks);
2869 core::list<MapBlock*>::Iterator j;
2870 for(j=blocks.begin(); j!=blocks.end(); j++)
2872 MapBlock *block = *j;
2876 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2877 || only_changed == false)
2882 /*dstream<<"ServerMap: Written block ("
2883 <<block->getPos().X<<","
2884 <<block->getPos().Y<<","
2885 <<block->getPos().Z<<")"
2892 Only print if something happened or saved whole map
2894 if(only_changed == false || sector_meta_count != 0
2895 || block_count != 0)
2897 dstream<<DTIME<<"ServerMap: Written: "
2898 <<sector_meta_count<<" sector metadata files, "
2899 <<block_count<<" block files"
2900 <<", "<<block_count_all<<" blocks in memory."
2905 void ServerMap::saveMapMeta()
2907 DSTACK(__FUNCTION_NAME);
2909 dstream<<"INFO: ServerMap::saveMapMeta(): "
2913 createDirs(m_savedir);
2915 std::string fullpath = m_savedir + "/map_meta.txt";
2916 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2917 if(os.good() == false)
2919 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2920 <<"could not open"<<fullpath<<std::endl;
2921 throw FileNotGoodException("Cannot open chunk metadata");
2925 params.setU64("seed", m_seed);
2927 params.writeLines(os);
2929 os<<"[end_of_params]\n";
2931 m_map_metadata_changed = false;
2934 void ServerMap::loadMapMeta()
2936 DSTACK(__FUNCTION_NAME);
2938 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2941 std::string fullpath = m_savedir + "/map_meta.txt";
2942 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2943 if(is.good() == false)
2945 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2946 <<"could not open"<<fullpath<<std::endl;
2947 throw FileNotGoodException("Cannot open map metadata");
2955 throw SerializationError
2956 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2958 std::getline(is, line);
2959 std::string trimmedline = trim(line);
2960 if(trimmedline == "[end_of_params]")
2962 params.parseConfigLine(line);
2965 m_seed = params.getU64("seed");
2967 dstream<<"INFO: ServerMap::loadMapMeta(): "
2972 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2974 DSTACK(__FUNCTION_NAME);
2975 // Format used for writing
2976 u8 version = SER_FMT_VER_HIGHEST;
2978 v2s16 pos = sector->getPos();
2979 std::string dir = getSectorDir(pos);
2982 std::string fullpath = dir + "/meta";
2983 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2984 if(o.good() == false)
2985 throw FileNotGoodException("Cannot open sector metafile");
2987 sector->serialize(o, version);
2989 sector->differs_from_disk = false;
2992 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2994 DSTACK(__FUNCTION_NAME);
2996 v2s16 p2d = getSectorPos(sectordir);
2998 ServerMapSector *sector = NULL;
3000 std::string fullpath = sectordir + "/meta";
3001 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3002 if(is.good() == false)
3004 // If the directory exists anyway, it probably is in some old
3005 // format. Just go ahead and create the sector.
3006 if(fs::PathExists(sectordir))
3008 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3009 <<fullpath<<" doesn't exist but directory does."
3010 <<" Continuing with a sector with no metadata."
3012 sector = new ServerMapSector(this, p2d);
3013 m_sectors.insert(p2d, sector);
3017 throw FileNotGoodException("Cannot open sector metafile");
3022 sector = ServerMapSector::deSerialize
3023 (is, this, p2d, m_sectors);
3025 saveSectorMeta(sector);
3028 sector->differs_from_disk = false;
3033 bool ServerMap::loadSectorMeta(v2s16 p2d)
3035 DSTACK(__FUNCTION_NAME);
3037 MapSector *sector = NULL;
3039 // The directory layout we're going to load from.
3040 // 1 - original sectors/xxxxzzzz/
3041 // 2 - new sectors2/xxx/zzz/
3042 // If we load from anything but the latest structure, we will
3043 // immediately save to the new one, and remove the old.
3045 std::string sectordir1 = getSectorDir(p2d, 1);
3046 std::string sectordir;
3047 if(fs::PathExists(sectordir1))
3049 sectordir = sectordir1;
3054 sectordir = getSectorDir(p2d, 2);
3058 sector = loadSectorMeta(sectordir, loadlayout != 2);
3060 catch(InvalidFilenameException &e)
3064 catch(FileNotGoodException &e)
3068 catch(std::exception &e)
3077 bool ServerMap::loadSectorFull(v2s16 p2d)
3079 DSTACK(__FUNCTION_NAME);
3081 MapSector *sector = NULL;
3083 // The directory layout we're going to load from.
3084 // 1 - original sectors/xxxxzzzz/
3085 // 2 - new sectors2/xxx/zzz/
3086 // If we load from anything but the latest structure, we will
3087 // immediately save to the new one, and remove the old.
3089 std::string sectordir1 = getSectorDir(p2d, 1);
3090 std::string sectordir;
3091 if(fs::PathExists(sectordir1))
3093 sectordir = sectordir1;
3098 sectordir = getSectorDir(p2d, 2);
3102 sector = loadSectorMeta(sectordir, loadlayout != 2);
3104 catch(InvalidFilenameException &e)
3108 catch(FileNotGoodException &e)
3112 catch(std::exception &e)
3120 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3122 std::vector<fs::DirListNode>::iterator i2;
3123 for(i2=list2.begin(); i2!=list2.end(); i2++)
3129 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3131 catch(InvalidFilenameException &e)
3133 // This catches unknown crap in directory
3139 dstream<<"Sector converted to new layout - deleting "<<
3140 sectordir1<<std::endl;
3141 fs::RecursiveDelete(sectordir1);
3148 void ServerMap::saveBlock(MapBlock *block)
3150 DSTACK(__FUNCTION_NAME);
3152 Dummy blocks are not written
3154 if(block->isDummy())
3156 /*v3s16 p = block->getPos();
3157 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3158 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3162 // Format used for writing
3163 u8 version = SER_FMT_VER_HIGHEST;
3165 v3s16 p3d = block->getPos();
3167 v2s16 p2d(p3d.X, p3d.Z);
3168 std::string sectordir = getSectorDir(p2d);
3170 createDirs(sectordir);
3172 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3173 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3174 if(o.good() == false)
3175 throw FileNotGoodException("Cannot open block data");
3178 [0] u8 serialization version
3181 o.write((char*)&version, 1);
3184 block->serialize(o, version);
3186 // Write extra data stored on disk
3187 block->serializeDiskExtra(o, version);
3189 // We just wrote it to the disk so clear modified flag
3190 block->resetModified();
3193 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3195 DSTACK(__FUNCTION_NAME);
3197 std::string fullpath = sectordir+"/"+blockfile;
3200 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3201 if(is.good() == false)
3202 throw FileNotGoodException("Cannot open block file");
3204 v3s16 p3d = getBlockPos(sectordir, blockfile);
3205 v2s16 p2d(p3d.X, p3d.Z);
3207 assert(sector->getPos() == p2d);
3209 u8 version = SER_FMT_VER_INVALID;
3210 is.read((char*)&version, 1);
3213 throw SerializationError("ServerMap::loadBlock(): Failed"
3214 " to read MapBlock version");
3216 /*u32 block_size = MapBlock::serializedLength(version);
3217 SharedBuffer<u8> data(block_size);
3218 is.read((char*)*data, block_size);*/
3220 // This will always return a sector because we're the server
3221 //MapSector *sector = emergeSector(p2d);
3223 MapBlock *block = NULL;
3224 bool created_new = false;
3225 block = sector->getBlockNoCreateNoEx(p3d.Y);
3228 block = sector->createBlankBlockNoInsert(p3d.Y);
3233 block->deSerialize(is, version);
3235 // Read extra data stored on disk
3236 block->deSerializeDiskExtra(is, version);
3238 // If it's a new block, insert it to the map
3240 sector->insertBlock(block);
3243 Save blocks loaded in old format in new format
3246 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3251 // We just loaded it from the disk, so it's up-to-date.
3252 block->resetModified();
3255 catch(SerializationError &e)
3257 dstream<<"WARNING: Invalid block data on disk "
3258 <<"fullpath="<<fullpath
3259 <<" (SerializationError). "
3260 <<"what()="<<e.what()
3262 //" Ignoring. A new one will be generated.
3265 // TODO: Backup file; name is in fullpath.
3269 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3271 DSTACK(__FUNCTION_NAME);
3273 v2s16 p2d(blockpos.X, blockpos.Z);
3275 // The directory layout we're going to load from.
3276 // 1 - original sectors/xxxxzzzz/
3277 // 2 - new sectors2/xxx/zzz/
3278 // If we load from anything but the latest structure, we will
3279 // immediately save to the new one, and remove the old.
3281 std::string sectordir1 = getSectorDir(p2d, 1);
3282 std::string sectordir;
3283 if(fs::PathExists(sectordir1))
3285 sectordir = sectordir1;
3290 sectordir = getSectorDir(p2d, 2);
3294 Make sure sector is loaded
3296 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3300 sector = loadSectorMeta(sectordir, loadlayout != 2);
3302 catch(InvalidFilenameException &e)
3306 catch(FileNotGoodException &e)
3310 catch(std::exception &e)
3317 Make sure file exists
3320 std::string blockfilename = getBlockFilename(blockpos);
3321 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3327 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3328 return getBlockNoCreateNoEx(blockpos);
3331 void ServerMap::PrintInfo(std::ostream &out)
3342 ClientMap::ClientMap(
3344 MapDrawControl &control,
3345 scene::ISceneNode* parent,
3346 scene::ISceneManager* mgr,
3350 scene::ISceneNode(parent, mgr, id),
3353 m_camera_position(0,0,0),
3354 m_camera_direction(0,0,1)
3356 m_camera_mutex.Init();
3357 assert(m_camera_mutex.IsInitialized());
3359 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3360 BS*1000000,BS*1000000,BS*1000000);
3363 ClientMap::~ClientMap()
3365 /*JMutexAutoLock lock(mesh_mutex);
3374 MapSector * ClientMap::emergeSector(v2s16 p2d)
3376 DSTACK(__FUNCTION_NAME);
3377 // Check that it doesn't exist already
3379 return getSectorNoGenerate(p2d);
3381 catch(InvalidPositionException &e)
3386 ClientMapSector *sector = new ClientMapSector(this, p2d);
3389 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3390 m_sectors.insert(p2d, sector);
3397 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3399 DSTACK(__FUNCTION_NAME);
3400 ClientMapSector *sector = NULL;
3402 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3404 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3408 sector = (ClientMapSector*)n->getValue();
3409 assert(sector->getId() == MAPSECTOR_CLIENT);
3413 sector = new ClientMapSector(this, p2d);
3415 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3416 m_sectors.insert(p2d, sector);
3420 sector->deSerialize(is);
3424 void ClientMap::OnRegisterSceneNode()
3428 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3429 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3432 ISceneNode::OnRegisterSceneNode();
3435 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3437 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3438 DSTACK(__FUNCTION_NAME);
3440 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3443 This is called two times per frame, reset on the non-transparent one
3445 if(pass == scene::ESNRP_SOLID)
3447 m_last_drawn_sectors.clear();
3451 Get time for measuring timeout.
3453 Measuring time is very useful for long delays when the
3454 machine is swapping a lot.
3456 int time1 = time(0);
3458 //u32 daynight_ratio = m_client->getDayNightRatio();
3460 m_camera_mutex.Lock();
3461 v3f camera_position = m_camera_position;
3462 v3f camera_direction = m_camera_direction;
3463 m_camera_mutex.Unlock();
3466 Get all blocks and draw all visible ones
3469 v3s16 cam_pos_nodes(
3470 camera_position.X / BS,
3471 camera_position.Y / BS,
3472 camera_position.Z / BS);
3474 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3476 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3477 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3479 // Take a fair amount as we will be dropping more out later
3481 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3482 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3483 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3485 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3486 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3487 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3489 u32 vertex_count = 0;
3491 // For limiting number of mesh updates per frame
3492 u32 mesh_update_count = 0;
3494 u32 blocks_would_have_drawn = 0;
3495 u32 blocks_drawn = 0;
3497 int timecheck_counter = 0;
3498 core::map<v2s16, MapSector*>::Iterator si;
3499 si = m_sectors.getIterator();
3500 for(; si.atEnd() == false; si++)
3503 timecheck_counter++;
3504 if(timecheck_counter > 50)
3506 timecheck_counter = 0;
3507 int time2 = time(0);
3508 if(time2 > time1 + 4)
3510 dstream<<"ClientMap::renderMap(): "
3511 "Rendering takes ages, returning."
3518 MapSector *sector = si.getNode()->getValue();
3519 v2s16 sp = sector->getPos();
3521 if(m_control.range_all == false)
3523 if(sp.X < p_blocks_min.X
3524 || sp.X > p_blocks_max.X
3525 || sp.Y < p_blocks_min.Z
3526 || sp.Y > p_blocks_max.Z)
3530 core::list< MapBlock * > sectorblocks;
3531 sector->getBlocks(sectorblocks);
3537 u32 sector_blocks_drawn = 0;
3539 core::list< MapBlock * >::Iterator i;
3540 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3542 MapBlock *block = *i;
3545 Compare block position to camera position, skip
3546 if not seen on display
3549 float range = 100000 * BS;
3550 if(m_control.range_all == false)
3551 range = m_control.wanted_range * BS;
3554 if(isBlockInSight(block->getPos(), camera_position,
3555 camera_direction, range, &d) == false)
3560 // Okay, this block will be drawn. Reset usage timer.
3561 block->resetUsageTimer();
3563 // This is ugly (spherical distance limit?)
3564 /*if(m_control.range_all == false &&
3565 d - 0.5*BS*MAP_BLOCKSIZE > range)
3570 Update expired mesh (used for day/night change)
3572 It doesn't work exactly like it should now with the
3573 tasked mesh update but whatever.
3576 bool mesh_expired = false;
3579 JMutexAutoLock lock(block->mesh_mutex);
3581 mesh_expired = block->getMeshExpired();
3583 // Mesh has not been expired and there is no mesh:
3584 // block has no content
3585 if(block->mesh == NULL && mesh_expired == false)
3589 f32 faraway = BS*50;
3590 //f32 faraway = m_control.wanted_range * BS;
3593 This has to be done with the mesh_mutex unlocked
3595 // Pretty random but this should work somewhat nicely
3596 if(mesh_expired && (
3597 (mesh_update_count < 3
3598 && (d < faraway || mesh_update_count < 2)
3601 (m_control.range_all && mesh_update_count < 20)
3604 /*if(mesh_expired && mesh_update_count < 6
3605 && (d < faraway || mesh_update_count < 3))*/
3607 mesh_update_count++;
3609 // Mesh has been expired: generate new mesh
3610 //block->updateMesh(daynight_ratio);
3611 m_client->addUpdateMeshTask(block->getPos());
3613 mesh_expired = false;
3618 Draw the faces of the block
3621 JMutexAutoLock lock(block->mesh_mutex);
3623 scene::SMesh *mesh = block->mesh;
3628 blocks_would_have_drawn++;
3629 if(blocks_drawn >= m_control.wanted_max_blocks
3630 && m_control.range_all == false
3631 && d > m_control.wanted_min_range * BS)
3635 sector_blocks_drawn++;
3637 u32 c = mesh->getMeshBufferCount();
3639 for(u32 i=0; i<c; i++)
3641 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3642 const video::SMaterial& material = buf->getMaterial();
3643 video::IMaterialRenderer* rnd =
3644 driver->getMaterialRenderer(material.MaterialType);
3645 bool transparent = (rnd && rnd->isTransparent());
3646 // Render transparent on transparent pass and likewise.
3647 if(transparent == is_transparent_pass)
3650 This *shouldn't* hurt too much because Irrlicht
3651 doesn't change opengl textures if the old
3652 material is set again.
3654 driver->setMaterial(buf->getMaterial());
3655 driver->drawMeshBuffer(buf);
3656 vertex_count += buf->getVertexCount();
3660 } // foreach sectorblocks
3662 if(sector_blocks_drawn != 0)
3664 m_last_drawn_sectors[sp] = true;
3668 m_control.blocks_drawn = blocks_drawn;
3669 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3671 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3672 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3675 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3676 core::map<v3s16, MapBlock*> *affected_blocks)
3678 bool changed = false;
3680 Add it to all blocks touching it
3683 v3s16(0,0,0), // this
3684 v3s16(0,0,1), // back
3685 v3s16(0,1,0), // top
3686 v3s16(1,0,0), // right
3687 v3s16(0,0,-1), // front
3688 v3s16(0,-1,0), // bottom
3689 v3s16(-1,0,0), // left
3691 for(u16 i=0; i<7; i++)
3693 v3s16 p2 = p + dirs[i];
3694 // Block position of neighbor (or requested) node
3695 v3s16 blockpos = getNodeBlockPos(p2);
3696 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3697 if(blockref == NULL)
3699 // Relative position of requested node
3700 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3701 if(blockref->setTempMod(relpos, mod))
3706 if(changed && affected_blocks!=NULL)
3708 for(u16 i=0; i<7; i++)
3710 v3s16 p2 = p + dirs[i];
3711 // Block position of neighbor (or requested) node
3712 v3s16 blockpos = getNodeBlockPos(p2);
3713 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3714 if(blockref == NULL)
3716 affected_blocks->insert(blockpos, blockref);
3722 bool ClientMap::clearTempMod(v3s16 p,
3723 core::map<v3s16, MapBlock*> *affected_blocks)
3725 bool changed = false;
3727 v3s16(0,0,0), // this
3728 v3s16(0,0,1), // back
3729 v3s16(0,1,0), // top
3730 v3s16(1,0,0), // right
3731 v3s16(0,0,-1), // front
3732 v3s16(0,-1,0), // bottom
3733 v3s16(-1,0,0), // left
3735 for(u16 i=0; i<7; i++)
3737 v3s16 p2 = p + dirs[i];
3738 // Block position of neighbor (or requested) node
3739 v3s16 blockpos = getNodeBlockPos(p2);
3740 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3741 if(blockref == NULL)
3743 // Relative position of requested node
3744 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3745 if(blockref->clearTempMod(relpos))
3750 if(changed && affected_blocks!=NULL)
3752 for(u16 i=0; i<7; i++)
3754 v3s16 p2 = p + dirs[i];
3755 // Block position of neighbor (or requested) node
3756 v3s16 blockpos = getNodeBlockPos(p2);
3757 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3758 if(blockref == NULL)
3760 affected_blocks->insert(blockpos, blockref);
3766 void ClientMap::expireMeshes(bool only_daynight_diffed)
3768 TimeTaker timer("expireMeshes()");
3770 core::map<v2s16, MapSector*>::Iterator si;
3771 si = m_sectors.getIterator();
3772 for(; si.atEnd() == false; si++)
3774 MapSector *sector = si.getNode()->getValue();
3776 core::list< MapBlock * > sectorblocks;
3777 sector->getBlocks(sectorblocks);
3779 core::list< MapBlock * >::Iterator i;
3780 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3782 MapBlock *block = *i;
3784 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3790 JMutexAutoLock lock(block->mesh_mutex);
3791 if(block->mesh != NULL)
3793 /*block->mesh->drop();
3794 block->mesh = NULL;*/
3795 block->setMeshExpired(true);
3802 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3804 assert(mapType() == MAPTYPE_CLIENT);
3807 v3s16 p = blockpos + v3s16(0,0,0);
3808 MapBlock *b = getBlockNoCreate(p);
3809 b->updateMesh(daynight_ratio);
3810 //b->setMeshExpired(true);
3812 catch(InvalidPositionException &e){}
3815 v3s16 p = blockpos + v3s16(-1,0,0);
3816 MapBlock *b = getBlockNoCreate(p);
3817 b->updateMesh(daynight_ratio);
3818 //b->setMeshExpired(true);
3820 catch(InvalidPositionException &e){}
3822 v3s16 p = blockpos + v3s16(0,-1,0);
3823 MapBlock *b = getBlockNoCreate(p);
3824 b->updateMesh(daynight_ratio);
3825 //b->setMeshExpired(true);
3827 catch(InvalidPositionException &e){}
3829 v3s16 p = blockpos + v3s16(0,0,-1);
3830 MapBlock *b = getBlockNoCreate(p);
3831 b->updateMesh(daynight_ratio);
3832 //b->setMeshExpired(true);
3834 catch(InvalidPositionException &e){}
3839 Update mesh of block in which the node is, and if the node is at the
3840 leading edge, update the appropriate leading blocks too.
3842 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3850 v3s16 blockposes[4];
3851 for(u32 i=0; i<4; i++)
3853 v3s16 np = nodepos + dirs[i];
3854 blockposes[i] = getNodeBlockPos(np);
3855 // Don't update mesh of block if it has been done already
3856 bool already_updated = false;
3857 for(u32 j=0; j<i; j++)
3859 if(blockposes[j] == blockposes[i])
3861 already_updated = true;
3868 MapBlock *b = getBlockNoCreate(blockposes[i]);
3869 b->updateMesh(daynight_ratio);
3874 void ClientMap::PrintInfo(std::ostream &out)
3885 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3890 MapVoxelManipulator::~MapVoxelManipulator()
3892 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3896 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3898 TimeTaker timer1("emerge", &emerge_time);
3900 // Units of these are MapBlocks
3901 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3902 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3904 VoxelArea block_area_nodes
3905 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3907 addArea(block_area_nodes);
3909 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3910 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3911 for(s32 x=p_min.X; x<=p_max.X; x++)
3914 core::map<v3s16, bool>::Node *n;
3915 n = m_loaded_blocks.find(p);
3919 bool block_data_inexistent = false;
3922 TimeTaker timer1("emerge load", &emerge_load_time);
3924 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3925 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3928 dstream<<std::endl;*/
3930 MapBlock *block = m_map->getBlockNoCreate(p);
3931 if(block->isDummy())
3932 block_data_inexistent = true;
3934 block->copyTo(*this);
3936 catch(InvalidPositionException &e)
3938 block_data_inexistent = true;
3941 if(block_data_inexistent)
3943 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3944 // Fill with VOXELFLAG_INEXISTENT
3945 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3946 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3948 s32 i = m_area.index(a.MinEdge.X,y,z);
3949 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3953 m_loaded_blocks.insert(p, !block_data_inexistent);
3956 //dstream<<"emerge done"<<std::endl;
3960 SUGG: Add an option to only update eg. water and air nodes.
3961 This will make it interfere less with important stuff if
3964 void MapVoxelManipulator::blitBack
3965 (core::map<v3s16, MapBlock*> & modified_blocks)
3967 if(m_area.getExtent() == v3s16(0,0,0))
3970 //TimeTaker timer1("blitBack");
3972 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3973 <<m_loaded_blocks.size()<<std::endl;*/
3976 Initialize block cache
3978 v3s16 blockpos_last;
3979 MapBlock *block = NULL;
3980 bool block_checked_in_modified = false;
3982 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3983 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3984 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3988 u8 f = m_flags[m_area.index(p)];
3989 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3992 MapNode &n = m_data[m_area.index(p)];
3994 v3s16 blockpos = getNodeBlockPos(p);
3999 if(block == NULL || blockpos != blockpos_last){
4000 block = m_map->getBlockNoCreate(blockpos);
4001 blockpos_last = blockpos;
4002 block_checked_in_modified = false;
4005 // Calculate relative position in block
4006 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4008 // Don't continue if nothing has changed here
4009 if(block->getNode(relpos) == n)
4012 //m_map->setNode(m_area.MinEdge + p, n);
4013 block->setNode(relpos, n);
4016 Make sure block is in modified_blocks
4018 if(block_checked_in_modified == false)
4020 modified_blocks[blockpos] = block;
4021 block_checked_in_modified = true;
4024 catch(InvalidPositionException &e)
4030 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4031 MapVoxelManipulator(map),
4032 m_create_area(false)
4036 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4040 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4042 // Just create the area so that it can be pointed to
4043 VoxelManipulator::emerge(a, caller_id);
4046 void ManualMapVoxelManipulator::initialEmerge(
4047 v3s16 blockpos_min, v3s16 blockpos_max)
4049 TimeTaker timer1("initialEmerge", &emerge_time);
4051 // Units of these are MapBlocks
4052 v3s16 p_min = blockpos_min;
4053 v3s16 p_max = blockpos_max;
4055 VoxelArea block_area_nodes
4056 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4058 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4061 dstream<<"initialEmerge: area: ";
4062 block_area_nodes.print(dstream);
4063 dstream<<" ("<<size_MB<<"MB)";
4067 addArea(block_area_nodes);
4069 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4070 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4071 for(s32 x=p_min.X; x<=p_max.X; x++)
4074 core::map<v3s16, bool>::Node *n;
4075 n = m_loaded_blocks.find(p);
4079 bool block_data_inexistent = false;
4082 TimeTaker timer1("emerge load", &emerge_load_time);
4084 MapBlock *block = m_map->getBlockNoCreate(p);
4085 if(block->isDummy())
4086 block_data_inexistent = true;
4088 block->copyTo(*this);
4090 catch(InvalidPositionException &e)
4092 block_data_inexistent = true;
4095 if(block_data_inexistent)
4098 Mark area inexistent
4100 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4101 // Fill with VOXELFLAG_INEXISTENT
4102 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4103 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4105 s32 i = m_area.index(a.MinEdge.X,y,z);
4106 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4110 m_loaded_blocks.insert(p, !block_data_inexistent);
4114 void ManualMapVoxelManipulator::blitBackAll(
4115 core::map<v3s16, MapBlock*> * modified_blocks)
4117 if(m_area.getExtent() == v3s16(0,0,0))
4121 Copy data of all blocks
4123 for(core::map<v3s16, bool>::Iterator
4124 i = m_loaded_blocks.getIterator();
4125 i.atEnd() == false; i++)
4127 v3s16 p = i.getNode()->getKey();
4128 bool existed = i.getNode()->getValue();
4129 if(existed == false)
4131 // The Great Bug was found using this
4132 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4133 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4137 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4140 dstream<<"WARNING: "<<__FUNCTION_NAME
4141 <<": got NULL block "
4142 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4147 block->copyFrom(*this);
4150 modified_blocks->insert(p, block);