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 is a water source the neighbor
1633 // should be enqueded for transformation regardless of whether the
1634 // current node changes or not.
1635 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1636 m_transforming_liquid.push_back(npos);
1637 // if the current node happens to be a flowing node, it will start to flow down here.
1638 if (nb.t == NEIGHBOR_LOWER) {
1639 flowing_down = true;
1642 neutrals[num_neutrals++] = nb;
1646 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1647 if (liquid_kind == CONTENT_AIR)
1648 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1649 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1650 neutrals[num_neutrals++] = nb;
1652 sources[num_sources++] = nb;
1655 case LIQUID_FLOWING:
1656 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1657 if (liquid_kind == CONTENT_AIR)
1658 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1659 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1660 neutrals[num_neutrals++] = nb;
1662 flows[num_flows++] = nb;
1663 if (nb.t == NEIGHBOR_LOWER)
1664 flowing_down = true;
1671 decide on the type (and possibly level) of the current node
1673 content_t new_node_content;
1674 s8 new_node_level = -1;
1675 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1676 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1677 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1678 // it's perfectly safe to use liquid_kind here to determine the new node content.
1679 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1680 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1681 // liquid_kind is set properly, see above
1682 new_node_content = liquid_kind;
1683 new_node_level = LIQUID_LEVEL_MAX;
1685 // no surrounding sources, so get the maximum level that can flow into this node
1686 for (u16 i = 0; i < num_flows; i++) {
1687 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1688 switch (flows[i].t) {
1689 case NEIGHBOR_UPPER:
1690 if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
1691 new_node_level = LIQUID_LEVEL_MAX;
1692 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1693 new_node_level = nb_liquid_level + WATER_DROP_BOOST;
1696 case NEIGHBOR_LOWER:
1698 case NEIGHBOR_SAME_LEVEL:
1699 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1700 nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) {
1701 new_node_level = nb_liquid_level - 1;
1707 if (new_node_level >= 0)
1708 new_node_content = liquid_kind;
1710 new_node_content = CONTENT_AIR;
1714 check if anything has changed. if not, just continue with the next node.
1716 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1717 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1718 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1724 update the current node
1726 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1727 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1728 // set level to last 3 bits, flowing down bit to 4th bit
1729 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1731 // set the liquid level and flow bit to 0
1732 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1734 n0.setContent(new_node_content);
1736 v3s16 blockpos = getNodeBlockPos(p0);
1737 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1739 modified_blocks.insert(blockpos, block);
1742 enqueue neighbors for update if neccessary
1744 switch (content_features(n0.getContent()).liquid_type) {
1746 case LIQUID_FLOWING:
1747 // make sure source flows into all neighboring nodes
1748 for (u16 i = 0; i < num_flows; i++)
1749 if (flows[i].t != NEIGHBOR_UPPER)
1750 m_transforming_liquid.push_back(flows[i].p);
1751 for (u16 i = 0; i < num_airs; i++)
1752 if (airs[i].t != NEIGHBOR_UPPER)
1753 m_transforming_liquid.push_back(airs[i].p);
1756 // this flow has turned to air; neighboring flows might need to do the same
1757 for (u16 i = 0; i < num_flows; i++)
1758 m_transforming_liquid.push_back(flows[i].p);
1762 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1765 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1767 v3s16 blockpos = getNodeBlockPos(p);
1768 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1769 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1772 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1776 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1780 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1782 v3s16 blockpos = getNodeBlockPos(p);
1783 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1784 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1787 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1791 block->m_node_metadata.set(p_rel, meta);
1794 void Map::removeNodeMetadata(v3s16 p)
1796 v3s16 blockpos = getNodeBlockPos(p);
1797 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1798 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1801 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1805 block->m_node_metadata.remove(p_rel);
1808 void Map::nodeMetadataStep(float dtime,
1809 core::map<v3s16, MapBlock*> &changed_blocks)
1813 Currently there is no way to ensure that all the necessary
1814 blocks are loaded when this is run. (They might get unloaded)
1815 NOTE: ^- Actually, that might not be so. In a quick test it
1816 reloaded a block with a furnace when I walked back to it from
1819 core::map<v2s16, MapSector*>::Iterator si;
1820 si = m_sectors.getIterator();
1821 for(; si.atEnd() == false; si++)
1823 MapSector *sector = si.getNode()->getValue();
1824 core::list< MapBlock * > sectorblocks;
1825 sector->getBlocks(sectorblocks);
1826 core::list< MapBlock * >::Iterator i;
1827 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1829 MapBlock *block = *i;
1830 bool changed = block->m_node_metadata.step(dtime);
1832 changed_blocks[block->getPos()] = block;
1841 ServerMap::ServerMap(std::string savedir):
1844 m_map_metadata_changed(true)
1846 dstream<<__FUNCTION_NAME<<std::endl;
1848 //m_chunksize = 8; // Takes a few seconds
1850 m_seed = (((u64)(myrand()%0xffff)<<0)
1851 + ((u64)(myrand()%0xffff)<<16)
1852 + ((u64)(myrand()%0xffff)<<32)
1853 + ((u64)(myrand()%0xffff)<<48));
1856 Experimental and debug stuff
1863 Try to load map; if not found, create a new one.
1866 m_savedir = savedir;
1867 m_map_saving_enabled = false;
1871 // If directory exists, check contents and load if possible
1872 if(fs::PathExists(m_savedir))
1874 // If directory is empty, it is safe to save into it.
1875 if(fs::GetDirListing(m_savedir).size() == 0)
1877 dstream<<DTIME<<"Server: Empty save directory is valid."
1879 m_map_saving_enabled = true;
1884 // Load map metadata (seed, chunksize)
1887 catch(FileNotGoodException &e){
1888 dstream<<DTIME<<"WARNING: Could not load map metadata"
1889 //<<" Disabling chunk-based generator."
1895 // Load chunk metadata
1898 catch(FileNotGoodException &e){
1899 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1900 <<" Disabling chunk-based generator."
1905 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1906 "metadata and sector (0,0) from "<<savedir<<
1907 ", assuming valid save directory."
1910 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1911 <<"and chunk metadata from "<<savedir
1912 <<", assuming valid save directory."
1915 m_map_saving_enabled = true;
1916 // Map loaded, not creating new one
1920 // If directory doesn't exist, it is safe to save to it
1922 m_map_saving_enabled = true;
1925 catch(std::exception &e)
1927 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1928 <<", exception: "<<e.what()<<std::endl;
1929 dstream<<"Please remove the map or fix it."<<std::endl;
1930 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1933 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1935 // Create zero sector
1936 emergeSector(v2s16(0,0));
1938 // Initially write whole map
1942 ServerMap::~ServerMap()
1944 dstream<<__FUNCTION_NAME<<std::endl;
1948 if(m_map_saving_enabled)
1950 // Save only changed parts
1952 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1956 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1959 catch(std::exception &e)
1961 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1962 <<", exception: "<<e.what()<<std::endl;
1969 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1970 for(; i.atEnd() == false; i++)
1972 MapChunk *chunk = i.getNode()->getValue();
1978 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
1980 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
1981 if(enable_mapgen_debug_info)
1982 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
1983 <<blockpos.Z<<")"<<std::endl;
1985 // Do nothing if not inside limits (+-1 because of neighbors)
1986 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
1987 blockpos_over_limit(blockpos + v3s16(1,1,1)))
1993 data->no_op = false;
1994 data->seed = m_seed;
1995 data->blockpos = blockpos;
1998 Create the whole area of this and the neighboring blocks
2001 //TimeTaker timer("initBlockMake() create area");
2003 for(s16 x=-1; x<=1; x++)
2004 for(s16 z=-1; z<=1; z++)
2006 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2007 // Sector metadata is loaded from disk if not already loaded.
2008 ServerMapSector *sector = createSector(sectorpos);
2011 for(s16 y=-1; y<=1; y++)
2013 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2014 //MapBlock *block = createBlock(p);
2015 // 1) get from memory, 2) load from disk
2016 MapBlock *block = emergeBlock(p, false);
2017 // 3) create a blank one
2020 block = createBlock(p);
2023 Block gets sunlight if this is true.
2025 Refer to the map generator heuristics.
2027 bool ug = mapgen::block_is_underground(data->seed, p);
2028 block->setIsUnderground(ug);
2031 // Lighting will not be valid after make_chunk is called
2032 block->setLightingExpired(true);
2033 // Lighting will be calculated
2034 //block->setLightingExpired(false);
2040 Now we have a big empty area.
2042 Make a ManualMapVoxelManipulator that contains this and the
2046 // The area that contains this block and it's neighbors
2047 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2048 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2050 data->vmanip = new ManualMapVoxelManipulator(this);
2051 //data->vmanip->setMap(this);
2055 //TimeTaker timer("initBlockMake() initialEmerge");
2056 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2059 // Data is ready now.
2062 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2063 core::map<v3s16, MapBlock*> &changed_blocks)
2065 v3s16 blockpos = data->blockpos;
2066 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2067 <<blockpos.Z<<")"<<std::endl;*/
2071 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2075 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2077 /*dstream<<"Resulting vmanip:"<<std::endl;
2078 data->vmanip.print(dstream);*/
2081 Blit generated stuff to map
2082 NOTE: blitBackAll adds nearly everything to changed_blocks
2086 //TimeTaker timer("finishBlockMake() blitBackAll");
2087 data->vmanip->blitBackAll(&changed_blocks);
2090 if(enable_mapgen_debug_info)
2091 dstream<<"finishBlockMake: changed_blocks.size()="
2092 <<changed_blocks.size()<<std::endl;
2095 Copy transforming liquid information
2097 while(data->transforming_liquid.size() > 0)
2099 v3s16 p = data->transforming_liquid.pop_front();
2100 m_transforming_liquid.push_back(p);
2106 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2110 Set is_underground flag for lighting with sunlight.
2112 Refer to map generator heuristics.
2114 NOTE: This is done in initChunkMake
2116 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2120 Add sunlight to central block.
2121 This makes in-dark-spawning monsters to not flood the whole thing.
2122 Do not spread the light, though.
2124 /*core::map<v3s16, bool> light_sources;
2125 bool black_air_left = false;
2126 block->propagateSunlight(light_sources, true, &black_air_left);*/
2129 NOTE: Lighting and object adding shouldn't really be here, but
2130 lighting is a bit tricky to move properly to makeBlock.
2131 TODO: Do this the right way anyway, that is, move it to makeBlock.
2132 - There needs to be some way for makeBlock to report back if
2133 the lighting update is going further down because of the
2134 new block blocking light
2139 NOTE: This takes ~60ms, TODO: Investigate why
2142 TimeTaker t("finishBlockMake lighting update");
2144 core::map<v3s16, MapBlock*> lighting_update_blocks;
2147 lighting_update_blocks.insert(block->getPos(), block);
2152 v3s16 p = block->getPos()+v3s16(x,1,z);
2153 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2157 // All modified blocks
2158 // NOTE: Should this be done? If this is not done, then the lighting
2159 // of the others will be updated in a different place, one by one, i
2160 // think... or they might not? Well, at least they are left marked as
2161 // "lighting expired"; it seems that is not handled at all anywhere,
2162 // so enabling this will slow it down A LOT because otherwise it
2163 // would not do this at all. This causes the black trees.
2164 for(core::map<v3s16, MapBlock*>::Iterator
2165 i = changed_blocks.getIterator();
2166 i.atEnd() == false; i++)
2168 lighting_update_blocks.insert(i.getNode()->getKey(),
2169 i.getNode()->getValue());
2171 /*// Also force-add all the upmost blocks for proper sunlight
2172 for(s16 x=-1; x<=1; x++)
2173 for(s16 z=-1; z<=1; z++)
2175 v3s16 p = block->getPos()+v3s16(x,1,z);
2176 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2179 updateLighting(lighting_update_blocks, changed_blocks);
2182 Set lighting to non-expired state in all of them.
2183 This is cheating, but it is not fast enough if all of them
2184 would actually be updated.
2186 for(s16 x=-1; x<=1; x++)
2187 for(s16 y=-1; y<=1; y++)
2188 for(s16 z=-1; z<=1; z++)
2190 v3s16 p = block->getPos()+v3s16(x,y,z);
2191 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2194 if(enable_mapgen_debug_info == false)
2195 t.stop(true); // Hide output
2199 Add random objects to block
2201 mapgen::add_random_objects(block);
2204 Go through changed blocks
2206 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2207 i.atEnd() == false; i++)
2209 MapBlock *block = i.getNode()->getValue();
2212 Update day/night difference cache of the MapBlocks
2214 block->updateDayNightDiff();
2216 Set block as modified
2218 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2222 Set central block as generated
2224 block->setGenerated(true);
2227 Save changed parts of map
2228 NOTE: Will be saved later.
2232 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2233 <<blockpos.Z<<")"<<std::endl;*/
2235 if(enable_mapgen_debug_info)
2238 Analyze resulting blocks
2240 for(s16 x=-1; x<=1; x++)
2241 for(s16 y=-1; y<=1; y++)
2242 for(s16 z=-1; z<=1; z++)
2244 v3s16 p = block->getPos()+v3s16(x,y,z);
2245 MapBlock *block = getBlockNoCreateNoEx(p);
2247 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2248 dstream<<"Generated "<<spos<<": "
2249 <<analyze_block(block)<<std::endl;
2257 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2259 DSTACKF("%s: p2d=(%d,%d)",
2264 Check if it exists already in memory
2266 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2271 Try to load it from disk (with blocks)
2273 //if(loadSectorFull(p2d) == true)
2276 Try to load metadata from disk
2278 if(loadSectorMeta(p2d) == true)
2280 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2283 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2284 throw InvalidPositionException("");
2290 Do not create over-limit
2292 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2293 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2294 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2295 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2296 throw InvalidPositionException("createSector(): pos. over limit");
2299 Generate blank sector
2302 sector = new ServerMapSector(this, p2d);
2304 // Sector position on map in nodes
2305 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2310 m_sectors.insert(p2d, sector);
2316 This is a quick-hand function for calling makeBlock().
2318 MapBlock * ServerMap::generateBlock(
2320 core::map<v3s16, MapBlock*> &modified_blocks
2323 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2325 /*dstream<<"generateBlock(): "
2326 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2329 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2331 TimeTaker timer("generateBlock");
2333 //MapBlock *block = original_dummy;
2335 v2s16 p2d(p.X, p.Z);
2336 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2339 Do not generate over-limit
2341 if(blockpos_over_limit(p))
2343 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2344 throw InvalidPositionException("generateBlock(): pos. over limit");
2348 Create block make data
2350 mapgen::BlockMakeData data;
2351 initBlockMake(&data, p);
2357 TimeTaker t("mapgen::make_block()");
2358 mapgen::make_block(&data);
2360 if(enable_mapgen_debug_info == false)
2361 t.stop(true); // Hide output
2365 Blit data back on map, update lighting, add mobs and whatever this does
2367 finishBlockMake(&data, modified_blocks);
2372 MapBlock *block = getBlockNoCreateNoEx(p);
2380 bool erroneus_content = false;
2381 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2382 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2383 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2386 MapNode n = block->getNode(p);
2387 if(n.getContent() == CONTENT_IGNORE)
2389 dstream<<"CONTENT_IGNORE at "
2390 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2392 erroneus_content = true;
2396 if(erroneus_content)
2405 Generate a completely empty block
2409 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2410 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2412 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2416 n.setContent(CONTENT_AIR);
2418 n.setContent(CONTENT_STONE);
2419 block->setNode(v3s16(x0,y0,z0), n);
2425 if(enable_mapgen_debug_info == false)
2426 timer.stop(true); // Hide output
2431 MapBlock * ServerMap::createBlock(v3s16 p)
2433 DSTACKF("%s: p=(%d,%d,%d)",
2434 __FUNCTION_NAME, p.X, p.Y, p.Z);
2437 Do not create over-limit
2439 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2440 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2441 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2442 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2443 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2444 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2445 throw InvalidPositionException("createBlock(): pos. over limit");
2447 v2s16 p2d(p.X, p.Z);
2450 This will create or load a sector if not found in memory.
2451 If block exists on disk, it will be loaded.
2453 NOTE: On old save formats, this will be slow, as it generates
2454 lighting on blocks for them.
2456 ServerMapSector *sector;
2458 sector = (ServerMapSector*)createSector(p2d);
2459 assert(sector->getId() == MAPSECTOR_SERVER);
2461 catch(InvalidPositionException &e)
2463 dstream<<"createBlock: createSector() failed"<<std::endl;
2467 NOTE: This should not be done, or at least the exception
2468 should not be passed on as std::exception, because it
2469 won't be catched at all.
2471 /*catch(std::exception &e)
2473 dstream<<"createBlock: createSector() failed: "
2474 <<e.what()<<std::endl;
2479 Try to get a block from the sector
2482 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2485 if(block->isDummy())
2490 block = sector->createBlankBlock(block_y);
2494 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2496 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2498 p.X, p.Y, p.Z, allow_generate);
2501 MapBlock *block = getBlockNoCreateNoEx(p);
2502 if(block && block->isDummy() == false)
2507 MapBlock *block = loadBlock(p);
2514 core::map<v3s16, MapBlock*> modified_blocks;
2515 MapBlock *block = generateBlock(p, modified_blocks);
2519 event.type = MEET_OTHER;
2522 // Copy modified_blocks to event
2523 for(core::map<v3s16, MapBlock*>::Iterator
2524 i = modified_blocks.getIterator();
2525 i.atEnd()==false; i++)
2527 event.modified_blocks.insert(i.getNode()->getKey(), false);
2531 dispatchEvent(&event);
2542 Do not generate over-limit
2544 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2545 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2546 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2547 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2548 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2549 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2550 throw InvalidPositionException("emergeBlock(): pos. over limit");
2552 v2s16 p2d(p.X, p.Z);
2555 This will create or load a sector if not found in memory.
2556 If block exists on disk, it will be loaded.
2558 ServerMapSector *sector;
2560 sector = createSector(p2d);
2561 //sector = emergeSector(p2d, changed_blocks);
2563 catch(InvalidPositionException &e)
2565 dstream<<"emergeBlock: createSector() failed: "
2566 <<e.what()<<std::endl;
2567 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2569 <<"You could try to delete it."<<std::endl;
2572 catch(VersionMismatchException &e)
2574 dstream<<"emergeBlock: createSector() failed: "
2575 <<e.what()<<std::endl;
2576 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2578 <<"You could try to delete it."<<std::endl;
2583 Try to get a block from the sector
2586 bool does_not_exist = false;
2587 bool lighting_expired = false;
2588 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2590 // If not found, try loading from disk
2593 block = loadBlock(p);
2599 does_not_exist = true;
2601 else if(block->isDummy() == true)
2603 does_not_exist = true;
2605 else if(block->getLightingExpired())
2607 lighting_expired = true;
2612 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2617 If block was not found on disk and not going to generate a
2618 new one, make sure there is a dummy block in place.
2620 if(only_from_disk && (does_not_exist || lighting_expired))
2622 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2626 // Create dummy block
2627 block = new MapBlock(this, p, true);
2629 // Add block to sector
2630 sector->insertBlock(block);
2636 //dstream<<"Not found on disk, generating."<<std::endl;
2638 //TimeTaker("emergeBlock() generate");
2640 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2643 If the block doesn't exist, generate the block.
2647 block = generateBlock(p, block, sector, changed_blocks,
2648 lighting_invalidated_blocks);
2651 if(lighting_expired)
2653 lighting_invalidated_blocks.insert(p, block);
2658 Initially update sunlight
2661 core::map<v3s16, bool> light_sources;
2662 bool black_air_left = false;
2663 bool bottom_invalid =
2664 block->propagateSunlight(light_sources, true,
2667 // If sunlight didn't reach everywhere and part of block is
2668 // above ground, lighting has to be properly updated
2669 //if(black_air_left && some_part_underground)
2672 lighting_invalidated_blocks[block->getPos()] = block;
2677 lighting_invalidated_blocks[block->getPos()] = block;
2686 s16 ServerMap::findGroundLevel(v2s16 p2d)
2690 Uh, just do something random...
2692 // Find existing map from top to down
2695 v3s16 p(p2d.X, max, p2d.Y);
2696 for(; p.Y>min; p.Y--)
2698 MapNode n = getNodeNoEx(p);
2699 if(n.getContent() != CONTENT_IGNORE)
2704 // If this node is not air, go to plan b
2705 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2707 // Search existing walkable and return it
2708 for(; p.Y>min; p.Y--)
2710 MapNode n = getNodeNoEx(p);
2711 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2720 Determine from map generator noise functions
2723 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2726 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2727 //return (s16)level;
2730 void ServerMap::createDirs(std::string path)
2732 if(fs::CreateAllDirs(path) == false)
2734 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2735 <<"\""<<path<<"\""<<std::endl;
2736 throw BaseException("ServerMap failed to create directory");
2740 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2746 snprintf(cc, 9, "%.4x%.4x",
2747 (unsigned int)pos.X&0xffff,
2748 (unsigned int)pos.Y&0xffff);
2750 return m_savedir + "/sectors/" + cc;
2752 snprintf(cc, 9, "%.3x/%.3x",
2753 (unsigned int)pos.X&0xfff,
2754 (unsigned int)pos.Y&0xfff);
2756 return m_savedir + "/sectors2/" + cc;
2762 v2s16 ServerMap::getSectorPos(std::string dirname)
2766 size_t spos = dirname.rfind('/') + 1;
2767 assert(spos != std::string::npos);
2768 if(dirname.size() - spos == 8)
2771 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2773 else if(dirname.size() - spos == 3)
2776 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2777 // Sign-extend the 12 bit values up to 16 bits...
2778 if(x&0x800) x|=0xF000;
2779 if(y&0x800) y|=0xF000;
2786 v2s16 pos((s16)x, (s16)y);
2790 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2792 v2s16 p2d = getSectorPos(sectordir);
2794 if(blockfile.size() != 4){
2795 throw InvalidFilenameException("Invalid block filename");
2798 int r = sscanf(blockfile.c_str(), "%4x", &y);
2800 throw InvalidFilenameException("Invalid block filename");
2801 return v3s16(p2d.X, y, p2d.Y);
2804 std::string ServerMap::getBlockFilename(v3s16 p)
2807 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2811 void ServerMap::save(bool only_changed)
2813 DSTACK(__FUNCTION_NAME);
2814 if(m_map_saving_enabled == false)
2816 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2820 if(only_changed == false)
2821 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2824 if(only_changed == false || m_map_metadata_changed)
2829 u32 sector_meta_count = 0;
2830 u32 block_count = 0;
2831 u32 block_count_all = 0; // Number of blocks in memory
2833 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2834 for(; i.atEnd() == false; i++)
2836 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2837 assert(sector->getId() == MAPSECTOR_SERVER);
2839 if(sector->differs_from_disk || only_changed == false)
2841 saveSectorMeta(sector);
2842 sector_meta_count++;
2844 core::list<MapBlock*> blocks;
2845 sector->getBlocks(blocks);
2846 core::list<MapBlock*>::Iterator j;
2847 for(j=blocks.begin(); j!=blocks.end(); j++)
2849 MapBlock *block = *j;
2853 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2854 || only_changed == false)
2859 /*dstream<<"ServerMap: Written block ("
2860 <<block->getPos().X<<","
2861 <<block->getPos().Y<<","
2862 <<block->getPos().Z<<")"
2869 Only print if something happened or saved whole map
2871 if(only_changed == false || sector_meta_count != 0
2872 || block_count != 0)
2874 dstream<<DTIME<<"ServerMap: Written: "
2875 <<sector_meta_count<<" sector metadata files, "
2876 <<block_count<<" block files"
2877 <<", "<<block_count_all<<" blocks in memory."
2882 void ServerMap::saveMapMeta()
2884 DSTACK(__FUNCTION_NAME);
2886 dstream<<"INFO: ServerMap::saveMapMeta(): "
2890 createDirs(m_savedir);
2892 std::string fullpath = m_savedir + "/map_meta.txt";
2893 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2894 if(os.good() == false)
2896 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2897 <<"could not open"<<fullpath<<std::endl;
2898 throw FileNotGoodException("Cannot open chunk metadata");
2902 params.setU64("seed", m_seed);
2904 params.writeLines(os);
2906 os<<"[end_of_params]\n";
2908 m_map_metadata_changed = false;
2911 void ServerMap::loadMapMeta()
2913 DSTACK(__FUNCTION_NAME);
2915 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2918 std::string fullpath = m_savedir + "/map_meta.txt";
2919 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2920 if(is.good() == false)
2922 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2923 <<"could not open"<<fullpath<<std::endl;
2924 throw FileNotGoodException("Cannot open map metadata");
2932 throw SerializationError
2933 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2935 std::getline(is, line);
2936 std::string trimmedline = trim(line);
2937 if(trimmedline == "[end_of_params]")
2939 params.parseConfigLine(line);
2942 m_seed = params.getU64("seed");
2944 dstream<<"INFO: ServerMap::loadMapMeta(): "
2949 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2951 DSTACK(__FUNCTION_NAME);
2952 // Format used for writing
2953 u8 version = SER_FMT_VER_HIGHEST;
2955 v2s16 pos = sector->getPos();
2956 std::string dir = getSectorDir(pos);
2959 std::string fullpath = dir + "/meta";
2960 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2961 if(o.good() == false)
2962 throw FileNotGoodException("Cannot open sector metafile");
2964 sector->serialize(o, version);
2966 sector->differs_from_disk = false;
2969 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2971 DSTACK(__FUNCTION_NAME);
2973 v2s16 p2d = getSectorPos(sectordir);
2975 ServerMapSector *sector = NULL;
2977 std::string fullpath = sectordir + "/meta";
2978 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2979 if(is.good() == false)
2981 // If the directory exists anyway, it probably is in some old
2982 // format. Just go ahead and create the sector.
2983 if(fs::PathExists(sectordir))
2985 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2986 <<fullpath<<" doesn't exist but directory does."
2987 <<" Continuing with a sector with no metadata."
2989 sector = new ServerMapSector(this, p2d);
2990 m_sectors.insert(p2d, sector);
2994 throw FileNotGoodException("Cannot open sector metafile");
2999 sector = ServerMapSector::deSerialize
3000 (is, this, p2d, m_sectors);
3002 saveSectorMeta(sector);
3005 sector->differs_from_disk = false;
3010 bool ServerMap::loadSectorMeta(v2s16 p2d)
3012 DSTACK(__FUNCTION_NAME);
3014 MapSector *sector = NULL;
3016 // The directory layout we're going to load from.
3017 // 1 - original sectors/xxxxzzzz/
3018 // 2 - new sectors2/xxx/zzz/
3019 // If we load from anything but the latest structure, we will
3020 // immediately save to the new one, and remove the old.
3022 std::string sectordir1 = getSectorDir(p2d, 1);
3023 std::string sectordir;
3024 if(fs::PathExists(sectordir1))
3026 sectordir = sectordir1;
3031 sectordir = getSectorDir(p2d, 2);
3035 sector = loadSectorMeta(sectordir, loadlayout != 2);
3037 catch(InvalidFilenameException &e)
3041 catch(FileNotGoodException &e)
3045 catch(std::exception &e)
3054 bool ServerMap::loadSectorFull(v2s16 p2d)
3056 DSTACK(__FUNCTION_NAME);
3058 MapSector *sector = NULL;
3060 // The directory layout we're going to load from.
3061 // 1 - original sectors/xxxxzzzz/
3062 // 2 - new sectors2/xxx/zzz/
3063 // If we load from anything but the latest structure, we will
3064 // immediately save to the new one, and remove the old.
3066 std::string sectordir1 = getSectorDir(p2d, 1);
3067 std::string sectordir;
3068 if(fs::PathExists(sectordir1))
3070 sectordir = sectordir1;
3075 sectordir = getSectorDir(p2d, 2);
3079 sector = loadSectorMeta(sectordir, loadlayout != 2);
3081 catch(InvalidFilenameException &e)
3085 catch(FileNotGoodException &e)
3089 catch(std::exception &e)
3097 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3099 std::vector<fs::DirListNode>::iterator i2;
3100 for(i2=list2.begin(); i2!=list2.end(); i2++)
3106 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3108 catch(InvalidFilenameException &e)
3110 // This catches unknown crap in directory
3116 dstream<<"Sector converted to new layout - deleting "<<
3117 sectordir1<<std::endl;
3118 fs::RecursiveDelete(sectordir1);
3125 void ServerMap::saveBlock(MapBlock *block)
3127 DSTACK(__FUNCTION_NAME);
3129 Dummy blocks are not written
3131 if(block->isDummy())
3133 /*v3s16 p = block->getPos();
3134 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3135 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3139 // Format used for writing
3140 u8 version = SER_FMT_VER_HIGHEST;
3142 v3s16 p3d = block->getPos();
3144 v2s16 p2d(p3d.X, p3d.Z);
3145 std::string sectordir = getSectorDir(p2d);
3147 createDirs(sectordir);
3149 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3150 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3151 if(o.good() == false)
3152 throw FileNotGoodException("Cannot open block data");
3155 [0] u8 serialization version
3158 o.write((char*)&version, 1);
3161 block->serialize(o, version);
3163 // Write extra data stored on disk
3164 block->serializeDiskExtra(o, version);
3166 // We just wrote it to the disk so clear modified flag
3167 block->resetModified();
3170 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3172 DSTACK(__FUNCTION_NAME);
3174 std::string fullpath = sectordir+"/"+blockfile;
3177 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3178 if(is.good() == false)
3179 throw FileNotGoodException("Cannot open block file");
3181 v3s16 p3d = getBlockPos(sectordir, blockfile);
3182 v2s16 p2d(p3d.X, p3d.Z);
3184 assert(sector->getPos() == p2d);
3186 u8 version = SER_FMT_VER_INVALID;
3187 is.read((char*)&version, 1);
3190 throw SerializationError("ServerMap::loadBlock(): Failed"
3191 " to read MapBlock version");
3193 /*u32 block_size = MapBlock::serializedLength(version);
3194 SharedBuffer<u8> data(block_size);
3195 is.read((char*)*data, block_size);*/
3197 // This will always return a sector because we're the server
3198 //MapSector *sector = emergeSector(p2d);
3200 MapBlock *block = NULL;
3201 bool created_new = false;
3202 block = sector->getBlockNoCreateNoEx(p3d.Y);
3205 block = sector->createBlankBlockNoInsert(p3d.Y);
3210 block->deSerialize(is, version);
3212 // Read extra data stored on disk
3213 block->deSerializeDiskExtra(is, version);
3215 // If it's a new block, insert it to the map
3217 sector->insertBlock(block);
3220 Save blocks loaded in old format in new format
3223 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3228 // We just loaded it from the disk, so it's up-to-date.
3229 block->resetModified();
3232 catch(SerializationError &e)
3234 dstream<<"WARNING: Invalid block data on disk "
3235 <<"fullpath="<<fullpath
3236 <<" (SerializationError). "
3237 <<"what()="<<e.what()
3239 //" Ignoring. A new one will be generated.
3242 // TODO: Backup file; name is in fullpath.
3246 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3248 DSTACK(__FUNCTION_NAME);
3250 v2s16 p2d(blockpos.X, blockpos.Z);
3252 // The directory layout we're going to load from.
3253 // 1 - original sectors/xxxxzzzz/
3254 // 2 - new sectors2/xxx/zzz/
3255 // If we load from anything but the latest structure, we will
3256 // immediately save to the new one, and remove the old.
3258 std::string sectordir1 = getSectorDir(p2d, 1);
3259 std::string sectordir;
3260 if(fs::PathExists(sectordir1))
3262 sectordir = sectordir1;
3267 sectordir = getSectorDir(p2d, 2);
3271 Make sure sector is loaded
3273 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3277 sector = loadSectorMeta(sectordir, loadlayout != 2);
3279 catch(InvalidFilenameException &e)
3283 catch(FileNotGoodException &e)
3287 catch(std::exception &e)
3294 Make sure file exists
3297 std::string blockfilename = getBlockFilename(blockpos);
3298 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3304 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3305 return getBlockNoCreateNoEx(blockpos);
3308 void ServerMap::PrintInfo(std::ostream &out)
3319 ClientMap::ClientMap(
3321 MapDrawControl &control,
3322 scene::ISceneNode* parent,
3323 scene::ISceneManager* mgr,
3327 scene::ISceneNode(parent, mgr, id),
3330 m_camera_position(0,0,0),
3331 m_camera_direction(0,0,1)
3333 m_camera_mutex.Init();
3334 assert(m_camera_mutex.IsInitialized());
3336 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3337 BS*1000000,BS*1000000,BS*1000000);
3340 ClientMap::~ClientMap()
3342 /*JMutexAutoLock lock(mesh_mutex);
3351 MapSector * ClientMap::emergeSector(v2s16 p2d)
3353 DSTACK(__FUNCTION_NAME);
3354 // Check that it doesn't exist already
3356 return getSectorNoGenerate(p2d);
3358 catch(InvalidPositionException &e)
3363 ClientMapSector *sector = new ClientMapSector(this, p2d);
3366 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3367 m_sectors.insert(p2d, sector);
3374 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3376 DSTACK(__FUNCTION_NAME);
3377 ClientMapSector *sector = NULL;
3379 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3381 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3385 sector = (ClientMapSector*)n->getValue();
3386 assert(sector->getId() == MAPSECTOR_CLIENT);
3390 sector = new ClientMapSector(this, p2d);
3392 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3393 m_sectors.insert(p2d, sector);
3397 sector->deSerialize(is);
3401 void ClientMap::OnRegisterSceneNode()
3405 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3406 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3409 ISceneNode::OnRegisterSceneNode();
3412 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3414 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3415 DSTACK(__FUNCTION_NAME);
3417 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3420 This is called two times per frame, reset on the non-transparent one
3422 if(pass == scene::ESNRP_SOLID)
3424 m_last_drawn_sectors.clear();
3428 Get time for measuring timeout.
3430 Measuring time is very useful for long delays when the
3431 machine is swapping a lot.
3433 int time1 = time(0);
3435 //u32 daynight_ratio = m_client->getDayNightRatio();
3437 m_camera_mutex.Lock();
3438 v3f camera_position = m_camera_position;
3439 v3f camera_direction = m_camera_direction;
3440 m_camera_mutex.Unlock();
3443 Get all blocks and draw all visible ones
3446 v3s16 cam_pos_nodes(
3447 camera_position.X / BS,
3448 camera_position.Y / BS,
3449 camera_position.Z / BS);
3451 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3453 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3454 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3456 // Take a fair amount as we will be dropping more out later
3458 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3459 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3460 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3462 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3463 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3464 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3466 u32 vertex_count = 0;
3468 // For limiting number of mesh updates per frame
3469 u32 mesh_update_count = 0;
3471 u32 blocks_would_have_drawn = 0;
3472 u32 blocks_drawn = 0;
3474 int timecheck_counter = 0;
3475 core::map<v2s16, MapSector*>::Iterator si;
3476 si = m_sectors.getIterator();
3477 for(; si.atEnd() == false; si++)
3480 timecheck_counter++;
3481 if(timecheck_counter > 50)
3483 timecheck_counter = 0;
3484 int time2 = time(0);
3485 if(time2 > time1 + 4)
3487 dstream<<"ClientMap::renderMap(): "
3488 "Rendering takes ages, returning."
3495 MapSector *sector = si.getNode()->getValue();
3496 v2s16 sp = sector->getPos();
3498 if(m_control.range_all == false)
3500 if(sp.X < p_blocks_min.X
3501 || sp.X > p_blocks_max.X
3502 || sp.Y < p_blocks_min.Z
3503 || sp.Y > p_blocks_max.Z)
3507 core::list< MapBlock * > sectorblocks;
3508 sector->getBlocks(sectorblocks);
3514 u32 sector_blocks_drawn = 0;
3516 core::list< MapBlock * >::Iterator i;
3517 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3519 MapBlock *block = *i;
3522 Compare block position to camera position, skip
3523 if not seen on display
3526 float range = 100000 * BS;
3527 if(m_control.range_all == false)
3528 range = m_control.wanted_range * BS;
3531 if(isBlockInSight(block->getPos(), camera_position,
3532 camera_direction, range, &d) == false)
3537 // Okay, this block will be drawn. Reset usage timer.
3538 block->resetUsageTimer();
3540 // This is ugly (spherical distance limit?)
3541 /*if(m_control.range_all == false &&
3542 d - 0.5*BS*MAP_BLOCKSIZE > range)
3547 Update expired mesh (used for day/night change)
3549 It doesn't work exactly like it should now with the
3550 tasked mesh update but whatever.
3553 bool mesh_expired = false;
3556 JMutexAutoLock lock(block->mesh_mutex);
3558 mesh_expired = block->getMeshExpired();
3560 // Mesh has not been expired and there is no mesh:
3561 // block has no content
3562 if(block->mesh == NULL && mesh_expired == false)
3566 f32 faraway = BS*50;
3567 //f32 faraway = m_control.wanted_range * BS;
3570 This has to be done with the mesh_mutex unlocked
3572 // Pretty random but this should work somewhat nicely
3573 if(mesh_expired && (
3574 (mesh_update_count < 3
3575 && (d < faraway || mesh_update_count < 2)
3578 (m_control.range_all && mesh_update_count < 20)
3581 /*if(mesh_expired && mesh_update_count < 6
3582 && (d < faraway || mesh_update_count < 3))*/
3584 mesh_update_count++;
3586 // Mesh has been expired: generate new mesh
3587 //block->updateMesh(daynight_ratio);
3588 m_client->addUpdateMeshTask(block->getPos());
3590 mesh_expired = false;
3595 Draw the faces of the block
3598 JMutexAutoLock lock(block->mesh_mutex);
3600 scene::SMesh *mesh = block->mesh;
3605 blocks_would_have_drawn++;
3606 if(blocks_drawn >= m_control.wanted_max_blocks
3607 && m_control.range_all == false
3608 && d > m_control.wanted_min_range * BS)
3612 sector_blocks_drawn++;
3614 u32 c = mesh->getMeshBufferCount();
3616 for(u32 i=0; i<c; i++)
3618 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3619 const video::SMaterial& material = buf->getMaterial();
3620 video::IMaterialRenderer* rnd =
3621 driver->getMaterialRenderer(material.MaterialType);
3622 bool transparent = (rnd && rnd->isTransparent());
3623 // Render transparent on transparent pass and likewise.
3624 if(transparent == is_transparent_pass)
3627 This *shouldn't* hurt too much because Irrlicht
3628 doesn't change opengl textures if the old
3629 material is set again.
3631 driver->setMaterial(buf->getMaterial());
3632 driver->drawMeshBuffer(buf);
3633 vertex_count += buf->getVertexCount();
3637 } // foreach sectorblocks
3639 if(sector_blocks_drawn != 0)
3641 m_last_drawn_sectors[sp] = true;
3645 m_control.blocks_drawn = blocks_drawn;
3646 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3648 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3649 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3652 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3653 core::map<v3s16, MapBlock*> *affected_blocks)
3655 bool changed = false;
3657 Add it to all blocks touching it
3660 v3s16(0,0,0), // this
3661 v3s16(0,0,1), // back
3662 v3s16(0,1,0), // top
3663 v3s16(1,0,0), // right
3664 v3s16(0,0,-1), // front
3665 v3s16(0,-1,0), // bottom
3666 v3s16(-1,0,0), // left
3668 for(u16 i=0; i<7; i++)
3670 v3s16 p2 = p + dirs[i];
3671 // Block position of neighbor (or requested) node
3672 v3s16 blockpos = getNodeBlockPos(p2);
3673 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3674 if(blockref == NULL)
3676 // Relative position of requested node
3677 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3678 if(blockref->setTempMod(relpos, mod))
3683 if(changed && affected_blocks!=NULL)
3685 for(u16 i=0; i<7; i++)
3687 v3s16 p2 = p + dirs[i];
3688 // Block position of neighbor (or requested) node
3689 v3s16 blockpos = getNodeBlockPos(p2);
3690 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3691 if(blockref == NULL)
3693 affected_blocks->insert(blockpos, blockref);
3699 bool ClientMap::clearTempMod(v3s16 p,
3700 core::map<v3s16, MapBlock*> *affected_blocks)
3702 bool changed = false;
3704 v3s16(0,0,0), // this
3705 v3s16(0,0,1), // back
3706 v3s16(0,1,0), // top
3707 v3s16(1,0,0), // right
3708 v3s16(0,0,-1), // front
3709 v3s16(0,-1,0), // bottom
3710 v3s16(-1,0,0), // left
3712 for(u16 i=0; i<7; i++)
3714 v3s16 p2 = p + dirs[i];
3715 // Block position of neighbor (or requested) node
3716 v3s16 blockpos = getNodeBlockPos(p2);
3717 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3718 if(blockref == NULL)
3720 // Relative position of requested node
3721 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3722 if(blockref->clearTempMod(relpos))
3727 if(changed && affected_blocks!=NULL)
3729 for(u16 i=0; i<7; i++)
3731 v3s16 p2 = p + dirs[i];
3732 // Block position of neighbor (or requested) node
3733 v3s16 blockpos = getNodeBlockPos(p2);
3734 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3735 if(blockref == NULL)
3737 affected_blocks->insert(blockpos, blockref);
3743 void ClientMap::expireMeshes(bool only_daynight_diffed)
3745 TimeTaker timer("expireMeshes()");
3747 core::map<v2s16, MapSector*>::Iterator si;
3748 si = m_sectors.getIterator();
3749 for(; si.atEnd() == false; si++)
3751 MapSector *sector = si.getNode()->getValue();
3753 core::list< MapBlock * > sectorblocks;
3754 sector->getBlocks(sectorblocks);
3756 core::list< MapBlock * >::Iterator i;
3757 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3759 MapBlock *block = *i;
3761 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3767 JMutexAutoLock lock(block->mesh_mutex);
3768 if(block->mesh != NULL)
3770 /*block->mesh->drop();
3771 block->mesh = NULL;*/
3772 block->setMeshExpired(true);
3779 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3781 assert(mapType() == MAPTYPE_CLIENT);
3784 v3s16 p = blockpos + v3s16(0,0,0);
3785 MapBlock *b = getBlockNoCreate(p);
3786 b->updateMesh(daynight_ratio);
3787 //b->setMeshExpired(true);
3789 catch(InvalidPositionException &e){}
3792 v3s16 p = blockpos + v3s16(-1,0,0);
3793 MapBlock *b = getBlockNoCreate(p);
3794 b->updateMesh(daynight_ratio);
3795 //b->setMeshExpired(true);
3797 catch(InvalidPositionException &e){}
3799 v3s16 p = blockpos + v3s16(0,-1,0);
3800 MapBlock *b = getBlockNoCreate(p);
3801 b->updateMesh(daynight_ratio);
3802 //b->setMeshExpired(true);
3804 catch(InvalidPositionException &e){}
3806 v3s16 p = blockpos + v3s16(0,0,-1);
3807 MapBlock *b = getBlockNoCreate(p);
3808 b->updateMesh(daynight_ratio);
3809 //b->setMeshExpired(true);
3811 catch(InvalidPositionException &e){}
3816 Update mesh of block in which the node is, and if the node is at the
3817 leading edge, update the appropriate leading blocks too.
3819 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3827 v3s16 blockposes[4];
3828 for(u32 i=0; i<4; i++)
3830 v3s16 np = nodepos + dirs[i];
3831 blockposes[i] = getNodeBlockPos(np);
3832 // Don't update mesh of block if it has been done already
3833 bool already_updated = false;
3834 for(u32 j=0; j<i; j++)
3836 if(blockposes[j] == blockposes[i])
3838 already_updated = true;
3845 MapBlock *b = getBlockNoCreate(blockposes[i]);
3846 b->updateMesh(daynight_ratio);
3851 void ClientMap::PrintInfo(std::ostream &out)
3862 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3867 MapVoxelManipulator::~MapVoxelManipulator()
3869 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3873 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3875 TimeTaker timer1("emerge", &emerge_time);
3877 // Units of these are MapBlocks
3878 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3879 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3881 VoxelArea block_area_nodes
3882 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3884 addArea(block_area_nodes);
3886 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3887 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3888 for(s32 x=p_min.X; x<=p_max.X; x++)
3891 core::map<v3s16, bool>::Node *n;
3892 n = m_loaded_blocks.find(p);
3896 bool block_data_inexistent = false;
3899 TimeTaker timer1("emerge load", &emerge_load_time);
3901 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3902 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3905 dstream<<std::endl;*/
3907 MapBlock *block = m_map->getBlockNoCreate(p);
3908 if(block->isDummy())
3909 block_data_inexistent = true;
3911 block->copyTo(*this);
3913 catch(InvalidPositionException &e)
3915 block_data_inexistent = true;
3918 if(block_data_inexistent)
3920 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3921 // Fill with VOXELFLAG_INEXISTENT
3922 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3923 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3925 s32 i = m_area.index(a.MinEdge.X,y,z);
3926 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3930 m_loaded_blocks.insert(p, !block_data_inexistent);
3933 //dstream<<"emerge done"<<std::endl;
3937 SUGG: Add an option to only update eg. water and air nodes.
3938 This will make it interfere less with important stuff if
3941 void MapVoxelManipulator::blitBack
3942 (core::map<v3s16, MapBlock*> & modified_blocks)
3944 if(m_area.getExtent() == v3s16(0,0,0))
3947 //TimeTaker timer1("blitBack");
3949 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3950 <<m_loaded_blocks.size()<<std::endl;*/
3953 Initialize block cache
3955 v3s16 blockpos_last;
3956 MapBlock *block = NULL;
3957 bool block_checked_in_modified = false;
3959 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3960 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3961 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3965 u8 f = m_flags[m_area.index(p)];
3966 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3969 MapNode &n = m_data[m_area.index(p)];
3971 v3s16 blockpos = getNodeBlockPos(p);
3976 if(block == NULL || blockpos != blockpos_last){
3977 block = m_map->getBlockNoCreate(blockpos);
3978 blockpos_last = blockpos;
3979 block_checked_in_modified = false;
3982 // Calculate relative position in block
3983 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3985 // Don't continue if nothing has changed here
3986 if(block->getNode(relpos) == n)
3989 //m_map->setNode(m_area.MinEdge + p, n);
3990 block->setNode(relpos, n);
3993 Make sure block is in modified_blocks
3995 if(block_checked_in_modified == false)
3997 modified_blocks[blockpos] = block;
3998 block_checked_in_modified = true;
4001 catch(InvalidPositionException &e)
4007 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4008 MapVoxelManipulator(map),
4009 m_create_area(false)
4013 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4017 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4019 // Just create the area so that it can be pointed to
4020 VoxelManipulator::emerge(a, caller_id);
4023 void ManualMapVoxelManipulator::initialEmerge(
4024 v3s16 blockpos_min, v3s16 blockpos_max)
4026 TimeTaker timer1("initialEmerge", &emerge_time);
4028 // Units of these are MapBlocks
4029 v3s16 p_min = blockpos_min;
4030 v3s16 p_max = blockpos_max;
4032 VoxelArea block_area_nodes
4033 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4035 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4038 dstream<<"initialEmerge: area: ";
4039 block_area_nodes.print(dstream);
4040 dstream<<" ("<<size_MB<<"MB)";
4044 addArea(block_area_nodes);
4046 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4047 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4048 for(s32 x=p_min.X; x<=p_max.X; x++)
4051 core::map<v3s16, bool>::Node *n;
4052 n = m_loaded_blocks.find(p);
4056 bool block_data_inexistent = false;
4059 TimeTaker timer1("emerge load", &emerge_load_time);
4061 MapBlock *block = m_map->getBlockNoCreate(p);
4062 if(block->isDummy())
4063 block_data_inexistent = true;
4065 block->copyTo(*this);
4067 catch(InvalidPositionException &e)
4069 block_data_inexistent = true;
4072 if(block_data_inexistent)
4075 Mark area inexistent
4077 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4078 // Fill with VOXELFLAG_INEXISTENT
4079 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4080 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4082 s32 i = m_area.index(a.MinEdge.X,y,z);
4083 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4087 m_loaded_blocks.insert(p, !block_data_inexistent);
4091 void ManualMapVoxelManipulator::blitBackAll(
4092 core::map<v3s16, MapBlock*> * modified_blocks)
4094 if(m_area.getExtent() == v3s16(0,0,0))
4098 Copy data of all blocks
4100 for(core::map<v3s16, bool>::Iterator
4101 i = m_loaded_blocks.getIterator();
4102 i.atEnd() == false; i++)
4104 v3s16 p = i.getNode()->getKey();
4105 bool existed = i.getNode()->getValue();
4106 if(existed == false)
4108 // The Great Bug was found using this
4109 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4110 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4114 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4117 dstream<<"WARNING: "<<__FUNCTION_NAME
4118 <<": got NULL block "
4119 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4124 block->copyFrom(*this);
4127 modified_blocks->insert(p, block);