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 while(m_transforming_liquid.size() != 0)
1567 // This should be done here so that it is done when continue is used
1568 if(loopcount >= initial_size * 3)
1573 Get a queued transforming liquid node
1575 v3s16 p0 = m_transforming_liquid.pop_front();
1577 MapNode n0 = getNodeNoEx(p0);
1580 Collect information about current node
1582 s8 liquid_level = -1;
1583 u8 liquid_kind = CONTENT_IGNORE;
1584 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1585 switch (liquid_type) {
1587 liquid_level = LIQUID_LEVEL_SOURCE;
1588 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1590 case LIQUID_FLOWING:
1591 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1592 liquid_kind = n0.getContent();
1595 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1596 // continue with the next node.
1597 if (n0.getContent() != CONTENT_AIR)
1599 liquid_kind = CONTENT_AIR;
1604 Collect information about the environment
1606 const v3s16 *dirs = g_6dirs;
1607 NodeNeighbor sources[6]; // surrounding sources
1608 int num_sources = 0;
1609 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1611 NodeNeighbor airs[6]; // surrounding air
1613 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1614 int num_neutrals = 0;
1615 bool flowing_down = false;
1616 for (u16 i = 0; i < 6; i++) {
1617 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1620 nt = NEIGHBOR_UPPER;
1623 nt = NEIGHBOR_LOWER;
1626 v3s16 npos = p0 + dirs[i];
1627 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1628 switch (content_features(nb.n.getContent()).liquid_type) {
1630 if (nb.n.getContent() == CONTENT_AIR) {
1631 airs[num_airs++] = nb;
1632 // if the current node happens to be a flowing node, it will start to flow down here.
1633 if (nb.t == NEIGHBOR_LOWER)
1634 flowing_down = true;
1636 neutrals[num_neutrals++] = nb;
1640 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1641 if (liquid_kind == CONTENT_AIR)
1642 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1643 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1644 neutrals[num_neutrals++] = nb;
1646 sources[num_sources++] = nb;
1649 case LIQUID_FLOWING:
1650 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1651 if (liquid_kind == CONTENT_AIR)
1652 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1653 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1654 neutrals[num_neutrals++] = nb;
1656 flows[num_flows++] = nb;
1657 if (nb.t == NEIGHBOR_LOWER)
1658 flowing_down = true;
1665 decide on the type (and possibly level) of the current node
1667 content_t new_node_content;
1668 s8 new_node_level = -1;
1669 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1670 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1671 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1672 // it's perfectly safe to use liquid_kind here to determine the new node content.
1673 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1674 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1675 // liquid_kind is set properly, see above
1676 new_node_content = liquid_kind;
1677 new_node_level = LIQUID_LEVEL_MAX;
1679 // no surrounding sources, so get the maximum level that can flow into this node
1680 for (u16 i = 0; i < num_flows; i++) {
1681 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1682 switch (flows[i].t) {
1683 case NEIGHBOR_UPPER:
1684 if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
1685 new_node_level = LIQUID_LEVEL_MAX;
1686 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1687 new_node_level = nb_liquid_level + WATER_DROP_BOOST;
1690 case NEIGHBOR_LOWER:
1692 case NEIGHBOR_SAME_LEVEL:
1693 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1694 nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) {
1695 new_node_level = nb_liquid_level - 1;
1700 // don't flow as far in open terrain - if there isn't at least one adjacent solid block,
1701 // substract another unit from the resulting water level.
1702 if (!flowing_down && new_node_level >= 1) {
1703 bool at_wall = false;
1704 for (u16 i = 0; i < num_neutrals; i++) {
1705 if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) {
1711 new_node_level -= 1;
1714 if (new_node_level >= 0)
1715 new_node_content = liquid_kind;
1717 new_node_content = CONTENT_AIR;
1721 check if anything has changed. if not, just continue with the next node.
1723 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1724 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1725 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1731 update the current node
1733 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1734 n0.setContent(new_node_content);
1735 if (content_features(n0.getContent()).liquid_type == LIQUID_FLOWING) {
1736 // set level to last 3 bits, flowing down bit to 4th bit
1737 n0.param2 |= (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1739 // set the liquid level and flow bit to 0
1740 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1743 v3s16 blockpos = getNodeBlockPos(p0);
1744 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1746 modified_blocks.insert(blockpos, block);
1749 enqueue neighbors for update if neccessary
1751 switch (content_features(n0.getContent()).liquid_type) {
1753 // make sure source flows into all neighboring nodes
1754 for (u16 i = 0; i < num_flows; i++)
1755 if (flows[i].t != NEIGHBOR_UPPER)
1756 m_transforming_liquid.push_back(flows[i].p);
1757 for (u16 i = 0; i < num_airs; i++)
1758 if (airs[i].t != NEIGHBOR_UPPER)
1759 m_transforming_liquid.push_back(airs[i].p);
1762 // this flow has turned to air; neighboring flows might need to do the same
1763 for (u16 i = 0; i < num_flows; i++)
1764 m_transforming_liquid.push_back(flows[i].p);
1766 case LIQUID_FLOWING:
1767 for (u16 i = 0; i < num_flows; i++) {
1768 u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1769 // liquid_level is still the ORIGINAL level of this node.
1770 if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
1772 m_transforming_liquid.push_back(flows[i].p);
1774 for (u16 i = 0; i < num_airs; i++) {
1775 if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
1776 m_transforming_liquid.push_back(airs[i].p);
1781 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1784 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1786 v3s16 blockpos = getNodeBlockPos(p);
1787 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1788 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1791 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1795 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1799 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1801 v3s16 blockpos = getNodeBlockPos(p);
1802 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1803 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1806 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1810 block->m_node_metadata.set(p_rel, meta);
1813 void Map::removeNodeMetadata(v3s16 p)
1815 v3s16 blockpos = getNodeBlockPos(p);
1816 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1817 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1820 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1824 block->m_node_metadata.remove(p_rel);
1827 void Map::nodeMetadataStep(float dtime,
1828 core::map<v3s16, MapBlock*> &changed_blocks)
1832 Currently there is no way to ensure that all the necessary
1833 blocks are loaded when this is run. (They might get unloaded)
1834 NOTE: ^- Actually, that might not be so. In a quick test it
1835 reloaded a block with a furnace when I walked back to it from
1838 core::map<v2s16, MapSector*>::Iterator si;
1839 si = m_sectors.getIterator();
1840 for(; si.atEnd() == false; si++)
1842 MapSector *sector = si.getNode()->getValue();
1843 core::list< MapBlock * > sectorblocks;
1844 sector->getBlocks(sectorblocks);
1845 core::list< MapBlock * >::Iterator i;
1846 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1848 MapBlock *block = *i;
1849 bool changed = block->m_node_metadata.step(dtime);
1851 changed_blocks[block->getPos()] = block;
1860 ServerMap::ServerMap(std::string savedir):
1863 m_map_metadata_changed(true)
1865 dstream<<__FUNCTION_NAME<<std::endl;
1867 //m_chunksize = 8; // Takes a few seconds
1869 m_seed = (((u64)(myrand()%0xffff)<<0)
1870 + ((u64)(myrand()%0xffff)<<16)
1871 + ((u64)(myrand()%0xffff)<<32)
1872 + ((u64)(myrand()%0xffff)<<48));
1875 Experimental and debug stuff
1882 Try to load map; if not found, create a new one.
1885 m_savedir = savedir;
1886 m_map_saving_enabled = false;
1890 // If directory exists, check contents and load if possible
1891 if(fs::PathExists(m_savedir))
1893 // If directory is empty, it is safe to save into it.
1894 if(fs::GetDirListing(m_savedir).size() == 0)
1896 dstream<<DTIME<<"Server: Empty save directory is valid."
1898 m_map_saving_enabled = true;
1903 // Load map metadata (seed, chunksize)
1906 catch(FileNotGoodException &e){
1907 dstream<<DTIME<<"WARNING: Could not load map metadata"
1908 //<<" Disabling chunk-based generator."
1914 // Load chunk metadata
1917 catch(FileNotGoodException &e){
1918 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1919 <<" Disabling chunk-based generator."
1924 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1925 "metadata and sector (0,0) from "<<savedir<<
1926 ", assuming valid save directory."
1929 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1930 <<"and chunk metadata from "<<savedir
1931 <<", assuming valid save directory."
1934 m_map_saving_enabled = true;
1935 // Map loaded, not creating new one
1939 // If directory doesn't exist, it is safe to save to it
1941 m_map_saving_enabled = true;
1944 catch(std::exception &e)
1946 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1947 <<", exception: "<<e.what()<<std::endl;
1948 dstream<<"Please remove the map or fix it."<<std::endl;
1949 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1952 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1954 // Create zero sector
1955 emergeSector(v2s16(0,0));
1957 // Initially write whole map
1961 ServerMap::~ServerMap()
1963 dstream<<__FUNCTION_NAME<<std::endl;
1967 if(m_map_saving_enabled)
1969 // Save only changed parts
1971 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1975 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1978 catch(std::exception &e)
1980 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1981 <<", exception: "<<e.what()<<std::endl;
1988 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1989 for(; i.atEnd() == false; i++)
1991 MapChunk *chunk = i.getNode()->getValue();
1997 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
1999 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2000 if(enable_mapgen_debug_info)
2001 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2002 <<blockpos.Z<<")"<<std::endl;
2004 // Do nothing if not inside limits (+-1 because of neighbors)
2005 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2006 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2012 data->no_op = false;
2013 data->seed = m_seed;
2014 data->blockpos = blockpos;
2017 Create the whole area of this and the neighboring blocks
2020 //TimeTaker timer("initBlockMake() create area");
2022 for(s16 x=-1; x<=1; x++)
2023 for(s16 z=-1; z<=1; z++)
2025 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2026 // Sector metadata is loaded from disk if not already loaded.
2027 ServerMapSector *sector = createSector(sectorpos);
2030 for(s16 y=-1; y<=1; y++)
2032 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2033 //MapBlock *block = createBlock(p);
2034 // 1) get from memory, 2) load from disk
2035 MapBlock *block = emergeBlock(p, false);
2036 // 3) create a blank one
2039 block = createBlock(p);
2042 Block gets sunlight if this is true.
2044 Refer to the map generator heuristics.
2046 bool ug = mapgen::block_is_underground(data->seed, p);
2047 block->setIsUnderground(ug);
2050 // Lighting will not be valid after make_chunk is called
2051 block->setLightingExpired(true);
2052 // Lighting will be calculated
2053 //block->setLightingExpired(false);
2059 Now we have a big empty area.
2061 Make a ManualMapVoxelManipulator that contains this and the
2065 // The area that contains this block and it's neighbors
2066 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2067 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2069 data->vmanip = new ManualMapVoxelManipulator(this);
2070 //data->vmanip->setMap(this);
2074 //TimeTaker timer("initBlockMake() initialEmerge");
2075 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2078 // Data is ready now.
2081 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2082 core::map<v3s16, MapBlock*> &changed_blocks)
2084 v3s16 blockpos = data->blockpos;
2085 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2086 <<blockpos.Z<<")"<<std::endl;*/
2090 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2094 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2096 /*dstream<<"Resulting vmanip:"<<std::endl;
2097 data->vmanip.print(dstream);*/
2100 Blit generated stuff to map
2101 NOTE: blitBackAll adds nearly everything to changed_blocks
2105 //TimeTaker timer("finishBlockMake() blitBackAll");
2106 data->vmanip->blitBackAll(&changed_blocks);
2109 if(enable_mapgen_debug_info)
2110 dstream<<"finishBlockMake: changed_blocks.size()="
2111 <<changed_blocks.size()<<std::endl;
2114 Copy transforming liquid information
2116 while(data->transforming_liquid.size() > 0)
2118 v3s16 p = data->transforming_liquid.pop_front();
2119 m_transforming_liquid.push_back(p);
2125 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2129 Set is_underground flag for lighting with sunlight.
2131 Refer to map generator heuristics.
2133 NOTE: This is done in initChunkMake
2135 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2139 Add sunlight to central block.
2140 This makes in-dark-spawning monsters to not flood the whole thing.
2141 Do not spread the light, though.
2143 /*core::map<v3s16, bool> light_sources;
2144 bool black_air_left = false;
2145 block->propagateSunlight(light_sources, true, &black_air_left);*/
2148 NOTE: Lighting and object adding shouldn't really be here, but
2149 lighting is a bit tricky to move properly to makeBlock.
2150 TODO: Do this the right way anyway, that is, move it to makeBlock.
2151 - There needs to be some way for makeBlock to report back if
2152 the lighting update is going further down because of the
2153 new block blocking light
2158 NOTE: This takes ~60ms, TODO: Investigate why
2161 TimeTaker t("finishBlockMake lighting update");
2163 core::map<v3s16, MapBlock*> lighting_update_blocks;
2166 lighting_update_blocks.insert(block->getPos(), block);
2171 v3s16 p = block->getPos()+v3s16(x,1,z);
2172 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2176 // All modified blocks
2177 // NOTE: Should this be done? If this is not done, then the lighting
2178 // of the others will be updated in a different place, one by one, i
2179 // think... or they might not? Well, at least they are left marked as
2180 // "lighting expired"; it seems that is not handled at all anywhere,
2181 // so enabling this will slow it down A LOT because otherwise it
2182 // would not do this at all. This causes the black trees.
2183 for(core::map<v3s16, MapBlock*>::Iterator
2184 i = changed_blocks.getIterator();
2185 i.atEnd() == false; i++)
2187 lighting_update_blocks.insert(i.getNode()->getKey(),
2188 i.getNode()->getValue());
2190 /*// Also force-add all the upmost blocks for proper sunlight
2191 for(s16 x=-1; x<=1; x++)
2192 for(s16 z=-1; z<=1; z++)
2194 v3s16 p = block->getPos()+v3s16(x,1,z);
2195 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2198 updateLighting(lighting_update_blocks, changed_blocks);
2201 Set lighting to non-expired state in all of them.
2202 This is cheating, but it is not fast enough if all of them
2203 would actually be updated.
2205 for(s16 x=-1; x<=1; x++)
2206 for(s16 y=-1; y<=1; y++)
2207 for(s16 z=-1; z<=1; z++)
2209 v3s16 p = block->getPos()+v3s16(x,y,z);
2210 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2213 if(enable_mapgen_debug_info == false)
2214 t.stop(true); // Hide output
2218 Add random objects to block
2220 mapgen::add_random_objects(block);
2223 Go through changed blocks
2225 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2226 i.atEnd() == false; i++)
2228 MapBlock *block = i.getNode()->getValue();
2231 Update day/night difference cache of the MapBlocks
2233 block->updateDayNightDiff();
2235 Set block as modified
2237 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2241 Set central block as generated
2243 block->setGenerated(true);
2246 Save changed parts of map
2247 NOTE: Will be saved later.
2251 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2252 <<blockpos.Z<<")"<<std::endl;*/
2254 if(enable_mapgen_debug_info)
2257 Analyze resulting blocks
2259 for(s16 x=-1; x<=1; x++)
2260 for(s16 y=-1; y<=1; y++)
2261 for(s16 z=-1; z<=1; z++)
2263 v3s16 p = block->getPos()+v3s16(x,y,z);
2264 MapBlock *block = getBlockNoCreateNoEx(p);
2266 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2267 dstream<<"Generated "<<spos<<": "
2268 <<analyze_block(block)<<std::endl;
2276 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2278 DSTACKF("%s: p2d=(%d,%d)",
2283 Check if it exists already in memory
2285 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2290 Try to load it from disk (with blocks)
2292 //if(loadSectorFull(p2d) == true)
2295 Try to load metadata from disk
2297 if(loadSectorMeta(p2d) == true)
2299 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2302 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2303 throw InvalidPositionException("");
2309 Do not create over-limit
2311 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2312 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2313 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2314 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2315 throw InvalidPositionException("createSector(): pos. over limit");
2318 Generate blank sector
2321 sector = new ServerMapSector(this, p2d);
2323 // Sector position on map in nodes
2324 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2329 m_sectors.insert(p2d, sector);
2335 This is a quick-hand function for calling makeBlock().
2337 MapBlock * ServerMap::generateBlock(
2339 core::map<v3s16, MapBlock*> &modified_blocks
2342 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2344 /*dstream<<"generateBlock(): "
2345 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2348 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2350 TimeTaker timer("generateBlock");
2352 //MapBlock *block = original_dummy;
2354 v2s16 p2d(p.X, p.Z);
2355 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2358 Do not generate over-limit
2360 if(blockpos_over_limit(p))
2362 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2363 throw InvalidPositionException("generateBlock(): pos. over limit");
2367 Create block make data
2369 mapgen::BlockMakeData data;
2370 initBlockMake(&data, p);
2376 TimeTaker t("mapgen::make_block()");
2377 mapgen::make_block(&data);
2379 if(enable_mapgen_debug_info == false)
2380 t.stop(true); // Hide output
2384 Blit data back on map, update lighting, add mobs and whatever this does
2386 finishBlockMake(&data, modified_blocks);
2391 MapBlock *block = getBlockNoCreateNoEx(p);
2399 bool erroneus_content = false;
2400 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2401 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2402 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2405 MapNode n = block->getNode(p);
2406 if(n.getContent() == CONTENT_IGNORE)
2408 dstream<<"CONTENT_IGNORE at "
2409 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2411 erroneus_content = true;
2415 if(erroneus_content)
2424 Generate a completely empty block
2428 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2429 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2431 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2435 n.setContent(CONTENT_AIR);
2437 n.setContent(CONTENT_STONE);
2438 block->setNode(v3s16(x0,y0,z0), n);
2444 if(enable_mapgen_debug_info == false)
2445 timer.stop(true); // Hide output
2450 MapBlock * ServerMap::createBlock(v3s16 p)
2452 DSTACKF("%s: p=(%d,%d,%d)",
2453 __FUNCTION_NAME, p.X, p.Y, p.Z);
2456 Do not create over-limit
2458 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2459 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2460 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2461 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2462 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2463 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2464 throw InvalidPositionException("createBlock(): pos. over limit");
2466 v2s16 p2d(p.X, p.Z);
2469 This will create or load a sector if not found in memory.
2470 If block exists on disk, it will be loaded.
2472 NOTE: On old save formats, this will be slow, as it generates
2473 lighting on blocks for them.
2475 ServerMapSector *sector;
2477 sector = (ServerMapSector*)createSector(p2d);
2478 assert(sector->getId() == MAPSECTOR_SERVER);
2480 catch(InvalidPositionException &e)
2482 dstream<<"createBlock: createSector() failed"<<std::endl;
2486 NOTE: This should not be done, or at least the exception
2487 should not be passed on as std::exception, because it
2488 won't be catched at all.
2490 /*catch(std::exception &e)
2492 dstream<<"createBlock: createSector() failed: "
2493 <<e.what()<<std::endl;
2498 Try to get a block from the sector
2501 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2504 if(block->isDummy())
2509 block = sector->createBlankBlock(block_y);
2513 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2515 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2517 p.X, p.Y, p.Z, allow_generate);
2520 MapBlock *block = getBlockNoCreateNoEx(p);
2521 if(block && block->isDummy() == false)
2526 MapBlock *block = loadBlock(p);
2533 core::map<v3s16, MapBlock*> modified_blocks;
2534 MapBlock *block = generateBlock(p, modified_blocks);
2538 event.type = MEET_OTHER;
2541 // Copy modified_blocks to event
2542 for(core::map<v3s16, MapBlock*>::Iterator
2543 i = modified_blocks.getIterator();
2544 i.atEnd()==false; i++)
2546 event.modified_blocks.insert(i.getNode()->getKey(), false);
2550 dispatchEvent(&event);
2561 Do not generate over-limit
2563 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2564 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2565 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2566 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2567 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2568 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2569 throw InvalidPositionException("emergeBlock(): pos. over limit");
2571 v2s16 p2d(p.X, p.Z);
2574 This will create or load a sector if not found in memory.
2575 If block exists on disk, it will be loaded.
2577 ServerMapSector *sector;
2579 sector = createSector(p2d);
2580 //sector = emergeSector(p2d, changed_blocks);
2582 catch(InvalidPositionException &e)
2584 dstream<<"emergeBlock: createSector() failed: "
2585 <<e.what()<<std::endl;
2586 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2588 <<"You could try to delete it."<<std::endl;
2591 catch(VersionMismatchException &e)
2593 dstream<<"emergeBlock: createSector() failed: "
2594 <<e.what()<<std::endl;
2595 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2597 <<"You could try to delete it."<<std::endl;
2602 Try to get a block from the sector
2605 bool does_not_exist = false;
2606 bool lighting_expired = false;
2607 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2609 // If not found, try loading from disk
2612 block = loadBlock(p);
2618 does_not_exist = true;
2620 else if(block->isDummy() == true)
2622 does_not_exist = true;
2624 else if(block->getLightingExpired())
2626 lighting_expired = true;
2631 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2636 If block was not found on disk and not going to generate a
2637 new one, make sure there is a dummy block in place.
2639 if(only_from_disk && (does_not_exist || lighting_expired))
2641 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2645 // Create dummy block
2646 block = new MapBlock(this, p, true);
2648 // Add block to sector
2649 sector->insertBlock(block);
2655 //dstream<<"Not found on disk, generating."<<std::endl;
2657 //TimeTaker("emergeBlock() generate");
2659 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2662 If the block doesn't exist, generate the block.
2666 block = generateBlock(p, block, sector, changed_blocks,
2667 lighting_invalidated_blocks);
2670 if(lighting_expired)
2672 lighting_invalidated_blocks.insert(p, block);
2677 Initially update sunlight
2680 core::map<v3s16, bool> light_sources;
2681 bool black_air_left = false;
2682 bool bottom_invalid =
2683 block->propagateSunlight(light_sources, true,
2686 // If sunlight didn't reach everywhere and part of block is
2687 // above ground, lighting has to be properly updated
2688 //if(black_air_left && some_part_underground)
2691 lighting_invalidated_blocks[block->getPos()] = block;
2696 lighting_invalidated_blocks[block->getPos()] = block;
2705 s16 ServerMap::findGroundLevel(v2s16 p2d)
2709 Uh, just do something random...
2711 // Find existing map from top to down
2714 v3s16 p(p2d.X, max, p2d.Y);
2715 for(; p.Y>min; p.Y--)
2717 MapNode n = getNodeNoEx(p);
2718 if(n.getContent() != CONTENT_IGNORE)
2723 // If this node is not air, go to plan b
2724 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2726 // Search existing walkable and return it
2727 for(; p.Y>min; p.Y--)
2729 MapNode n = getNodeNoEx(p);
2730 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2739 Determine from map generator noise functions
2742 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2745 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2746 //return (s16)level;
2749 void ServerMap::createDirs(std::string path)
2751 if(fs::CreateAllDirs(path) == false)
2753 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2754 <<"\""<<path<<"\""<<std::endl;
2755 throw BaseException("ServerMap failed to create directory");
2759 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2765 snprintf(cc, 9, "%.4x%.4x",
2766 (unsigned int)pos.X&0xffff,
2767 (unsigned int)pos.Y&0xffff);
2769 return m_savedir + "/sectors/" + cc;
2771 snprintf(cc, 9, "%.3x/%.3x",
2772 (unsigned int)pos.X&0xfff,
2773 (unsigned int)pos.Y&0xfff);
2775 return m_savedir + "/sectors2/" + cc;
2781 v2s16 ServerMap::getSectorPos(std::string dirname)
2785 size_t spos = dirname.rfind('/') + 1;
2786 assert(spos != std::string::npos);
2787 if(dirname.size() - spos == 8)
2790 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2792 else if(dirname.size() - spos == 3)
2795 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2796 // Sign-extend the 12 bit values up to 16 bits...
2797 if(x&0x800) x|=0xF000;
2798 if(y&0x800) y|=0xF000;
2805 v2s16 pos((s16)x, (s16)y);
2809 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2811 v2s16 p2d = getSectorPos(sectordir);
2813 if(blockfile.size() != 4){
2814 throw InvalidFilenameException("Invalid block filename");
2817 int r = sscanf(blockfile.c_str(), "%4x", &y);
2819 throw InvalidFilenameException("Invalid block filename");
2820 return v3s16(p2d.X, y, p2d.Y);
2823 std::string ServerMap::getBlockFilename(v3s16 p)
2826 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2830 void ServerMap::save(bool only_changed)
2832 DSTACK(__FUNCTION_NAME);
2833 if(m_map_saving_enabled == false)
2835 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2839 if(only_changed == false)
2840 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2843 if(only_changed == false || m_map_metadata_changed)
2848 u32 sector_meta_count = 0;
2849 u32 block_count = 0;
2850 u32 block_count_all = 0; // Number of blocks in memory
2852 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2853 for(; i.atEnd() == false; i++)
2855 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2856 assert(sector->getId() == MAPSECTOR_SERVER);
2858 if(sector->differs_from_disk || only_changed == false)
2860 saveSectorMeta(sector);
2861 sector_meta_count++;
2863 core::list<MapBlock*> blocks;
2864 sector->getBlocks(blocks);
2865 core::list<MapBlock*>::Iterator j;
2866 for(j=blocks.begin(); j!=blocks.end(); j++)
2868 MapBlock *block = *j;
2872 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2873 || only_changed == false)
2878 /*dstream<<"ServerMap: Written block ("
2879 <<block->getPos().X<<","
2880 <<block->getPos().Y<<","
2881 <<block->getPos().Z<<")"
2888 Only print if something happened or saved whole map
2890 if(only_changed == false || sector_meta_count != 0
2891 || block_count != 0)
2893 dstream<<DTIME<<"ServerMap: Written: "
2894 <<sector_meta_count<<" sector metadata files, "
2895 <<block_count<<" block files"
2896 <<", "<<block_count_all<<" blocks in memory."
2901 void ServerMap::saveMapMeta()
2903 DSTACK(__FUNCTION_NAME);
2905 dstream<<"INFO: ServerMap::saveMapMeta(): "
2909 createDirs(m_savedir);
2911 std::string fullpath = m_savedir + "/map_meta.txt";
2912 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2913 if(os.good() == false)
2915 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2916 <<"could not open"<<fullpath<<std::endl;
2917 throw FileNotGoodException("Cannot open chunk metadata");
2921 params.setU64("seed", m_seed);
2923 params.writeLines(os);
2925 os<<"[end_of_params]\n";
2927 m_map_metadata_changed = false;
2930 void ServerMap::loadMapMeta()
2932 DSTACK(__FUNCTION_NAME);
2934 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2937 std::string fullpath = m_savedir + "/map_meta.txt";
2938 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2939 if(is.good() == false)
2941 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2942 <<"could not open"<<fullpath<<std::endl;
2943 throw FileNotGoodException("Cannot open map metadata");
2951 throw SerializationError
2952 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2954 std::getline(is, line);
2955 std::string trimmedline = trim(line);
2956 if(trimmedline == "[end_of_params]")
2958 params.parseConfigLine(line);
2961 m_seed = params.getU64("seed");
2963 dstream<<"INFO: ServerMap::loadMapMeta(): "
2968 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2970 DSTACK(__FUNCTION_NAME);
2971 // Format used for writing
2972 u8 version = SER_FMT_VER_HIGHEST;
2974 v2s16 pos = sector->getPos();
2975 std::string dir = getSectorDir(pos);
2978 std::string fullpath = dir + "/meta";
2979 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2980 if(o.good() == false)
2981 throw FileNotGoodException("Cannot open sector metafile");
2983 sector->serialize(o, version);
2985 sector->differs_from_disk = false;
2988 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2990 DSTACK(__FUNCTION_NAME);
2992 v2s16 p2d = getSectorPos(sectordir);
2994 ServerMapSector *sector = NULL;
2996 std::string fullpath = sectordir + "/meta";
2997 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2998 if(is.good() == false)
3000 // If the directory exists anyway, it probably is in some old
3001 // format. Just go ahead and create the sector.
3002 if(fs::PathExists(sectordir))
3004 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3005 <<fullpath<<" doesn't exist but directory does."
3006 <<" Continuing with a sector with no metadata."
3008 sector = new ServerMapSector(this, p2d);
3009 m_sectors.insert(p2d, sector);
3013 throw FileNotGoodException("Cannot open sector metafile");
3018 sector = ServerMapSector::deSerialize
3019 (is, this, p2d, m_sectors);
3021 saveSectorMeta(sector);
3024 sector->differs_from_disk = false;
3029 bool ServerMap::loadSectorMeta(v2s16 p2d)
3031 DSTACK(__FUNCTION_NAME);
3033 MapSector *sector = NULL;
3035 // The directory layout we're going to load from.
3036 // 1 - original sectors/xxxxzzzz/
3037 // 2 - new sectors2/xxx/zzz/
3038 // If we load from anything but the latest structure, we will
3039 // immediately save to the new one, and remove the old.
3041 std::string sectordir1 = getSectorDir(p2d, 1);
3042 std::string sectordir;
3043 if(fs::PathExists(sectordir1))
3045 sectordir = sectordir1;
3050 sectordir = getSectorDir(p2d, 2);
3054 sector = loadSectorMeta(sectordir, loadlayout != 2);
3056 catch(InvalidFilenameException &e)
3060 catch(FileNotGoodException &e)
3064 catch(std::exception &e)
3073 bool ServerMap::loadSectorFull(v2s16 p2d)
3075 DSTACK(__FUNCTION_NAME);
3077 MapSector *sector = NULL;
3079 // The directory layout we're going to load from.
3080 // 1 - original sectors/xxxxzzzz/
3081 // 2 - new sectors2/xxx/zzz/
3082 // If we load from anything but the latest structure, we will
3083 // immediately save to the new one, and remove the old.
3085 std::string sectordir1 = getSectorDir(p2d, 1);
3086 std::string sectordir;
3087 if(fs::PathExists(sectordir1))
3089 sectordir = sectordir1;
3094 sectordir = getSectorDir(p2d, 2);
3098 sector = loadSectorMeta(sectordir, loadlayout != 2);
3100 catch(InvalidFilenameException &e)
3104 catch(FileNotGoodException &e)
3108 catch(std::exception &e)
3116 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3118 std::vector<fs::DirListNode>::iterator i2;
3119 for(i2=list2.begin(); i2!=list2.end(); i2++)
3125 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3127 catch(InvalidFilenameException &e)
3129 // This catches unknown crap in directory
3135 dstream<<"Sector converted to new layout - deleting "<<
3136 sectordir1<<std::endl;
3137 fs::RecursiveDelete(sectordir1);
3144 void ServerMap::saveBlock(MapBlock *block)
3146 DSTACK(__FUNCTION_NAME);
3148 Dummy blocks are not written
3150 if(block->isDummy())
3152 /*v3s16 p = block->getPos();
3153 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3154 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3158 // Format used for writing
3159 u8 version = SER_FMT_VER_HIGHEST;
3161 v3s16 p3d = block->getPos();
3163 v2s16 p2d(p3d.X, p3d.Z);
3164 std::string sectordir = getSectorDir(p2d);
3166 createDirs(sectordir);
3168 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3169 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3170 if(o.good() == false)
3171 throw FileNotGoodException("Cannot open block data");
3174 [0] u8 serialization version
3177 o.write((char*)&version, 1);
3180 block->serialize(o, version);
3182 // Write extra data stored on disk
3183 block->serializeDiskExtra(o, version);
3185 // We just wrote it to the disk so clear modified flag
3186 block->resetModified();
3189 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3191 DSTACK(__FUNCTION_NAME);
3193 std::string fullpath = sectordir+"/"+blockfile;
3196 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3197 if(is.good() == false)
3198 throw FileNotGoodException("Cannot open block file");
3200 v3s16 p3d = getBlockPos(sectordir, blockfile);
3201 v2s16 p2d(p3d.X, p3d.Z);
3203 assert(sector->getPos() == p2d);
3205 u8 version = SER_FMT_VER_INVALID;
3206 is.read((char*)&version, 1);
3209 throw SerializationError("ServerMap::loadBlock(): Failed"
3210 " to read MapBlock version");
3212 /*u32 block_size = MapBlock::serializedLength(version);
3213 SharedBuffer<u8> data(block_size);
3214 is.read((char*)*data, block_size);*/
3216 // This will always return a sector because we're the server
3217 //MapSector *sector = emergeSector(p2d);
3219 MapBlock *block = NULL;
3220 bool created_new = false;
3221 block = sector->getBlockNoCreateNoEx(p3d.Y);
3224 block = sector->createBlankBlockNoInsert(p3d.Y);
3229 block->deSerialize(is, version);
3231 // Read extra data stored on disk
3232 block->deSerializeDiskExtra(is, version);
3234 // If it's a new block, insert it to the map
3236 sector->insertBlock(block);
3239 Save blocks loaded in old format in new format
3242 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3247 // We just loaded it from the disk, so it's up-to-date.
3248 block->resetModified();
3251 catch(SerializationError &e)
3253 dstream<<"WARNING: Invalid block data on disk "
3254 <<"fullpath="<<fullpath
3255 <<" (SerializationError). "
3256 <<"what()="<<e.what()
3258 //" Ignoring. A new one will be generated.
3261 // TODO: Backup file; name is in fullpath.
3265 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3267 DSTACK(__FUNCTION_NAME);
3269 v2s16 p2d(blockpos.X, blockpos.Z);
3271 // The directory layout we're going to load from.
3272 // 1 - original sectors/xxxxzzzz/
3273 // 2 - new sectors2/xxx/zzz/
3274 // If we load from anything but the latest structure, we will
3275 // immediately save to the new one, and remove the old.
3277 std::string sectordir1 = getSectorDir(p2d, 1);
3278 std::string sectordir;
3279 if(fs::PathExists(sectordir1))
3281 sectordir = sectordir1;
3286 sectordir = getSectorDir(p2d, 2);
3290 Make sure sector is loaded
3292 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3296 sector = loadSectorMeta(sectordir, loadlayout != 2);
3298 catch(InvalidFilenameException &e)
3302 catch(FileNotGoodException &e)
3306 catch(std::exception &e)
3313 Make sure file exists
3316 std::string blockfilename = getBlockFilename(blockpos);
3317 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3323 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3324 return getBlockNoCreateNoEx(blockpos);
3327 void ServerMap::PrintInfo(std::ostream &out)
3338 ClientMap::ClientMap(
3340 MapDrawControl &control,
3341 scene::ISceneNode* parent,
3342 scene::ISceneManager* mgr,
3346 scene::ISceneNode(parent, mgr, id),
3349 m_camera_position(0,0,0),
3350 m_camera_direction(0,0,1)
3352 m_camera_mutex.Init();
3353 assert(m_camera_mutex.IsInitialized());
3355 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3356 BS*1000000,BS*1000000,BS*1000000);
3359 ClientMap::~ClientMap()
3361 /*JMutexAutoLock lock(mesh_mutex);
3370 MapSector * ClientMap::emergeSector(v2s16 p2d)
3372 DSTACK(__FUNCTION_NAME);
3373 // Check that it doesn't exist already
3375 return getSectorNoGenerate(p2d);
3377 catch(InvalidPositionException &e)
3382 ClientMapSector *sector = new ClientMapSector(this, p2d);
3385 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3386 m_sectors.insert(p2d, sector);
3393 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3395 DSTACK(__FUNCTION_NAME);
3396 ClientMapSector *sector = NULL;
3398 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3400 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3404 sector = (ClientMapSector*)n->getValue();
3405 assert(sector->getId() == MAPSECTOR_CLIENT);
3409 sector = new ClientMapSector(this, p2d);
3411 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3412 m_sectors.insert(p2d, sector);
3416 sector->deSerialize(is);
3420 void ClientMap::OnRegisterSceneNode()
3424 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3425 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3428 ISceneNode::OnRegisterSceneNode();
3431 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3433 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3434 DSTACK(__FUNCTION_NAME);
3436 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3439 This is called two times per frame, reset on the non-transparent one
3441 if(pass == scene::ESNRP_SOLID)
3443 m_last_drawn_sectors.clear();
3447 Get time for measuring timeout.
3449 Measuring time is very useful for long delays when the
3450 machine is swapping a lot.
3452 int time1 = time(0);
3454 //u32 daynight_ratio = m_client->getDayNightRatio();
3456 m_camera_mutex.Lock();
3457 v3f camera_position = m_camera_position;
3458 v3f camera_direction = m_camera_direction;
3459 m_camera_mutex.Unlock();
3462 Get all blocks and draw all visible ones
3465 v3s16 cam_pos_nodes(
3466 camera_position.X / BS,
3467 camera_position.Y / BS,
3468 camera_position.Z / BS);
3470 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3472 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3473 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3475 // Take a fair amount as we will be dropping more out later
3477 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3478 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3479 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3481 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3482 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3483 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3485 u32 vertex_count = 0;
3487 // For limiting number of mesh updates per frame
3488 u32 mesh_update_count = 0;
3490 u32 blocks_would_have_drawn = 0;
3491 u32 blocks_drawn = 0;
3493 int timecheck_counter = 0;
3494 core::map<v2s16, MapSector*>::Iterator si;
3495 si = m_sectors.getIterator();
3496 for(; si.atEnd() == false; si++)
3499 timecheck_counter++;
3500 if(timecheck_counter > 50)
3502 timecheck_counter = 0;
3503 int time2 = time(0);
3504 if(time2 > time1 + 4)
3506 dstream<<"ClientMap::renderMap(): "
3507 "Rendering takes ages, returning."
3514 MapSector *sector = si.getNode()->getValue();
3515 v2s16 sp = sector->getPos();
3517 if(m_control.range_all == false)
3519 if(sp.X < p_blocks_min.X
3520 || sp.X > p_blocks_max.X
3521 || sp.Y < p_blocks_min.Z
3522 || sp.Y > p_blocks_max.Z)
3526 core::list< MapBlock * > sectorblocks;
3527 sector->getBlocks(sectorblocks);
3533 u32 sector_blocks_drawn = 0;
3535 core::list< MapBlock * >::Iterator i;
3536 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3538 MapBlock *block = *i;
3541 Compare block position to camera position, skip
3542 if not seen on display
3545 float range = 100000 * BS;
3546 if(m_control.range_all == false)
3547 range = m_control.wanted_range * BS;
3550 if(isBlockInSight(block->getPos(), camera_position,
3551 camera_direction, range, &d) == false)
3556 // Okay, this block will be drawn. Reset usage timer.
3557 block->resetUsageTimer();
3559 // This is ugly (spherical distance limit?)
3560 /*if(m_control.range_all == false &&
3561 d - 0.5*BS*MAP_BLOCKSIZE > range)
3566 Update expired mesh (used for day/night change)
3568 It doesn't work exactly like it should now with the
3569 tasked mesh update but whatever.
3572 bool mesh_expired = false;
3575 JMutexAutoLock lock(block->mesh_mutex);
3577 mesh_expired = block->getMeshExpired();
3579 // Mesh has not been expired and there is no mesh:
3580 // block has no content
3581 if(block->mesh == NULL && mesh_expired == false)
3585 f32 faraway = BS*50;
3586 //f32 faraway = m_control.wanted_range * BS;
3589 This has to be done with the mesh_mutex unlocked
3591 // Pretty random but this should work somewhat nicely
3592 if(mesh_expired && (
3593 (mesh_update_count < 3
3594 && (d < faraway || mesh_update_count < 2)
3597 (m_control.range_all && mesh_update_count < 20)
3600 /*if(mesh_expired && mesh_update_count < 6
3601 && (d < faraway || mesh_update_count < 3))*/
3603 mesh_update_count++;
3605 // Mesh has been expired: generate new mesh
3606 //block->updateMesh(daynight_ratio);
3607 m_client->addUpdateMeshTask(block->getPos());
3609 mesh_expired = false;
3614 Draw the faces of the block
3617 JMutexAutoLock lock(block->mesh_mutex);
3619 scene::SMesh *mesh = block->mesh;
3624 blocks_would_have_drawn++;
3625 if(blocks_drawn >= m_control.wanted_max_blocks
3626 && m_control.range_all == false
3627 && d > m_control.wanted_min_range * BS)
3631 sector_blocks_drawn++;
3633 u32 c = mesh->getMeshBufferCount();
3635 for(u32 i=0; i<c; i++)
3637 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3638 const video::SMaterial& material = buf->getMaterial();
3639 video::IMaterialRenderer* rnd =
3640 driver->getMaterialRenderer(material.MaterialType);
3641 bool transparent = (rnd && rnd->isTransparent());
3642 // Render transparent on transparent pass and likewise.
3643 if(transparent == is_transparent_pass)
3646 This *shouldn't* hurt too much because Irrlicht
3647 doesn't change opengl textures if the old
3648 material is set again.
3650 driver->setMaterial(buf->getMaterial());
3651 driver->drawMeshBuffer(buf);
3652 vertex_count += buf->getVertexCount();
3656 } // foreach sectorblocks
3658 if(sector_blocks_drawn != 0)
3660 m_last_drawn_sectors[sp] = true;
3664 m_control.blocks_drawn = blocks_drawn;
3665 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3667 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3668 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3671 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3672 core::map<v3s16, MapBlock*> *affected_blocks)
3674 bool changed = false;
3676 Add it to all blocks touching it
3679 v3s16(0,0,0), // this
3680 v3s16(0,0,1), // back
3681 v3s16(0,1,0), // top
3682 v3s16(1,0,0), // right
3683 v3s16(0,0,-1), // front
3684 v3s16(0,-1,0), // bottom
3685 v3s16(-1,0,0), // left
3687 for(u16 i=0; i<7; i++)
3689 v3s16 p2 = p + dirs[i];
3690 // Block position of neighbor (or requested) node
3691 v3s16 blockpos = getNodeBlockPos(p2);
3692 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3693 if(blockref == NULL)
3695 // Relative position of requested node
3696 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3697 if(blockref->setTempMod(relpos, mod))
3702 if(changed && affected_blocks!=NULL)
3704 for(u16 i=0; i<7; i++)
3706 v3s16 p2 = p + dirs[i];
3707 // Block position of neighbor (or requested) node
3708 v3s16 blockpos = getNodeBlockPos(p2);
3709 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3710 if(blockref == NULL)
3712 affected_blocks->insert(blockpos, blockref);
3718 bool ClientMap::clearTempMod(v3s16 p,
3719 core::map<v3s16, MapBlock*> *affected_blocks)
3721 bool changed = false;
3723 v3s16(0,0,0), // this
3724 v3s16(0,0,1), // back
3725 v3s16(0,1,0), // top
3726 v3s16(1,0,0), // right
3727 v3s16(0,0,-1), // front
3728 v3s16(0,-1,0), // bottom
3729 v3s16(-1,0,0), // left
3731 for(u16 i=0; i<7; i++)
3733 v3s16 p2 = p + dirs[i];
3734 // Block position of neighbor (or requested) node
3735 v3s16 blockpos = getNodeBlockPos(p2);
3736 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3737 if(blockref == NULL)
3739 // Relative position of requested node
3740 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3741 if(blockref->clearTempMod(relpos))
3746 if(changed && affected_blocks!=NULL)
3748 for(u16 i=0; i<7; i++)
3750 v3s16 p2 = p + dirs[i];
3751 // Block position of neighbor (or requested) node
3752 v3s16 blockpos = getNodeBlockPos(p2);
3753 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3754 if(blockref == NULL)
3756 affected_blocks->insert(blockpos, blockref);
3762 void ClientMap::expireMeshes(bool only_daynight_diffed)
3764 TimeTaker timer("expireMeshes()");
3766 core::map<v2s16, MapSector*>::Iterator si;
3767 si = m_sectors.getIterator();
3768 for(; si.atEnd() == false; si++)
3770 MapSector *sector = si.getNode()->getValue();
3772 core::list< MapBlock * > sectorblocks;
3773 sector->getBlocks(sectorblocks);
3775 core::list< MapBlock * >::Iterator i;
3776 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3778 MapBlock *block = *i;
3780 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3786 JMutexAutoLock lock(block->mesh_mutex);
3787 if(block->mesh != NULL)
3789 /*block->mesh->drop();
3790 block->mesh = NULL;*/
3791 block->setMeshExpired(true);
3798 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3800 assert(mapType() == MAPTYPE_CLIENT);
3803 v3s16 p = blockpos + v3s16(0,0,0);
3804 MapBlock *b = getBlockNoCreate(p);
3805 b->updateMesh(daynight_ratio);
3806 //b->setMeshExpired(true);
3808 catch(InvalidPositionException &e){}
3811 v3s16 p = blockpos + v3s16(-1,0,0);
3812 MapBlock *b = getBlockNoCreate(p);
3813 b->updateMesh(daynight_ratio);
3814 //b->setMeshExpired(true);
3816 catch(InvalidPositionException &e){}
3818 v3s16 p = blockpos + v3s16(0,-1,0);
3819 MapBlock *b = getBlockNoCreate(p);
3820 b->updateMesh(daynight_ratio);
3821 //b->setMeshExpired(true);
3823 catch(InvalidPositionException &e){}
3825 v3s16 p = blockpos + v3s16(0,0,-1);
3826 MapBlock *b = getBlockNoCreate(p);
3827 b->updateMesh(daynight_ratio);
3828 //b->setMeshExpired(true);
3830 catch(InvalidPositionException &e){}
3835 Update mesh of block in which the node is, and if the node is at the
3836 leading edge, update the appropriate leading blocks too.
3838 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3846 v3s16 blockposes[4];
3847 for(u32 i=0; i<4; i++)
3849 v3s16 np = nodepos + dirs[i];
3850 blockposes[i] = getNodeBlockPos(np);
3851 // Don't update mesh of block if it has been done already
3852 bool already_updated = false;
3853 for(u32 j=0; j<i; j++)
3855 if(blockposes[j] == blockposes[i])
3857 already_updated = true;
3864 MapBlock *b = getBlockNoCreate(blockposes[i]);
3865 b->updateMesh(daynight_ratio);
3870 void ClientMap::PrintInfo(std::ostream &out)
3881 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3886 MapVoxelManipulator::~MapVoxelManipulator()
3888 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3892 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3894 TimeTaker timer1("emerge", &emerge_time);
3896 // Units of these are MapBlocks
3897 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3898 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3900 VoxelArea block_area_nodes
3901 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3903 addArea(block_area_nodes);
3905 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3906 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3907 for(s32 x=p_min.X; x<=p_max.X; x++)
3910 core::map<v3s16, bool>::Node *n;
3911 n = m_loaded_blocks.find(p);
3915 bool block_data_inexistent = false;
3918 TimeTaker timer1("emerge load", &emerge_load_time);
3920 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3921 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3924 dstream<<std::endl;*/
3926 MapBlock *block = m_map->getBlockNoCreate(p);
3927 if(block->isDummy())
3928 block_data_inexistent = true;
3930 block->copyTo(*this);
3932 catch(InvalidPositionException &e)
3934 block_data_inexistent = true;
3937 if(block_data_inexistent)
3939 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3940 // Fill with VOXELFLAG_INEXISTENT
3941 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3942 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3944 s32 i = m_area.index(a.MinEdge.X,y,z);
3945 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3949 m_loaded_blocks.insert(p, !block_data_inexistent);
3952 //dstream<<"emerge done"<<std::endl;
3956 SUGG: Add an option to only update eg. water and air nodes.
3957 This will make it interfere less with important stuff if
3960 void MapVoxelManipulator::blitBack
3961 (core::map<v3s16, MapBlock*> & modified_blocks)
3963 if(m_area.getExtent() == v3s16(0,0,0))
3966 //TimeTaker timer1("blitBack");
3968 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3969 <<m_loaded_blocks.size()<<std::endl;*/
3972 Initialize block cache
3974 v3s16 blockpos_last;
3975 MapBlock *block = NULL;
3976 bool block_checked_in_modified = false;
3978 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3979 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3980 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3984 u8 f = m_flags[m_area.index(p)];
3985 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3988 MapNode &n = m_data[m_area.index(p)];
3990 v3s16 blockpos = getNodeBlockPos(p);
3995 if(block == NULL || blockpos != blockpos_last){
3996 block = m_map->getBlockNoCreate(blockpos);
3997 blockpos_last = blockpos;
3998 block_checked_in_modified = false;
4001 // Calculate relative position in block
4002 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4004 // Don't continue if nothing has changed here
4005 if(block->getNode(relpos) == n)
4008 //m_map->setNode(m_area.MinEdge + p, n);
4009 block->setNode(relpos, n);
4012 Make sure block is in modified_blocks
4014 if(block_checked_in_modified == false)
4016 modified_blocks[blockpos] = block;
4017 block_checked_in_modified = true;
4020 catch(InvalidPositionException &e)
4026 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4027 MapVoxelManipulator(map),
4028 m_create_area(false)
4032 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4036 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4038 // Just create the area so that it can be pointed to
4039 VoxelManipulator::emerge(a, caller_id);
4042 void ManualMapVoxelManipulator::initialEmerge(
4043 v3s16 blockpos_min, v3s16 blockpos_max)
4045 TimeTaker timer1("initialEmerge", &emerge_time);
4047 // Units of these are MapBlocks
4048 v3s16 p_min = blockpos_min;
4049 v3s16 p_max = blockpos_max;
4051 VoxelArea block_area_nodes
4052 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4054 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4057 dstream<<"initialEmerge: area: ";
4058 block_area_nodes.print(dstream);
4059 dstream<<" ("<<size_MB<<"MB)";
4063 addArea(block_area_nodes);
4065 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4066 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4067 for(s32 x=p_min.X; x<=p_max.X; x++)
4070 core::map<v3s16, bool>::Node *n;
4071 n = m_loaded_blocks.find(p);
4075 bool block_data_inexistent = false;
4078 TimeTaker timer1("emerge load", &emerge_load_time);
4080 MapBlock *block = m_map->getBlockNoCreate(p);
4081 if(block->isDummy())
4082 block_data_inexistent = true;
4084 block->copyTo(*this);
4086 catch(InvalidPositionException &e)
4088 block_data_inexistent = true;
4091 if(block_data_inexistent)
4094 Mark area inexistent
4096 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4097 // Fill with VOXELFLAG_INEXISTENT
4098 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4099 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4101 s32 i = m_area.index(a.MinEdge.X,y,z);
4102 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4106 m_loaded_blocks.insert(p, !block_data_inexistent);
4110 void ManualMapVoxelManipulator::blitBackAll(
4111 core::map<v3s16, MapBlock*> * modified_blocks)
4113 if(m_area.getExtent() == v3s16(0,0,0))
4117 Copy data of all blocks
4119 for(core::map<v3s16, bool>::Iterator
4120 i = m_loaded_blocks.getIterator();
4121 i.atEnd() == false; i++)
4123 v3s16 p = i.getNode()->getKey();
4124 bool existed = i.getNode()->getValue();
4125 if(existed == false)
4127 // The Great Bug was found using this
4128 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4129 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4133 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4136 dstream<<"WARNING: "<<__FUNCTION_NAME
4137 <<": got NULL block "
4138 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4143 block->copyFrom(*this);
4146 modified_blocks->insert(p, block);