3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
30 #include "nodemetadata.h"
36 SQLite format specification:
37 - Initially only replaces sectors/ and sectors2/
44 Map::Map(std::ostream &dout):
48 /*m_sector_mutex.Init();
49 assert(m_sector_mutex.IsInitialized());*/
57 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
58 for(; i.atEnd() == false; i++)
60 MapSector *sector = i.getNode()->getValue();
65 void Map::addEventReceiver(MapEventReceiver *event_receiver)
67 m_event_receivers.insert(event_receiver, false);
70 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
72 if(m_event_receivers.find(event_receiver) == NULL)
74 m_event_receivers.remove(event_receiver);
77 void Map::dispatchEvent(MapEditEvent *event)
79 for(core::map<MapEventReceiver*, bool>::Iterator
80 i = m_event_receivers.getIterator();
81 i.atEnd()==false; i++)
83 MapEventReceiver* event_receiver = i.getNode()->getKey();
84 event_receiver->onMapEditEvent(event);
88 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
90 if(m_sector_cache != NULL && p == m_sector_cache_p){
91 MapSector * sector = m_sector_cache;
95 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
100 MapSector *sector = n->getValue();
102 // Cache the last result
103 m_sector_cache_p = p;
104 m_sector_cache = sector;
109 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
111 return getSectorNoGenerateNoExNoLock(p);
114 MapSector * Map::getSectorNoGenerate(v2s16 p)
116 MapSector *sector = getSectorNoGenerateNoEx(p);
118 throw InvalidPositionException();
123 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerateNoEx(p2d);
129 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
133 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
135 MapBlock *block = getBlockNoCreateNoEx(p3d);
137 throw InvalidPositionException();
141 bool Map::isNodeUnderground(v3s16 p)
143 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock * block = getBlockNoCreate(blockpos);
146 return block->getIsUnderground();
148 catch(InvalidPositionException &e)
154 bool Map::isValidPosition(v3s16 p)
156 v3s16 blockpos = getNodeBlockPos(p);
157 MapBlock *block = getBlockNoCreate(blockpos);
158 return (block != NULL);
161 // Returns a CONTENT_IGNORE node if not found
162 MapNode Map::getNodeNoEx(v3s16 p)
164 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock *block = getBlockNoCreateNoEx(blockpos);
167 return MapNode(CONTENT_IGNORE);
168 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
169 return block->getNodeNoCheck(relpos);
172 // throws InvalidPositionException if not found
173 MapNode Map::getNode(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock *block = getBlockNoCreateNoEx(blockpos);
178 throw InvalidPositionException();
179 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
180 return block->getNodeNoCheck(relpos);
183 // throws InvalidPositionException if not found
184 void Map::setNode(v3s16 p, MapNode & n)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreate(blockpos);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 block->setNodeNoCheck(relpos, n);
194 Goes recursively through the neighbours of the node.
196 Alters only transparent nodes.
198 If the lighting of the neighbour is lower than the lighting of
199 the node was (before changing it to 0 at the step before), the
200 lighting of the neighbour is set to 0 and then the same stuff
201 repeats for the neighbour.
203 The ending nodes of the routine are stored in light_sources.
204 This is useful when a light is removed. In such case, this
205 routine can be called for the light node and then again for
206 light_sources to re-light the area without the removed light.
208 values of from_nodes are lighting values.
210 void Map::unspreadLight(enum LightBank bank,
211 core::map<v3s16, u8> & from_nodes,
212 core::map<v3s16, bool> & light_sources,
213 core::map<v3s16, MapBlock*> & modified_blocks)
216 v3s16(0,0,1), // back
218 v3s16(1,0,0), // right
219 v3s16(0,0,-1), // front
220 v3s16(0,-1,0), // bottom
221 v3s16(-1,0,0), // left
224 if(from_nodes.size() == 0)
227 u32 blockchangecount = 0;
229 core::map<v3s16, u8> unlighted_nodes;
230 core::map<v3s16, u8>::Iterator j;
231 j = from_nodes.getIterator();
234 Initialize block cache
237 MapBlock *block = NULL;
238 // Cache this a bit, too
239 bool block_checked_in_modified = false;
241 for(; j.atEnd() == false; j++)
243 v3s16 pos = j.getNode()->getKey();
244 v3s16 blockpos = getNodeBlockPos(pos);
246 // Only fetch a new block if the block position has changed
248 if(block == NULL || blockpos != blockpos_last){
249 block = getBlockNoCreate(blockpos);
250 blockpos_last = blockpos;
252 block_checked_in_modified = false;
256 catch(InvalidPositionException &e)
264 // Calculate relative position in block
265 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
267 // Get node straight from the block
268 MapNode n = block->getNode(relpos);
270 u8 oldlight = j.getNode()->getValue();
272 // Loop through 6 neighbors
273 for(u16 i=0; i<6; i++)
275 // Get the position of the neighbor node
276 v3s16 n2pos = pos + dirs[i];
278 // Get the block where the node is located
279 v3s16 blockpos = getNodeBlockPos(n2pos);
283 // Only fetch a new block if the block position has changed
285 if(block == NULL || blockpos != blockpos_last){
286 block = getBlockNoCreate(blockpos);
287 blockpos_last = blockpos;
289 block_checked_in_modified = false;
293 catch(InvalidPositionException &e)
298 // Calculate relative position in block
299 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
300 // Get node straight from the block
301 MapNode n2 = block->getNode(relpos);
303 bool changed = false;
305 //TODO: Optimize output by optimizing light_sources?
308 If the neighbor is dimmer than what was specified
309 as oldlight (the light of the previous node)
311 if(n2.getLight(bank) < oldlight)
314 And the neighbor is transparent and it has some light
316 if(n2.light_propagates() && n2.getLight(bank) != 0)
319 Set light to 0 and add to queue
322 u8 current_light = n2.getLight(bank);
323 n2.setLight(bank, 0);
324 block->setNode(relpos, n2);
326 unlighted_nodes.insert(n2pos, current_light);
330 Remove from light_sources if it is there
331 NOTE: This doesn't happen nearly at all
333 /*if(light_sources.find(n2pos))
335 std::cout<<"Removed from light_sources"<<std::endl;
336 light_sources.remove(n2pos);
341 if(light_sources.find(n2pos) != NULL)
342 light_sources.remove(n2pos);*/
345 light_sources.insert(n2pos, true);
348 // Add to modified_blocks
349 if(changed == true && block_checked_in_modified == false)
351 // If the block is not found in modified_blocks, add.
352 if(modified_blocks.find(blockpos) == NULL)
354 modified_blocks.insert(blockpos, block);
356 block_checked_in_modified = true;
359 catch(InvalidPositionException &e)
366 /*dstream<<"unspreadLight(): Changed block "
367 <<blockchangecount<<" times"
368 <<" for "<<from_nodes.size()<<" nodes"
371 if(unlighted_nodes.size() > 0)
372 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
376 A single-node wrapper of the above
378 void Map::unLightNeighbors(enum LightBank bank,
379 v3s16 pos, u8 lightwas,
380 core::map<v3s16, bool> & light_sources,
381 core::map<v3s16, MapBlock*> & modified_blocks)
383 core::map<v3s16, u8> from_nodes;
384 from_nodes.insert(pos, lightwas);
386 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
390 Lights neighbors of from_nodes, collects all them and then
393 void Map::spreadLight(enum LightBank bank,
394 core::map<v3s16, bool> & from_nodes,
395 core::map<v3s16, MapBlock*> & modified_blocks)
397 const v3s16 dirs[6] = {
398 v3s16(0,0,1), // back
400 v3s16(1,0,0), // right
401 v3s16(0,0,-1), // front
402 v3s16(0,-1,0), // bottom
403 v3s16(-1,0,0), // left
406 if(from_nodes.size() == 0)
409 u32 blockchangecount = 0;
411 core::map<v3s16, bool> lighted_nodes;
412 core::map<v3s16, bool>::Iterator j;
413 j = from_nodes.getIterator();
416 Initialize block cache
419 MapBlock *block = NULL;
420 // Cache this a bit, too
421 bool block_checked_in_modified = false;
423 for(; j.atEnd() == false; j++)
424 //for(; j != from_nodes.end(); j++)
426 v3s16 pos = j.getNode()->getKey();
428 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
429 v3s16 blockpos = getNodeBlockPos(pos);
431 // Only fetch a new block if the block position has changed
433 if(block == NULL || blockpos != blockpos_last){
434 block = getBlockNoCreate(blockpos);
435 blockpos_last = blockpos;
437 block_checked_in_modified = false;
441 catch(InvalidPositionException &e)
449 // Calculate relative position in block
450 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
452 // Get node straight from the block
453 MapNode n = block->getNode(relpos);
455 u8 oldlight = n.getLight(bank);
456 u8 newlight = diminish_light(oldlight);
458 // Loop through 6 neighbors
459 for(u16 i=0; i<6; i++){
460 // Get the position of the neighbor node
461 v3s16 n2pos = pos + dirs[i];
463 // Get the block where the node is located
464 v3s16 blockpos = getNodeBlockPos(n2pos);
468 // Only fetch a new block if the block position has changed
470 if(block == NULL || blockpos != blockpos_last){
471 block = getBlockNoCreate(blockpos);
472 blockpos_last = blockpos;
474 block_checked_in_modified = false;
478 catch(InvalidPositionException &e)
483 // Calculate relative position in block
484 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
485 // Get node straight from the block
486 MapNode n2 = block->getNode(relpos);
488 bool changed = false;
490 If the neighbor is brighter than the current node,
491 add to list (it will light up this node on its turn)
493 if(n2.getLight(bank) > undiminish_light(oldlight))
495 lighted_nodes.insert(n2pos, true);
496 //lighted_nodes.push_back(n2pos);
500 If the neighbor is dimmer than how much light this node
501 would spread on it, add to list
503 if(n2.getLight(bank) < newlight)
505 if(n2.light_propagates())
507 n2.setLight(bank, newlight);
508 block->setNode(relpos, n2);
509 lighted_nodes.insert(n2pos, true);
510 //lighted_nodes.push_back(n2pos);
515 // Add to modified_blocks
516 if(changed == true && block_checked_in_modified == false)
518 // If the block is not found in modified_blocks, add.
519 if(modified_blocks.find(blockpos) == NULL)
521 modified_blocks.insert(blockpos, block);
523 block_checked_in_modified = true;
526 catch(InvalidPositionException &e)
533 /*dstream<<"spreadLight(): Changed block "
534 <<blockchangecount<<" times"
535 <<" for "<<from_nodes.size()<<" nodes"
538 if(lighted_nodes.size() > 0)
539 spreadLight(bank, lighted_nodes, modified_blocks);
543 A single-node source variation of the above.
545 void Map::lightNeighbors(enum LightBank bank,
547 core::map<v3s16, MapBlock*> & modified_blocks)
549 core::map<v3s16, bool> from_nodes;
550 from_nodes.insert(pos, true);
551 spreadLight(bank, from_nodes, modified_blocks);
554 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
557 v3s16(0,0,1), // back
559 v3s16(1,0,0), // right
560 v3s16(0,0,-1), // front
561 v3s16(0,-1,0), // bottom
562 v3s16(-1,0,0), // left
565 u8 brightest_light = 0;
566 v3s16 brightest_pos(0,0,0);
567 bool found_something = false;
569 // Loop through 6 neighbors
570 for(u16 i=0; i<6; i++){
571 // Get the position of the neighbor node
572 v3s16 n2pos = p + dirs[i];
577 catch(InvalidPositionException &e)
581 if(n2.getLight(bank) > brightest_light || found_something == false){
582 brightest_light = n2.getLight(bank);
583 brightest_pos = n2pos;
584 found_something = true;
588 if(found_something == false)
589 throw InvalidPositionException();
591 return brightest_pos;
595 Propagates sunlight down from a node.
596 Starting point gets sunlight.
598 Returns the lowest y value of where the sunlight went.
600 Mud is turned into grass in where the sunlight stops.
602 s16 Map::propagateSunlight(v3s16 start,
603 core::map<v3s16, MapBlock*> & modified_blocks)
608 v3s16 pos(start.X, y, start.Z);
610 v3s16 blockpos = getNodeBlockPos(pos);
613 block = getBlockNoCreate(blockpos);
615 catch(InvalidPositionException &e)
620 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
621 MapNode n = block->getNode(relpos);
623 if(n.sunlight_propagates())
625 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
626 block->setNode(relpos, n);
628 modified_blocks.insert(blockpos, block);
632 /*// Turn mud into grass
633 if(n.d == CONTENT_MUD)
636 block->setNode(relpos, n);
637 modified_blocks.insert(blockpos, block);
640 // Sunlight goes no further
647 void Map::updateLighting(enum LightBank bank,
648 core::map<v3s16, MapBlock*> & a_blocks,
649 core::map<v3s16, MapBlock*> & modified_blocks)
651 /*m_dout<<DTIME<<"Map::updateLighting(): "
652 <<a_blocks.size()<<" blocks."<<std::endl;*/
654 //TimeTaker timer("updateLighting");
658 //u32 count_was = modified_blocks.size();
660 core::map<v3s16, MapBlock*> blocks_to_update;
662 core::map<v3s16, bool> light_sources;
664 core::map<v3s16, u8> unlight_from;
666 core::map<v3s16, MapBlock*>::Iterator i;
667 i = a_blocks.getIterator();
668 for(; i.atEnd() == false; i++)
670 MapBlock *block = i.getNode()->getValue();
674 // Don't bother with dummy blocks.
678 v3s16 pos = block->getPos();
679 modified_blocks.insert(pos, block);
681 blocks_to_update.insert(pos, block);
684 Clear all light from block
686 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
687 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
688 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
693 MapNode n = block->getNode(v3s16(x,y,z));
694 u8 oldlight = n.getLight(bank);
696 block->setNode(v3s16(x,y,z), n);
698 // Collect borders for unlighting
699 if(x==0 || x == MAP_BLOCKSIZE-1
700 || y==0 || y == MAP_BLOCKSIZE-1
701 || z==0 || z == MAP_BLOCKSIZE-1)
703 v3s16 p_map = p + v3s16(
706 MAP_BLOCKSIZE*pos.Z);
707 unlight_from.insert(p_map, oldlight);
710 catch(InvalidPositionException &e)
713 This would happen when dealing with a
717 dstream<<"updateLighting(): InvalidPositionException"
722 if(bank == LIGHTBANK_DAY)
724 bool bottom_valid = block->propagateSunlight(light_sources);
726 // If bottom is valid, we're done.
730 else if(bank == LIGHTBANK_NIGHT)
732 // For night lighting, sunlight is not propagated
737 // Invalid lighting bank
741 /*dstream<<"Bottom for sunlight-propagated block ("
742 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
745 // Bottom sunlight is not valid; get the block and loop to it
749 block = getBlockNoCreate(pos);
751 catch(InvalidPositionException &e)
760 Enable this to disable proper lighting for speeding up map
761 generation for testing or whatever
764 //if(g_settings.get(""))
766 core::map<v3s16, MapBlock*>::Iterator i;
767 i = blocks_to_update.getIterator();
768 for(; i.atEnd() == false; i++)
770 MapBlock *block = i.getNode()->getValue();
771 v3s16 p = block->getPos();
772 block->setLightingExpired(false);
780 TimeTaker timer("unspreadLight");
781 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
786 u32 diff = modified_blocks.size() - count_was;
787 count_was = modified_blocks.size();
788 dstream<<"unspreadLight modified "<<diff<<std::endl;
792 TimeTaker timer("spreadLight");
793 spreadLight(bank, light_sources, modified_blocks);
798 u32 diff = modified_blocks.size() - count_was;
799 count_was = modified_blocks.size();
800 dstream<<"spreadLight modified "<<diff<<std::endl;
805 //MapVoxelManipulator vmanip(this);
807 // Make a manual voxel manipulator and load all the blocks
808 // that touch the requested blocks
809 ManualMapVoxelManipulator vmanip(this);
810 core::map<v3s16, MapBlock*>::Iterator i;
811 i = blocks_to_update.getIterator();
812 for(; i.atEnd() == false; i++)
814 MapBlock *block = i.getNode()->getValue();
815 v3s16 p = block->getPos();
817 // Add all surrounding blocks
818 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
821 Add all surrounding blocks that have up-to-date lighting
822 NOTE: This doesn't quite do the job (not everything
823 appropriate is lighted)
825 /*for(s16 z=-1; z<=1; z++)
826 for(s16 y=-1; y<=1; y++)
827 for(s16 x=-1; x<=1; x++)
830 MapBlock *block = getBlockNoCreateNoEx(p);
835 if(block->getLightingExpired())
837 vmanip.initialEmerge(p, p);
840 // Lighting of block will be updated completely
841 block->setLightingExpired(false);
845 //TimeTaker timer("unSpreadLight");
846 vmanip.unspreadLight(bank, unlight_from, light_sources);
849 //TimeTaker timer("spreadLight");
850 vmanip.spreadLight(bank, light_sources);
853 //TimeTaker timer("blitBack");
854 vmanip.blitBack(modified_blocks);
856 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
860 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
863 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
864 core::map<v3s16, MapBlock*> & modified_blocks)
866 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
867 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
870 Update information about whether day and night light differ
872 for(core::map<v3s16, MapBlock*>::Iterator
873 i = modified_blocks.getIterator();
874 i.atEnd() == false; i++)
876 MapBlock *block = i.getNode()->getValue();
877 block->updateDayNightDiff();
883 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
884 core::map<v3s16, MapBlock*> &modified_blocks)
887 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
888 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
891 From this node to nodes underneath:
892 If lighting is sunlight (1.0), unlight neighbours and
897 v3s16 toppos = p + v3s16(0,1,0);
898 v3s16 bottompos = p + v3s16(0,-1,0);
900 bool node_under_sunlight = true;
901 core::map<v3s16, bool> light_sources;
904 If there is a node at top and it doesn't have sunlight,
905 there has not been any sunlight going down.
907 Otherwise there probably is.
910 MapNode topnode = getNode(toppos);
912 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
913 node_under_sunlight = false;
915 catch(InvalidPositionException &e)
921 If the new node is solid and there is grass below, change it to mud
923 if(content_features(n.d).walkable == true)
926 MapNode bottomnode = getNode(bottompos);
928 if(bottomnode.d == CONTENT_GRASS
929 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
931 bottomnode.d = CONTENT_MUD;
932 setNode(bottompos, bottomnode);
935 catch(InvalidPositionException &e)
943 If the new node is mud and it is under sunlight, change it
946 if(n.d == CONTENT_MUD && node_under_sunlight)
953 Remove all light that has come out of this node
956 enum LightBank banks[] =
961 for(s32 i=0; i<2; i++)
963 enum LightBank bank = banks[i];
965 u8 lightwas = getNode(p).getLight(bank);
967 // Add the block of the added node to modified_blocks
968 v3s16 blockpos = getNodeBlockPos(p);
969 MapBlock * block = getBlockNoCreate(blockpos);
970 assert(block != NULL);
971 modified_blocks.insert(blockpos, block);
973 assert(isValidPosition(p));
975 // Unlight neighbours of node.
976 // This means setting light of all consequent dimmer nodes
978 // This also collects the nodes at the border which will spread
979 // light again into this.
980 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
986 If node lets sunlight through and is under sunlight, it has
989 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
991 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
995 Set the node on the map
1004 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
1007 NodeMetadata *meta = meta_proto->clone();
1008 setNodeMetadata(p, meta);
1012 If node is under sunlight and doesn't let sunlight through,
1013 take all sunlighted nodes under it and clear light from them
1014 and from where the light has been spread.
1015 TODO: This could be optimized by mass-unlighting instead
1018 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
1022 //m_dout<<DTIME<<"y="<<y<<std::endl;
1023 v3s16 n2pos(p.X, y, p.Z);
1027 n2 = getNode(n2pos);
1029 catch(InvalidPositionException &e)
1034 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1036 unLightNeighbors(LIGHTBANK_DAY,
1037 n2pos, n2.getLight(LIGHTBANK_DAY),
1038 light_sources, modified_blocks);
1039 n2.setLight(LIGHTBANK_DAY, 0);
1047 for(s32 i=0; i<2; i++)
1049 enum LightBank bank = banks[i];
1052 Spread light from all nodes that might be capable of doing so
1054 spreadLight(bank, light_sources, modified_blocks);
1058 Update information about whether day and night light differ
1060 for(core::map<v3s16, MapBlock*>::Iterator
1061 i = modified_blocks.getIterator();
1062 i.atEnd() == false; i++)
1064 MapBlock *block = i.getNode()->getValue();
1065 block->updateDayNightDiff();
1069 Add neighboring liquid nodes and the node itself if it is
1070 liquid (=water node was added) to transform queue.
1073 v3s16(0,0,0), // self
1074 v3s16(0,0,1), // back
1075 v3s16(0,1,0), // top
1076 v3s16(1,0,0), // right
1077 v3s16(0,0,-1), // front
1078 v3s16(0,-1,0), // bottom
1079 v3s16(-1,0,0), // left
1081 for(u16 i=0; i<7; i++)
1086 v3s16 p2 = p + dirs[i];
1088 MapNode n2 = getNode(p2);
1089 if(content_liquid(n2.d))
1091 m_transforming_liquid.push_back(p2);
1094 }catch(InvalidPositionException &e)
1102 void Map::removeNodeAndUpdate(v3s16 p,
1103 core::map<v3s16, MapBlock*> &modified_blocks)
1105 /*PrintInfo(m_dout);
1106 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1107 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1109 bool node_under_sunlight = true;
1111 v3s16 toppos = p + v3s16(0,1,0);
1113 // Node will be replaced with this
1114 u8 replace_material = CONTENT_AIR;
1117 If there is a node at top and it doesn't have sunlight,
1118 there will be no sunlight going down.
1121 MapNode topnode = getNode(toppos);
1123 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1124 node_under_sunlight = false;
1126 catch(InvalidPositionException &e)
1130 core::map<v3s16, bool> light_sources;
1132 enum LightBank banks[] =
1137 for(s32 i=0; i<2; i++)
1139 enum LightBank bank = banks[i];
1142 Unlight neighbors (in case the node is a light source)
1144 unLightNeighbors(bank, p,
1145 getNode(p).getLight(bank),
1146 light_sources, modified_blocks);
1150 Remove node metadata
1153 removeNodeMetadata(p);
1157 This also clears the lighting.
1161 n.d = replace_material;
1164 for(s32 i=0; i<2; i++)
1166 enum LightBank bank = banks[i];
1169 Recalculate lighting
1171 spreadLight(bank, light_sources, modified_blocks);
1174 // Add the block of the removed node to modified_blocks
1175 v3s16 blockpos = getNodeBlockPos(p);
1176 MapBlock * block = getBlockNoCreate(blockpos);
1177 assert(block != NULL);
1178 modified_blocks.insert(blockpos, block);
1181 If the removed node was under sunlight, propagate the
1182 sunlight down from it and then light all neighbors
1183 of the propagated blocks.
1185 if(node_under_sunlight)
1187 s16 ybottom = propagateSunlight(p, modified_blocks);
1188 /*m_dout<<DTIME<<"Node was under sunlight. "
1189 "Propagating sunlight";
1190 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1192 for(; y >= ybottom; y--)
1194 v3s16 p2(p.X, y, p.Z);
1195 /*m_dout<<DTIME<<"lighting neighbors of node ("
1196 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1198 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1203 // Set the lighting of this node to 0
1204 // TODO: Is this needed? Lighting is cleared up there already.
1206 MapNode n = getNode(p);
1207 n.setLight(LIGHTBANK_DAY, 0);
1210 catch(InvalidPositionException &e)
1216 for(s32 i=0; i<2; i++)
1218 enum LightBank bank = banks[i];
1220 // Get the brightest neighbour node and propagate light from it
1221 v3s16 n2p = getBrightestNeighbour(bank, p);
1223 MapNode n2 = getNode(n2p);
1224 lightNeighbors(bank, n2p, modified_blocks);
1226 catch(InvalidPositionException &e)
1232 Update information about whether day and night light differ
1234 for(core::map<v3s16, MapBlock*>::Iterator
1235 i = modified_blocks.getIterator();
1236 i.atEnd() == false; i++)
1238 MapBlock *block = i.getNode()->getValue();
1239 block->updateDayNightDiff();
1243 Add neighboring liquid nodes to transform queue.
1246 v3s16(0,0,1), // back
1247 v3s16(0,1,0), // top
1248 v3s16(1,0,0), // right
1249 v3s16(0,0,-1), // front
1250 v3s16(0,-1,0), // bottom
1251 v3s16(-1,0,0), // left
1253 for(u16 i=0; i<6; i++)
1258 v3s16 p2 = p + dirs[i];
1260 MapNode n2 = getNode(p2);
1261 if(content_liquid(n2.d))
1263 m_transforming_liquid.push_back(p2);
1266 }catch(InvalidPositionException &e)
1272 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1275 event.type = MEET_ADDNODE;
1279 bool succeeded = true;
1281 core::map<v3s16, MapBlock*> modified_blocks;
1282 addNodeAndUpdate(p, n, modified_blocks);
1284 // Copy modified_blocks to event
1285 for(core::map<v3s16, MapBlock*>::Iterator
1286 i = modified_blocks.getIterator();
1287 i.atEnd()==false; i++)
1289 event.modified_blocks.insert(i.getNode()->getKey(), false);
1292 catch(InvalidPositionException &e){
1296 dispatchEvent(&event);
1301 bool Map::removeNodeWithEvent(v3s16 p)
1304 event.type = MEET_REMOVENODE;
1307 bool succeeded = true;
1309 core::map<v3s16, MapBlock*> modified_blocks;
1310 removeNodeAndUpdate(p, modified_blocks);
1312 // Copy modified_blocks to event
1313 for(core::map<v3s16, MapBlock*>::Iterator
1314 i = modified_blocks.getIterator();
1315 i.atEnd()==false; i++)
1317 event.modified_blocks.insert(i.getNode()->getKey(), false);
1320 catch(InvalidPositionException &e){
1324 dispatchEvent(&event);
1329 bool Map::dayNightDiffed(v3s16 blockpos)
1332 v3s16 p = blockpos + v3s16(0,0,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1340 v3s16 p = blockpos + v3s16(-1,0,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1347 v3s16 p = blockpos + v3s16(0,-1,0);
1348 MapBlock *b = getBlockNoCreate(p);
1349 if(b->dayNightDiffed())
1352 catch(InvalidPositionException &e){}
1354 v3s16 p = blockpos + v3s16(0,0,-1);
1355 MapBlock *b = getBlockNoCreate(p);
1356 if(b->dayNightDiffed())
1359 catch(InvalidPositionException &e){}
1362 v3s16 p = blockpos + v3s16(1,0,0);
1363 MapBlock *b = getBlockNoCreate(p);
1364 if(b->dayNightDiffed())
1367 catch(InvalidPositionException &e){}
1369 v3s16 p = blockpos + v3s16(0,1,0);
1370 MapBlock *b = getBlockNoCreate(p);
1371 if(b->dayNightDiffed())
1374 catch(InvalidPositionException &e){}
1376 v3s16 p = blockpos + v3s16(0,0,1);
1377 MapBlock *b = getBlockNoCreate(p);
1378 if(b->dayNightDiffed())
1381 catch(InvalidPositionException &e){}
1387 Updates usage timers
1389 void Map::timerUpdate(float dtime)
1391 core::map<v2s16, MapSector*>::Iterator si;
1393 si = m_sectors.getIterator();
1394 for(; si.atEnd() == false; si++)
1396 MapSector *sector = si.getNode()->getValue();
1398 core::list<MapBlock*> blocks;
1399 sector->getBlocks(blocks);
1400 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1401 i != blocks.end(); i++)
1403 (*i)->incrementUsageTimer(dtime);
1408 void Map::deleteSectors(core::list<v2s16> &list)
1410 core::list<v2s16>::Iterator j;
1411 for(j=list.begin(); j!=list.end(); j++)
1413 MapSector *sector = m_sectors[*j];
1414 // If sector is in sector cache, remove it from there
1415 if(m_sector_cache == sector)
1416 m_sector_cache = NULL;
1417 // Remove from map and delete
1418 m_sectors.remove(*j);
1423 void Map::unloadUnusedData(float timeout,
1424 core::list<v3s16> *deleted_blocks)
1426 core::list<v2s16> sector_deletion_queue;
1427 u32 deleted_blocks_count = 0;
1428 u32 saved_blocks_count = 0;
1430 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1431 for(; si.atEnd() == false; si++)
1433 MapSector *sector = si.getNode()->getValue();
1435 bool all_blocks_deleted = true;
1437 core::list<MapBlock*> blocks;
1438 sector->getBlocks(blocks);
1439 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1440 i != blocks.end(); i++)
1442 MapBlock *block = (*i);
1444 if(block->getUsageTimer() > timeout)
1447 if(block->getModified() != MOD_STATE_CLEAN)
1450 saved_blocks_count++;
1452 // Delete from memory
1453 sector->deleteBlock(block);
1454 deleted_blocks_count++;
1458 all_blocks_deleted = false;
1462 if(all_blocks_deleted)
1464 sector_deletion_queue.push_back(si.getNode()->getKey());
1468 deleteSectors(sector_deletion_queue);
1470 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1471 <<", of which "<<saved_blocks_count<<" were wr."
1474 //return sector_deletion_queue.getSize();
1475 //return deleted_blocks_count;
1478 void Map::PrintInfo(std::ostream &out)
1483 #define WATER_DROP_BOOST 4
1485 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1487 DSTACK(__FUNCTION_NAME);
1488 //TimeTaker timer("transformLiquids()");
1491 u32 initial_size = m_transforming_liquid.size();
1493 /*if(initial_size != 0)
1494 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1496 while(m_transforming_liquid.size() != 0)
1499 Get a queued transforming liquid node
1501 v3s16 p0 = m_transforming_liquid.pop_front();
1503 MapNode n0 = getNode(p0);
1505 // Don't deal with non-liquids
1506 if(content_liquid(n0.d) == false)
1509 bool is_source = !content_flowing_liquid(n0.d);
1511 u8 liquid_level = 8;
1512 if(is_source == false)
1513 liquid_level = n0.param2 & 0x0f;
1515 // Turn possible source into non-source
1516 u8 nonsource_c = make_liquid_flowing(n0.d);
1519 If not source, check that some node flows into this one
1520 and what is the level of liquid in this one
1522 if(is_source == false)
1524 s8 new_liquid_level_max = -1;
1526 v3s16 dirs_from[5] = {
1527 v3s16(0,1,0), // top
1528 v3s16(0,0,1), // back
1529 v3s16(1,0,0), // right
1530 v3s16(0,0,-1), // front
1531 v3s16(-1,0,0), // left
1533 for(u16 i=0; i<5; i++)
1538 bool from_top = (i==0);
1540 v3s16 p2 = p0 + dirs_from[i];
1541 MapNode n2 = getNode(p2);
1543 if(content_liquid(n2.d))
1545 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1546 // Check that the liquids are the same type
1547 if(n2_nonsource_c != nonsource_c)
1549 dstream<<"WARNING: Not handling: different liquids"
1550 " collide"<<std::endl;
1553 bool n2_is_source = !content_flowing_liquid(n2.d);
1554 s8 n2_liquid_level = 8;
1555 if(n2_is_source == false)
1556 n2_liquid_level = n2.param2 & 0x07;
1558 s8 new_liquid_level = -1;
1561 //new_liquid_level = 7;
1562 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1563 new_liquid_level = 7;
1565 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1567 else if(n2_liquid_level > 0)
1569 new_liquid_level = n2_liquid_level - 1;
1572 if(new_liquid_level > new_liquid_level_max)
1573 new_liquid_level_max = new_liquid_level;
1576 }catch(InvalidPositionException &e)
1582 If liquid level should be something else, update it and
1583 add all the neighboring water nodes to the transform queue.
1585 if(new_liquid_level_max != liquid_level)
1587 if(new_liquid_level_max == -1)
1589 // Remove water alltoghether
1596 n0.param2 = new_liquid_level_max;
1600 // Block has been modified
1602 v3s16 blockpos = getNodeBlockPos(p0);
1603 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1605 modified_blocks.insert(blockpos, block);
1609 Add neighboring non-source liquid nodes to transform queue.
1612 v3s16(0,0,1), // back
1613 v3s16(0,1,0), // top
1614 v3s16(1,0,0), // right
1615 v3s16(0,0,-1), // front
1616 v3s16(0,-1,0), // bottom
1617 v3s16(-1,0,0), // left
1619 for(u16 i=0; i<6; i++)
1624 v3s16 p2 = p0 + dirs[i];
1626 MapNode n2 = getNode(p2);
1627 if(content_flowing_liquid(n2.d))
1629 m_transforming_liquid.push_back(p2);
1632 }catch(InvalidPositionException &e)
1639 // Get a new one from queue if the node has turned into non-water
1640 if(content_liquid(n0.d) == false)
1644 Flow water from this node
1646 v3s16 dirs_to[5] = {
1647 v3s16(0,-1,0), // bottom
1648 v3s16(0,0,1), // back
1649 v3s16(1,0,0), // right
1650 v3s16(0,0,-1), // front
1651 v3s16(-1,0,0), // left
1653 for(u16 i=0; i<5; i++)
1658 bool to_bottom = (i == 0);
1660 // If liquid is at lowest possible height, it's not going
1661 // anywhere except down
1662 if(liquid_level == 0 && to_bottom == false)
1665 u8 liquid_next_level = 0;
1666 // If going to bottom
1669 //liquid_next_level = 7;
1670 if(liquid_level >= 7 - WATER_DROP_BOOST)
1671 liquid_next_level = 7;
1673 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1676 liquid_next_level = liquid_level - 1;
1678 bool n2_changed = false;
1679 bool flowed = false;
1681 v3s16 p2 = p0 + dirs_to[i];
1683 MapNode n2 = getNode(p2);
1684 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1686 if(content_liquid(n2.d))
1688 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1689 // Check that the liquids are the same type
1690 if(n2_nonsource_c != nonsource_c)
1692 dstream<<"WARNING: Not handling: different liquids"
1693 " collide"<<std::endl;
1696 bool n2_is_source = !content_flowing_liquid(n2.d);
1697 u8 n2_liquid_level = 8;
1698 if(n2_is_source == false)
1699 n2_liquid_level = n2.param2 & 0x07;
1708 // Just flow into the source, nothing changes.
1709 // n2_changed is not set because destination didn't change
1714 if(liquid_next_level > liquid_level)
1716 n2.param2 = liquid_next_level;
1724 else if(n2.d == CONTENT_AIR)
1727 n2.param2 = liquid_next_level;
1734 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1738 m_transforming_liquid.push_back(p2);
1740 v3s16 blockpos = getNodeBlockPos(p2);
1741 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1743 modified_blocks.insert(blockpos, block);
1746 // If n2_changed to bottom, don't flow anywhere else
1747 if(to_bottom && flowed && !is_source)
1750 }catch(InvalidPositionException &e)
1756 //if(loopcount >= 100000)
1757 if(loopcount >= initial_size * 1)
1760 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1763 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1765 v3s16 blockpos = getNodeBlockPos(p);
1766 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1767 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1770 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1774 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1778 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1780 v3s16 blockpos = getNodeBlockPos(p);
1781 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1782 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1785 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1789 block->m_node_metadata.set(p_rel, meta);
1792 void Map::removeNodeMetadata(v3s16 p)
1794 v3s16 blockpos = getNodeBlockPos(p);
1795 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1796 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1799 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1803 block->m_node_metadata.remove(p_rel);
1806 void Map::nodeMetadataStep(float dtime,
1807 core::map<v3s16, MapBlock*> &changed_blocks)
1811 Currently there is no way to ensure that all the necessary
1812 blocks are loaded when this is run. (They might get unloaded)
1813 NOTE: ^- Actually, that might not be so. In a quick test it
1814 reloaded a block with a furnace when I walked back to it from
1817 core::map<v2s16, MapSector*>::Iterator si;
1818 si = m_sectors.getIterator();
1819 for(; si.atEnd() == false; si++)
1821 MapSector *sector = si.getNode()->getValue();
1822 core::list< MapBlock * > sectorblocks;
1823 sector->getBlocks(sectorblocks);
1824 core::list< MapBlock * >::Iterator i;
1825 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1827 MapBlock *block = *i;
1828 bool changed = block->m_node_metadata.step(dtime);
1830 changed_blocks[block->getPos()] = block;
1839 ServerMap::ServerMap(std::string savedir):
1842 m_map_metadata_changed(true)
1844 dstream<<__FUNCTION_NAME<<std::endl;
1846 //m_chunksize = 8; // Takes a few seconds
1848 m_seed = (((u64)(myrand()%0xffff)<<0)
1849 + ((u64)(myrand()%0xffff)<<16)
1850 + ((u64)(myrand()%0xffff)<<32)
1851 + ((u64)(myrand()%0xffff)<<48));
1854 Experimental and debug stuff
1861 Try to load map; if not found, create a new one.
1864 m_savedir = savedir;
1865 m_map_saving_enabled = false;
1869 // If directory exists, check contents and load if possible
1870 if(fs::PathExists(m_savedir))
1872 // If directory is empty, it is safe to save into it.
1873 if(fs::GetDirListing(m_savedir).size() == 0)
1875 dstream<<DTIME<<"Server: Empty save directory is valid."
1877 m_map_saving_enabled = true;
1882 // Load map metadata (seed, chunksize)
1885 catch(FileNotGoodException &e){
1886 dstream<<DTIME<<"WARNING: Could not load map metadata"
1887 //<<" Disabling chunk-based generator."
1893 // Load chunk metadata
1896 catch(FileNotGoodException &e){
1897 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1898 <<" Disabling chunk-based generator."
1903 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1904 "metadata and sector (0,0) from "<<savedir<<
1905 ", assuming valid save directory."
1908 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1909 <<"and chunk metadata from "<<savedir
1910 <<", assuming valid save directory."
1913 m_map_saving_enabled = true;
1914 // Map loaded, not creating new one
1918 // If directory doesn't exist, it is safe to save to it
1920 m_map_saving_enabled = true;
1923 catch(std::exception &e)
1925 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1926 <<", exception: "<<e.what()<<std::endl;
1927 dstream<<"Please remove the map or fix it."<<std::endl;
1928 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1931 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1933 // Create zero sector
1934 emergeSector(v2s16(0,0));
1936 // Initially write whole map
1940 ServerMap::~ServerMap()
1942 dstream<<__FUNCTION_NAME<<std::endl;
1946 if(m_map_saving_enabled)
1949 // Save only changed parts
1951 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1955 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1958 catch(std::exception &e)
1960 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1961 <<", exception: "<<e.what()<<std::endl;
1968 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1969 for(; i.atEnd() == false; i++)
1971 MapChunk *chunk = i.getNode()->getValue();
1977 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
1979 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
1980 <<blockpos.Z<<")"<<std::endl;*/
1982 data->no_op = false;
1983 data->seed = m_seed;
1984 data->blockpos = blockpos;
1987 Create the whole area of this and the neighboring blocks
1990 //TimeTaker timer("initBlockMake() create area");
1992 for(s16 x=-1; x<=1; x++)
1993 for(s16 z=-1; z<=1; z++)
1995 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
1996 // Sector metadata is loaded from disk if not already loaded.
1997 ServerMapSector *sector = createSector(sectorpos);
2000 for(s16 y=-1; y<=1; y++)
2002 MapBlock *block = createBlock(blockpos);
2004 // Lighting won't be calculated
2005 block->setLightingExpired(true);
2006 // Lighting will be calculated
2007 //block->setLightingExpired(false);
2010 Block gets sunlight if this is true.
2012 This should be set to true when the top side of a block
2013 is completely exposed to the sky.
2015 block->setIsUnderground(false);
2021 Now we have a big empty area.
2023 Make a ManualMapVoxelManipulator that contains this and the
2027 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2028 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2030 data->vmanip = new ManualMapVoxelManipulator(this);
2031 //data->vmanip->setMap(this);
2035 //TimeTaker timer("initBlockMake() initialEmerge");
2036 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2039 // Data is ready now.
2042 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2043 core::map<v3s16, MapBlock*> &changed_blocks)
2045 v3s16 blockpos = data->blockpos;
2046 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2047 <<blockpos.Z<<")"<<std::endl;*/
2051 dstream<<"finishBlockMake(): no-op"<<std::endl;
2055 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2057 /*dstream<<"Resulting vmanip:"<<std::endl;
2058 data->vmanip.print(dstream);*/
2061 Blit generated stuff to map
2062 NOTE: blitBackAll adds nearly everything to changed_blocks
2066 //TimeTaker timer("finishBlockMake() blitBackAll");
2067 data->vmanip->blitBackAll(&changed_blocks);
2070 if(enable_mapgen_debug_info)
2071 dstream<<"finishBlockMake: changed_blocks.size()="
2072 <<changed_blocks.size()<<std::endl;
2075 Copy transforming liquid information
2077 while(data->transforming_liquid.size() > 0)
2079 v3s16 p = data->transforming_liquid.pop_front();
2080 m_transforming_liquid.push_back(p);
2086 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2090 Set is_underground flag for lighting with sunlight
2093 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2096 Add sunlight to central block.
2097 This makes in-dark-spawning monsters to not flood the whole thing.
2098 Do not spread the light, though.
2100 /*core::map<v3s16, bool> light_sources;
2101 bool black_air_left = false;
2102 block->propagateSunlight(light_sources, true, &black_air_left);*/
2105 NOTE: Lighting and object adding shouldn't really be here, but
2106 lighting is a bit tricky to move properly to makeBlock.
2107 TODO: Do this the right way anyway.
2114 TimeTaker t("finishBlockMake lighting update");
2116 core::map<v3s16, MapBlock*> lighting_update_blocks;
2118 lighting_update_blocks.insert(block->getPos(), block);
2120 // All modified blocks
2121 for(core::map<v3s16, MapBlock*>::Iterator
2122 i = changed_blocks.getIterator();
2123 i.atEnd() == false; i++)
2125 lighting_update_blocks.insert(i.getNode()->getKey(),
2126 i.getNode()->getValue());
2129 updateLighting(lighting_update_blocks, changed_blocks);
2131 if(enable_mapgen_debug_info == false)
2132 t.stop(true); // Hide output
2136 Add random objects to block
2138 mapgen::add_random_objects(block);
2141 Go through changed blocks
2143 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2144 i.atEnd() == false; i++)
2146 MapBlock *block = i.getNode()->getValue();
2149 Update day/night difference cache of the MapBlocks
2151 block->updateDayNightDiff();
2153 Set block as modified
2155 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2159 Set central block as generated
2161 block->setGenerated(true);
2164 Save changed parts of map
2165 NOTE: Will be saved later.
2169 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2170 <<blockpos.Z<<")"<<std::endl;*/
2175 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2177 DSTACKF("%s: p2d=(%d,%d)",
2182 Check if it exists already in memory
2184 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2189 Try to load it from disk (with blocks)
2191 //if(loadSectorFull(p2d) == true)
2194 Try to load metadata from disk
2196 if(loadSectorMeta(p2d) == true)
2198 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2201 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2202 throw InvalidPositionException("");
2208 Do not create over-limit
2210 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2211 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2212 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2213 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2214 throw InvalidPositionException("createSector(): pos. over limit");
2217 Generate blank sector
2220 sector = new ServerMapSector(this, p2d);
2222 // Sector position on map in nodes
2223 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2228 m_sectors.insert(p2d, sector);
2234 This is a quick-hand function for calling makeBlock().
2236 MapBlock * ServerMap::generateBlock(
2238 core::map<v3s16, MapBlock*> &modified_blocks
2241 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2243 /*dstream<<"generateBlock(): "
2244 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2247 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2249 TimeTaker timer("generateBlock");
2251 //MapBlock *block = original_dummy;
2253 v2s16 p2d(p.X, p.Z);
2254 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2257 Do not generate over-limit
2259 if(blockpos_over_limit(p))
2261 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2262 throw InvalidPositionException("generateBlock(): pos. over limit");
2266 Create block make data
2268 mapgen::BlockMakeData data;
2269 initBlockMake(&data, p);
2275 TimeTaker t("mapgen::make_block()");
2276 mapgen::make_block(&data);
2278 if(enable_mapgen_debug_info == false)
2279 t.stop(true); // Hide output
2283 Blit data back on map, update lighting, add mobs and whatever this does
2285 finishBlockMake(&data, modified_blocks);
2290 MapBlock *block = getBlockNoCreateNoEx(p);
2297 bool erroneus_content = false;
2298 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2299 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2300 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2303 MapNode n = block->getNode(p);
2304 if(n.d == CONTENT_IGNORE)
2306 dstream<<"CONTENT_IGNORE at "
2307 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2309 erroneus_content = true;
2313 if(erroneus_content)
2321 Generate a completely empty block
2323 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2324 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2326 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2332 n.d = CONTENT_STONE;
2333 block->setNode(v3s16(x0,y0,z0), n);
2338 if(enable_mapgen_debug_info == false)
2339 timer.stop(true); // Hide output
2344 MapBlock * ServerMap::createBlock(v3s16 p)
2346 DSTACKF("%s: p=(%d,%d,%d)",
2347 __FUNCTION_NAME, p.X, p.Y, p.Z);
2350 Do not create over-limit
2352 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2353 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2354 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2355 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2356 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2357 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2358 throw InvalidPositionException("createBlock(): pos. over limit");
2360 v2s16 p2d(p.X, p.Z);
2363 This will create or load a sector if not found in memory.
2364 If block exists on disk, it will be loaded.
2366 NOTE: On old save formats, this will be slow, as it generates
2367 lighting on blocks for them.
2369 ServerMapSector *sector;
2371 sector = (ServerMapSector*)createSector(p2d);
2372 assert(sector->getId() == MAPSECTOR_SERVER);
2374 catch(InvalidPositionException &e)
2376 dstream<<"createBlock: createSector() failed"<<std::endl;
2380 NOTE: This should not be done, or at least the exception
2381 should not be passed on as std::exception, because it
2382 won't be catched at all.
2384 /*catch(std::exception &e)
2386 dstream<<"createBlock: createSector() failed: "
2387 <<e.what()<<std::endl;
2392 Try to get a block from the sector
2395 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2398 if(block->isDummy())
2403 block = sector->createBlankBlock(block_y);
2408 MapBlock * ServerMap::emergeBlock(
2410 bool only_from_disk,
2411 core::map<v3s16, MapBlock*> &changed_blocks,
2412 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2415 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
2417 p.X, p.Y, p.Z, only_from_disk);
2419 // This has to be redone or removed
2427 Do not generate over-limit
2429 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2430 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2431 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2432 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2433 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2434 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2435 throw InvalidPositionException("emergeBlock(): pos. over limit");
2437 v2s16 p2d(p.X, p.Z);
2440 This will create or load a sector if not found in memory.
2441 If block exists on disk, it will be loaded.
2443 ServerMapSector *sector;
2445 sector = createSector(p2d);
2446 //sector = emergeSector(p2d, changed_blocks);
2448 catch(InvalidPositionException &e)
2450 dstream<<"emergeBlock: createSector() failed: "
2451 <<e.what()<<std::endl;
2452 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2454 <<"You could try to delete it."<<std::endl;
2457 catch(VersionMismatchException &e)
2459 dstream<<"emergeBlock: createSector() failed: "
2460 <<e.what()<<std::endl;
2461 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2463 <<"You could try to delete it."<<std::endl;
2468 Try to get a block from the sector
2471 bool does_not_exist = false;
2472 bool lighting_expired = false;
2473 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2475 // If not found, try loading from disk
2478 block = loadBlock(p);
2484 does_not_exist = true;
2486 else if(block->isDummy() == true)
2488 does_not_exist = true;
2490 else if(block->getLightingExpired())
2492 lighting_expired = true;
2497 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2502 If block was not found on disk and not going to generate a
2503 new one, make sure there is a dummy block in place.
2505 if(only_from_disk && (does_not_exist || lighting_expired))
2507 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2511 // Create dummy block
2512 block = new MapBlock(this, p, true);
2514 // Add block to sector
2515 sector->insertBlock(block);
2521 //dstream<<"Not found on disk, generating."<<std::endl;
2523 //TimeTaker("emergeBlock() generate");
2525 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2528 If the block doesn't exist, generate the block.
2532 block = generateBlock(p, block, sector, changed_blocks,
2533 lighting_invalidated_blocks);
2536 if(lighting_expired)
2538 lighting_invalidated_blocks.insert(p, block);
2543 Initially update sunlight
2546 core::map<v3s16, bool> light_sources;
2547 bool black_air_left = false;
2548 bool bottom_invalid =
2549 block->propagateSunlight(light_sources, true,
2552 // If sunlight didn't reach everywhere and part of block is
2553 // above ground, lighting has to be properly updated
2554 //if(black_air_left && some_part_underground)
2557 lighting_invalidated_blocks[block->getPos()] = block;
2562 lighting_invalidated_blocks[block->getPos()] = block;
2571 s16 ServerMap::findGroundLevel(v2s16 p2d)
2575 Uh, just do something random...
2577 // Find existing map from top to down
2580 v3s16 p(p2d.X, max, p2d.Y);
2581 for(; p.Y>min; p.Y--)
2583 MapNode n = getNodeNoEx(p);
2584 if(n.d != CONTENT_IGNORE)
2589 // If this node is not air, go to plan b
2590 if(getNodeNoEx(p).d != CONTENT_AIR)
2592 // Search existing walkable and return it
2593 for(; p.Y>min; p.Y--)
2595 MapNode n = getNodeNoEx(p);
2596 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2605 Determine from map generator noise functions
2608 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2611 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2612 //return (s16)level;
2615 void ServerMap::createDirs(std::string path)
2617 if(fs::CreateAllDirs(path) == false)
2619 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2620 <<"\""<<path<<"\""<<std::endl;
2621 throw BaseException("ServerMap failed to create directory");
2625 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2631 snprintf(cc, 9, "%.4x%.4x",
2632 (unsigned int)pos.X&0xffff,
2633 (unsigned int)pos.Y&0xffff);
2635 return m_savedir + "/sectors/" + cc;
2637 snprintf(cc, 9, "%.3x/%.3x",
2638 (unsigned int)pos.X&0xfff,
2639 (unsigned int)pos.Y&0xfff);
2641 return m_savedir + "/sectors2/" + cc;
2647 v2s16 ServerMap::getSectorPos(std::string dirname)
2651 size_t spos = dirname.rfind('/') + 1;
2652 assert(spos != std::string::npos);
2653 if(dirname.size() - spos == 8)
2656 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2658 else if(dirname.size() - spos == 3)
2661 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2662 // Sign-extend the 12 bit values up to 16 bits...
2663 if(x&0x800) x|=0xF000;
2664 if(y&0x800) y|=0xF000;
2671 v2s16 pos((s16)x, (s16)y);
2675 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2677 v2s16 p2d = getSectorPos(sectordir);
2679 if(blockfile.size() != 4){
2680 throw InvalidFilenameException("Invalid block filename");
2683 int r = sscanf(blockfile.c_str(), "%4x", &y);
2685 throw InvalidFilenameException("Invalid block filename");
2686 return v3s16(p2d.X, y, p2d.Y);
2689 std::string ServerMap::getBlockFilename(v3s16 p)
2692 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2696 void ServerMap::save(bool only_changed)
2698 DSTACK(__FUNCTION_NAME);
2699 if(m_map_saving_enabled == false)
2701 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2705 if(only_changed == false)
2706 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2709 if(only_changed == false || m_map_metadata_changed)
2714 u32 sector_meta_count = 0;
2715 u32 block_count = 0;
2716 u32 block_count_all = 0; // Number of blocks in memory
2718 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2719 for(; i.atEnd() == false; i++)
2721 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2722 assert(sector->getId() == MAPSECTOR_SERVER);
2724 if(sector->differs_from_disk || only_changed == false)
2726 saveSectorMeta(sector);
2727 sector_meta_count++;
2729 core::list<MapBlock*> blocks;
2730 sector->getBlocks(blocks);
2731 core::list<MapBlock*>::Iterator j;
2732 for(j=blocks.begin(); j!=blocks.end(); j++)
2734 MapBlock *block = *j;
2738 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2739 || only_changed == false)
2744 /*dstream<<"ServerMap: Written block ("
2745 <<block->getPos().X<<","
2746 <<block->getPos().Y<<","
2747 <<block->getPos().Z<<")"
2754 Only print if something happened or saved whole map
2756 if(only_changed == false || sector_meta_count != 0
2757 || block_count != 0)
2759 dstream<<DTIME<<"ServerMap: Written: "
2760 <<sector_meta_count<<" sector metadata files, "
2761 <<block_count<<" block files"
2762 <<", "<<block_count_all<<" blocks in memory."
2767 void ServerMap::saveMapMeta()
2769 DSTACK(__FUNCTION_NAME);
2771 dstream<<"INFO: ServerMap::saveMapMeta(): "
2775 createDirs(m_savedir);
2777 std::string fullpath = m_savedir + "/map_meta.txt";
2778 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2779 if(os.good() == false)
2781 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2782 <<"could not open"<<fullpath<<std::endl;
2783 throw FileNotGoodException("Cannot open chunk metadata");
2787 params.setU64("seed", m_seed);
2789 params.writeLines(os);
2791 os<<"[end_of_params]\n";
2793 m_map_metadata_changed = false;
2796 void ServerMap::loadMapMeta()
2798 DSTACK(__FUNCTION_NAME);
2800 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2803 std::string fullpath = m_savedir + "/map_meta.txt";
2804 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2805 if(is.good() == false)
2807 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2808 <<"could not open"<<fullpath<<std::endl;
2809 throw FileNotGoodException("Cannot open map metadata");
2817 throw SerializationError
2818 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2820 std::getline(is, line);
2821 std::string trimmedline = trim(line);
2822 if(trimmedline == "[end_of_params]")
2824 params.parseConfigLine(line);
2827 m_seed = params.getU64("seed");
2829 dstream<<"INFO: ServerMap::loadMapMeta(): "
2834 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2836 DSTACK(__FUNCTION_NAME);
2837 // Format used for writing
2838 u8 version = SER_FMT_VER_HIGHEST;
2840 v2s16 pos = sector->getPos();
2841 std::string dir = getSectorDir(pos);
2844 std::string fullpath = dir + "/meta";
2845 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2846 if(o.good() == false)
2847 throw FileNotGoodException("Cannot open sector metafile");
2849 sector->serialize(o, version);
2851 sector->differs_from_disk = false;
2854 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2856 DSTACK(__FUNCTION_NAME);
2858 v2s16 p2d = getSectorPos(sectordir);
2860 ServerMapSector *sector = NULL;
2862 std::string fullpath = sectordir + "/meta";
2863 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2864 if(is.good() == false)
2866 // If the directory exists anyway, it probably is in some old
2867 // format. Just go ahead and create the sector.
2868 if(fs::PathExists(sectordir))
2870 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2871 <<fullpath<<" doesn't exist but directory does."
2872 <<" Continuing with a sector with no metadata."
2874 sector = new ServerMapSector(this, p2d);
2875 m_sectors.insert(p2d, sector);
2879 throw FileNotGoodException("Cannot open sector metafile");
2884 sector = ServerMapSector::deSerialize
2885 (is, this, p2d, m_sectors);
2887 saveSectorMeta(sector);
2890 sector->differs_from_disk = false;
2895 bool ServerMap::loadSectorMeta(v2s16 p2d)
2897 DSTACK(__FUNCTION_NAME);
2899 MapSector *sector = NULL;
2901 // The directory layout we're going to load from.
2902 // 1 - original sectors/xxxxzzzz/
2903 // 2 - new sectors2/xxx/zzz/
2904 // If we load from anything but the latest structure, we will
2905 // immediately save to the new one, and remove the old.
2907 std::string sectordir1 = getSectorDir(p2d, 1);
2908 std::string sectordir;
2909 if(fs::PathExists(sectordir1))
2911 sectordir = sectordir1;
2916 sectordir = getSectorDir(p2d, 2);
2920 sector = loadSectorMeta(sectordir, loadlayout != 2);
2922 catch(InvalidFilenameException &e)
2926 catch(FileNotGoodException &e)
2930 catch(std::exception &e)
2939 bool ServerMap::loadSectorFull(v2s16 p2d)
2941 DSTACK(__FUNCTION_NAME);
2943 MapSector *sector = NULL;
2945 // The directory layout we're going to load from.
2946 // 1 - original sectors/xxxxzzzz/
2947 // 2 - new sectors2/xxx/zzz/
2948 // If we load from anything but the latest structure, we will
2949 // immediately save to the new one, and remove the old.
2951 std::string sectordir1 = getSectorDir(p2d, 1);
2952 std::string sectordir;
2953 if(fs::PathExists(sectordir1))
2955 sectordir = sectordir1;
2960 sectordir = getSectorDir(p2d, 2);
2964 sector = loadSectorMeta(sectordir, loadlayout != 2);
2966 catch(InvalidFilenameException &e)
2970 catch(FileNotGoodException &e)
2974 catch(std::exception &e)
2982 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2984 std::vector<fs::DirListNode>::iterator i2;
2985 for(i2=list2.begin(); i2!=list2.end(); i2++)
2991 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2993 catch(InvalidFilenameException &e)
2995 // This catches unknown crap in directory
3001 dstream<<"Sector converted to new layout - deleting "<<
3002 sectordir1<<std::endl;
3003 fs::RecursiveDelete(sectordir1);
3010 void ServerMap::saveBlock(MapBlock *block)
3012 DSTACK(__FUNCTION_NAME);
3014 Dummy blocks are not written
3016 if(block->isDummy())
3018 /*v3s16 p = block->getPos();
3019 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3020 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3024 // Format used for writing
3025 u8 version = SER_FMT_VER_HIGHEST;
3027 v3s16 p3d = block->getPos();
3029 v2s16 p2d(p3d.X, p3d.Z);
3030 std::string sectordir = getSectorDir(p2d);
3032 createDirs(sectordir);
3034 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3035 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3036 if(o.good() == false)
3037 throw FileNotGoodException("Cannot open block data");
3040 [0] u8 serialization version
3043 o.write((char*)&version, 1);
3046 block->serialize(o, version);
3048 // Write extra data stored on disk
3049 block->serializeDiskExtra(o, version);
3051 // We just wrote it to the disk so clear modified flag
3052 block->resetModified();
3055 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3057 DSTACK(__FUNCTION_NAME);
3059 std::string fullpath = sectordir+"/"+blockfile;
3062 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3063 if(is.good() == false)
3064 throw FileNotGoodException("Cannot open block file");
3066 v3s16 p3d = getBlockPos(sectordir, blockfile);
3067 v2s16 p2d(p3d.X, p3d.Z);
3069 assert(sector->getPos() == p2d);
3071 u8 version = SER_FMT_VER_INVALID;
3072 is.read((char*)&version, 1);
3075 throw SerializationError("ServerMap::loadBlock(): Failed"
3076 " to read MapBlock version");
3078 /*u32 block_size = MapBlock::serializedLength(version);
3079 SharedBuffer<u8> data(block_size);
3080 is.read((char*)*data, block_size);*/
3082 // This will always return a sector because we're the server
3083 //MapSector *sector = emergeSector(p2d);
3085 MapBlock *block = NULL;
3086 bool created_new = false;
3087 block = sector->getBlockNoCreateNoEx(p3d.Y);
3090 block = sector->createBlankBlockNoInsert(p3d.Y);
3095 block->deSerialize(is, version);
3097 // Read extra data stored on disk
3098 block->deSerializeDiskExtra(is, version);
3100 // If it's a new block, insert it to the map
3102 sector->insertBlock(block);
3105 Save blocks loaded in old format in new format
3108 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3113 // We just loaded it from the disk, so it's up-to-date.
3114 block->resetModified();
3117 catch(SerializationError &e)
3119 dstream<<"WARNING: Invalid block data on disk "
3120 <<"fullpath="<<fullpath
3121 <<" (SerializationError). "
3122 <<"what()="<<e.what()
3124 //" Ignoring. A new one will be generated.
3127 // TODO: Backup file; name is in fullpath.
3131 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3133 DSTACK(__FUNCTION_NAME);
3135 v2s16 p2d(blockpos.X, blockpos.Z);
3137 // The directory layout we're going to load from.
3138 // 1 - original sectors/xxxxzzzz/
3139 // 2 - new sectors2/xxx/zzz/
3140 // If we load from anything but the latest structure, we will
3141 // immediately save to the new one, and remove the old.
3143 std::string sectordir1 = getSectorDir(p2d, 1);
3144 std::string sectordir;
3145 if(fs::PathExists(sectordir1))
3147 sectordir = sectordir1;
3152 sectordir = getSectorDir(p2d, 2);
3156 Make sure sector is loaded
3158 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3162 sector = loadSectorMeta(sectordir, loadlayout != 2);
3164 catch(InvalidFilenameException &e)
3168 catch(FileNotGoodException &e)
3172 catch(std::exception &e)
3179 Make sure file exists
3182 std::string blockfilename = getBlockFilename(blockpos);
3183 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3189 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3190 return getBlockNoCreateNoEx(blockpos);
3193 void ServerMap::PrintInfo(std::ostream &out)
3204 ClientMap::ClientMap(
3206 MapDrawControl &control,
3207 scene::ISceneNode* parent,
3208 scene::ISceneManager* mgr,
3212 scene::ISceneNode(parent, mgr, id),
3215 m_camera_position(0,0,0),
3216 m_camera_direction(0,0,1)
3218 m_camera_mutex.Init();
3219 assert(m_camera_mutex.IsInitialized());
3221 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3222 BS*1000000,BS*1000000,BS*1000000);
3225 ClientMap::~ClientMap()
3227 /*JMutexAutoLock lock(mesh_mutex);
3236 MapSector * ClientMap::emergeSector(v2s16 p2d)
3238 DSTACK(__FUNCTION_NAME);
3239 // Check that it doesn't exist already
3241 return getSectorNoGenerate(p2d);
3243 catch(InvalidPositionException &e)
3248 ClientMapSector *sector = new ClientMapSector(this, p2d);
3251 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3252 m_sectors.insert(p2d, sector);
3259 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3261 DSTACK(__FUNCTION_NAME);
3262 ClientMapSector *sector = NULL;
3264 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3266 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3270 sector = (ClientMapSector*)n->getValue();
3271 assert(sector->getId() == MAPSECTOR_CLIENT);
3275 sector = new ClientMapSector(this, p2d);
3277 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3278 m_sectors.insert(p2d, sector);
3282 sector->deSerialize(is);
3286 void ClientMap::OnRegisterSceneNode()
3290 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3291 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3294 ISceneNode::OnRegisterSceneNode();
3297 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3299 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3300 DSTACK(__FUNCTION_NAME);
3302 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3305 This is called two times per frame, reset on the non-transparent one
3307 if(pass == scene::ESNRP_SOLID)
3309 m_last_drawn_sectors.clear();
3313 Get time for measuring timeout.
3315 Measuring time is very useful for long delays when the
3316 machine is swapping a lot.
3318 int time1 = time(0);
3320 //u32 daynight_ratio = m_client->getDayNightRatio();
3322 m_camera_mutex.Lock();
3323 v3f camera_position = m_camera_position;
3324 v3f camera_direction = m_camera_direction;
3325 m_camera_mutex.Unlock();
3328 Get all blocks and draw all visible ones
3331 v3s16 cam_pos_nodes(
3332 camera_position.X / BS,
3333 camera_position.Y / BS,
3334 camera_position.Z / BS);
3336 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3338 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3339 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3341 // Take a fair amount as we will be dropping more out later
3343 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3344 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3345 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3347 p_nodes_max.X / MAP_BLOCKSIZE,
3348 p_nodes_max.Y / MAP_BLOCKSIZE,
3349 p_nodes_max.Z / MAP_BLOCKSIZE);
3351 u32 vertex_count = 0;
3353 // For limiting number of mesh updates per frame
3354 u32 mesh_update_count = 0;
3356 u32 blocks_would_have_drawn = 0;
3357 u32 blocks_drawn = 0;
3359 int timecheck_counter = 0;
3360 core::map<v2s16, MapSector*>::Iterator si;
3361 si = m_sectors.getIterator();
3362 for(; si.atEnd() == false; si++)
3365 timecheck_counter++;
3366 if(timecheck_counter > 50)
3368 timecheck_counter = 0;
3369 int time2 = time(0);
3370 if(time2 > time1 + 4)
3372 dstream<<"ClientMap::renderMap(): "
3373 "Rendering takes ages, returning."
3380 MapSector *sector = si.getNode()->getValue();
3381 v2s16 sp = sector->getPos();
3383 if(m_control.range_all == false)
3385 if(sp.X < p_blocks_min.X
3386 || sp.X > p_blocks_max.X
3387 || sp.Y < p_blocks_min.Z
3388 || sp.Y > p_blocks_max.Z)
3392 core::list< MapBlock * > sectorblocks;
3393 sector->getBlocks(sectorblocks);
3399 u32 sector_blocks_drawn = 0;
3401 core::list< MapBlock * >::Iterator i;
3402 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3404 MapBlock *block = *i;
3407 Compare block position to camera position, skip
3408 if not seen on display
3411 float range = 100000 * BS;
3412 if(m_control.range_all == false)
3413 range = m_control.wanted_range * BS;
3416 if(isBlockInSight(block->getPos(), camera_position,
3417 camera_direction, range, &d) == false)
3422 // This is ugly (spherical distance limit?)
3423 /*if(m_control.range_all == false &&
3424 d - 0.5*BS*MAP_BLOCKSIZE > range)
3429 Update expired mesh (used for day/night change)
3431 It doesn't work exactly like it should now with the
3432 tasked mesh update but whatever.
3435 bool mesh_expired = false;
3438 JMutexAutoLock lock(block->mesh_mutex);
3440 mesh_expired = block->getMeshExpired();
3442 // Mesh has not been expired and there is no mesh:
3443 // block has no content
3444 if(block->mesh == NULL && mesh_expired == false)
3448 f32 faraway = BS*50;
3449 //f32 faraway = m_control.wanted_range * BS;
3452 This has to be done with the mesh_mutex unlocked
3454 // Pretty random but this should work somewhat nicely
3455 if(mesh_expired && (
3456 (mesh_update_count < 3
3457 && (d < faraway || mesh_update_count < 2)
3460 (m_control.range_all && mesh_update_count < 20)
3463 /*if(mesh_expired && mesh_update_count < 6
3464 && (d < faraway || mesh_update_count < 3))*/
3466 mesh_update_count++;
3468 // Mesh has been expired: generate new mesh
3469 //block->updateMesh(daynight_ratio);
3470 m_client->addUpdateMeshTask(block->getPos());
3472 mesh_expired = false;
3477 Draw the faces of the block
3480 JMutexAutoLock lock(block->mesh_mutex);
3482 scene::SMesh *mesh = block->mesh;
3487 blocks_would_have_drawn++;
3488 if(blocks_drawn >= m_control.wanted_max_blocks
3489 && m_control.range_all == false
3490 && d > m_control.wanted_min_range * BS)
3494 sector_blocks_drawn++;
3496 u32 c = mesh->getMeshBufferCount();
3498 for(u32 i=0; i<c; i++)
3500 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3501 const video::SMaterial& material = buf->getMaterial();
3502 video::IMaterialRenderer* rnd =
3503 driver->getMaterialRenderer(material.MaterialType);
3504 bool transparent = (rnd && rnd->isTransparent());
3505 // Render transparent on transparent pass and likewise.
3506 if(transparent == is_transparent_pass)
3509 This *shouldn't* hurt too much because Irrlicht
3510 doesn't change opengl textures if the old
3511 material is set again.
3513 driver->setMaterial(buf->getMaterial());
3514 driver->drawMeshBuffer(buf);
3515 vertex_count += buf->getVertexCount();
3519 } // foreach sectorblocks
3521 if(sector_blocks_drawn != 0)
3523 m_last_drawn_sectors[sp] = true;
3527 m_control.blocks_drawn = blocks_drawn;
3528 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3530 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3531 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3534 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3535 core::map<v3s16, MapBlock*> *affected_blocks)
3537 bool changed = false;
3539 Add it to all blocks touching it
3542 v3s16(0,0,0), // this
3543 v3s16(0,0,1), // back
3544 v3s16(0,1,0), // top
3545 v3s16(1,0,0), // right
3546 v3s16(0,0,-1), // front
3547 v3s16(0,-1,0), // bottom
3548 v3s16(-1,0,0), // left
3550 for(u16 i=0; i<7; i++)
3552 v3s16 p2 = p + dirs[i];
3553 // Block position of neighbor (or requested) node
3554 v3s16 blockpos = getNodeBlockPos(p2);
3555 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3556 if(blockref == NULL)
3558 // Relative position of requested node
3559 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3560 if(blockref->setTempMod(relpos, mod))
3565 if(changed && affected_blocks!=NULL)
3567 for(u16 i=0; i<7; i++)
3569 v3s16 p2 = p + dirs[i];
3570 // Block position of neighbor (or requested) node
3571 v3s16 blockpos = getNodeBlockPos(p2);
3572 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3573 if(blockref == NULL)
3575 affected_blocks->insert(blockpos, blockref);
3581 bool ClientMap::clearTempMod(v3s16 p,
3582 core::map<v3s16, MapBlock*> *affected_blocks)
3584 bool changed = false;
3586 v3s16(0,0,0), // this
3587 v3s16(0,0,1), // back
3588 v3s16(0,1,0), // top
3589 v3s16(1,0,0), // right
3590 v3s16(0,0,-1), // front
3591 v3s16(0,-1,0), // bottom
3592 v3s16(-1,0,0), // left
3594 for(u16 i=0; i<7; i++)
3596 v3s16 p2 = p + dirs[i];
3597 // Block position of neighbor (or requested) node
3598 v3s16 blockpos = getNodeBlockPos(p2);
3599 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3600 if(blockref == NULL)
3602 // Relative position of requested node
3603 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3604 if(blockref->clearTempMod(relpos))
3609 if(changed && affected_blocks!=NULL)
3611 for(u16 i=0; i<7; i++)
3613 v3s16 p2 = p + dirs[i];
3614 // Block position of neighbor (or requested) node
3615 v3s16 blockpos = getNodeBlockPos(p2);
3616 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3617 if(blockref == NULL)
3619 affected_blocks->insert(blockpos, blockref);
3625 void ClientMap::expireMeshes(bool only_daynight_diffed)
3627 TimeTaker timer("expireMeshes()");
3629 core::map<v2s16, MapSector*>::Iterator si;
3630 si = m_sectors.getIterator();
3631 for(; si.atEnd() == false; si++)
3633 MapSector *sector = si.getNode()->getValue();
3635 core::list< MapBlock * > sectorblocks;
3636 sector->getBlocks(sectorblocks);
3638 core::list< MapBlock * >::Iterator i;
3639 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3641 MapBlock *block = *i;
3643 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3649 JMutexAutoLock lock(block->mesh_mutex);
3650 if(block->mesh != NULL)
3652 /*block->mesh->drop();
3653 block->mesh = NULL;*/
3654 block->setMeshExpired(true);
3661 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3663 assert(mapType() == MAPTYPE_CLIENT);
3666 v3s16 p = blockpos + v3s16(0,0,0);
3667 MapBlock *b = getBlockNoCreate(p);
3668 b->updateMesh(daynight_ratio);
3669 //b->setMeshExpired(true);
3671 catch(InvalidPositionException &e){}
3674 v3s16 p = blockpos + v3s16(-1,0,0);
3675 MapBlock *b = getBlockNoCreate(p);
3676 b->updateMesh(daynight_ratio);
3677 //b->setMeshExpired(true);
3679 catch(InvalidPositionException &e){}
3681 v3s16 p = blockpos + v3s16(0,-1,0);
3682 MapBlock *b = getBlockNoCreate(p);
3683 b->updateMesh(daynight_ratio);
3684 //b->setMeshExpired(true);
3686 catch(InvalidPositionException &e){}
3688 v3s16 p = blockpos + v3s16(0,0,-1);
3689 MapBlock *b = getBlockNoCreate(p);
3690 b->updateMesh(daynight_ratio);
3691 //b->setMeshExpired(true);
3693 catch(InvalidPositionException &e){}
3698 Update mesh of block in which the node is, and if the node is at the
3699 leading edge, update the appropriate leading blocks too.
3701 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3709 v3s16 blockposes[4];
3710 for(u32 i=0; i<4; i++)
3712 v3s16 np = nodepos + dirs[i];
3713 blockposes[i] = getNodeBlockPos(np);
3714 // Don't update mesh of block if it has been done already
3715 bool already_updated = false;
3716 for(u32 j=0; j<i; j++)
3718 if(blockposes[j] == blockposes[i])
3720 already_updated = true;
3727 MapBlock *b = getBlockNoCreate(blockposes[i]);
3728 b->updateMesh(daynight_ratio);
3733 void ClientMap::PrintInfo(std::ostream &out)
3744 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3749 MapVoxelManipulator::~MapVoxelManipulator()
3751 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3755 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3757 TimeTaker timer1("emerge", &emerge_time);
3759 // Units of these are MapBlocks
3760 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3761 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3763 VoxelArea block_area_nodes
3764 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3766 addArea(block_area_nodes);
3768 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3769 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3770 for(s32 x=p_min.X; x<=p_max.X; x++)
3773 core::map<v3s16, bool>::Node *n;
3774 n = m_loaded_blocks.find(p);
3778 bool block_data_inexistent = false;
3781 TimeTaker timer1("emerge load", &emerge_load_time);
3783 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3784 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3787 dstream<<std::endl;*/
3789 MapBlock *block = m_map->getBlockNoCreate(p);
3790 if(block->isDummy())
3791 block_data_inexistent = true;
3793 block->copyTo(*this);
3795 catch(InvalidPositionException &e)
3797 block_data_inexistent = true;
3800 if(block_data_inexistent)
3802 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3803 // Fill with VOXELFLAG_INEXISTENT
3804 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3805 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3807 s32 i = m_area.index(a.MinEdge.X,y,z);
3808 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3812 m_loaded_blocks.insert(p, !block_data_inexistent);
3815 //dstream<<"emerge done"<<std::endl;
3819 SUGG: Add an option to only update eg. water and air nodes.
3820 This will make it interfere less with important stuff if
3823 void MapVoxelManipulator::blitBack
3824 (core::map<v3s16, MapBlock*> & modified_blocks)
3826 if(m_area.getExtent() == v3s16(0,0,0))
3829 //TimeTaker timer1("blitBack");
3831 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3832 <<m_loaded_blocks.size()<<std::endl;*/
3835 Initialize block cache
3837 v3s16 blockpos_last;
3838 MapBlock *block = NULL;
3839 bool block_checked_in_modified = false;
3841 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3842 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3843 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3847 u8 f = m_flags[m_area.index(p)];
3848 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3851 MapNode &n = m_data[m_area.index(p)];
3853 v3s16 blockpos = getNodeBlockPos(p);
3858 if(block == NULL || blockpos != blockpos_last){
3859 block = m_map->getBlockNoCreate(blockpos);
3860 blockpos_last = blockpos;
3861 block_checked_in_modified = false;
3864 // Calculate relative position in block
3865 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3867 // Don't continue if nothing has changed here
3868 if(block->getNode(relpos) == n)
3871 //m_map->setNode(m_area.MinEdge + p, n);
3872 block->setNode(relpos, n);
3875 Make sure block is in modified_blocks
3877 if(block_checked_in_modified == false)
3879 modified_blocks[blockpos] = block;
3880 block_checked_in_modified = true;
3883 catch(InvalidPositionException &e)
3889 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3890 MapVoxelManipulator(map),
3891 m_create_area(false)
3895 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3899 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3901 // Just create the area so that it can be pointed to
3902 VoxelManipulator::emerge(a, caller_id);
3905 void ManualMapVoxelManipulator::initialEmerge(
3906 v3s16 blockpos_min, v3s16 blockpos_max)
3908 TimeTaker timer1("initialEmerge", &emerge_time);
3910 // Units of these are MapBlocks
3911 v3s16 p_min = blockpos_min;
3912 v3s16 p_max = blockpos_max;
3914 VoxelArea block_area_nodes
3915 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3917 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3920 dstream<<"initialEmerge: area: ";
3921 block_area_nodes.print(dstream);
3922 dstream<<" ("<<size_MB<<"MB)";
3926 addArea(block_area_nodes);
3928 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3929 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3930 for(s32 x=p_min.X; x<=p_max.X; x++)
3933 core::map<v3s16, bool>::Node *n;
3934 n = m_loaded_blocks.find(p);
3938 bool block_data_inexistent = false;
3941 TimeTaker timer1("emerge load", &emerge_load_time);
3943 MapBlock *block = m_map->getBlockNoCreate(p);
3944 if(block->isDummy())
3945 block_data_inexistent = true;
3947 block->copyTo(*this);
3949 catch(InvalidPositionException &e)
3951 block_data_inexistent = true;
3954 if(block_data_inexistent)
3957 Mark area inexistent
3959 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3960 // Fill with VOXELFLAG_INEXISTENT
3961 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3962 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3964 s32 i = m_area.index(a.MinEdge.X,y,z);
3965 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3969 m_loaded_blocks.insert(p, !block_data_inexistent);
3973 void ManualMapVoxelManipulator::blitBackAll(
3974 core::map<v3s16, MapBlock*> * modified_blocks)
3976 if(m_area.getExtent() == v3s16(0,0,0))
3980 Copy data of all blocks
3982 for(core::map<v3s16, bool>::Iterator
3983 i = m_loaded_blocks.getIterator();
3984 i.atEnd() == false; i++)
3986 bool existed = i.getNode()->getValue();
3987 if(existed == false)
3989 v3s16 p = i.getNode()->getKey();
3990 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3993 dstream<<"WARNING: "<<__FUNCTION_NAME
3994 <<": got NULL block "
3995 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4000 block->copyFrom(*this);
4003 modified_blocks->insert(p, block);