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"
31 #include "serverobject.h"
32 #include "content_mapnode.h"
34 #include "nodemetadata.h"
40 SQLite format specification:
41 - Initially only replaces sectors/ and sectors2/
48 Map::Map(std::ostream &dout):
52 /*m_sector_mutex.Init();
53 assert(m_sector_mutex.IsInitialized());*/
61 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
62 for(; i.atEnd() == false; i++)
64 MapSector *sector = i.getNode()->getValue();
69 void Map::addEventReceiver(MapEventReceiver *event_receiver)
71 m_event_receivers.insert(event_receiver, false);
74 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
76 if(m_event_receivers.find(event_receiver) == NULL)
78 m_event_receivers.remove(event_receiver);
81 void Map::dispatchEvent(MapEditEvent *event)
83 for(core::map<MapEventReceiver*, bool>::Iterator
84 i = m_event_receivers.getIterator();
85 i.atEnd()==false; i++)
87 MapEventReceiver* event_receiver = i.getNode()->getKey();
88 event_receiver->onMapEditEvent(event);
92 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
94 if(m_sector_cache != NULL && p == m_sector_cache_p){
95 MapSector * sector = m_sector_cache;
99 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
104 MapSector *sector = n->getValue();
106 // Cache the last result
107 m_sector_cache_p = p;
108 m_sector_cache = sector;
113 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
115 return getSectorNoGenerateNoExNoLock(p);
118 MapSector * Map::getSectorNoGenerate(v2s16 p)
120 MapSector *sector = getSectorNoGenerateNoEx(p);
122 throw InvalidPositionException();
127 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
129 v2s16 p2d(p3d.X, p3d.Z);
130 MapSector * sector = getSectorNoGenerateNoEx(p2d);
133 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
137 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
139 MapBlock *block = getBlockNoCreateNoEx(p3d);
141 throw InvalidPositionException();
145 bool Map::isNodeUnderground(v3s16 p)
147 v3s16 blockpos = getNodeBlockPos(p);
149 MapBlock * block = getBlockNoCreate(blockpos);
150 return block->getIsUnderground();
152 catch(InvalidPositionException &e)
158 bool Map::isValidPosition(v3s16 p)
160 v3s16 blockpos = getNodeBlockPos(p);
161 MapBlock *block = getBlockNoCreate(blockpos);
162 return (block != NULL);
165 // Returns a CONTENT_IGNORE node if not found
166 MapNode Map::getNodeNoEx(v3s16 p)
168 v3s16 blockpos = getNodeBlockPos(p);
169 MapBlock *block = getBlockNoCreateNoEx(blockpos);
171 return MapNode(CONTENT_IGNORE);
172 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
173 return block->getNodeNoCheck(relpos);
176 // throws InvalidPositionException if not found
177 MapNode Map::getNode(v3s16 p)
179 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock *block = getBlockNoCreateNoEx(blockpos);
182 throw InvalidPositionException();
183 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
184 return block->getNodeNoCheck(relpos);
187 // throws InvalidPositionException if not found
188 void Map::setNode(v3s16 p, MapNode & n)
190 v3s16 blockpos = getNodeBlockPos(p);
191 MapBlock *block = getBlockNoCreate(blockpos);
192 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
193 block->setNodeNoCheck(relpos, n);
198 Goes recursively through the neighbours of the node.
200 Alters only transparent nodes.
202 If the lighting of the neighbour is lower than the lighting of
203 the node was (before changing it to 0 at the step before), the
204 lighting of the neighbour is set to 0 and then the same stuff
205 repeats for the neighbour.
207 The ending nodes of the routine are stored in light_sources.
208 This is useful when a light is removed. In such case, this
209 routine can be called for the light node and then again for
210 light_sources to re-light the area without the removed light.
212 values of from_nodes are lighting values.
214 void Map::unspreadLight(enum LightBank bank,
215 core::map<v3s16, u8> & from_nodes,
216 core::map<v3s16, bool> & light_sources,
217 core::map<v3s16, MapBlock*> & modified_blocks)
220 v3s16(0,0,1), // back
222 v3s16(1,0,0), // right
223 v3s16(0,0,-1), // front
224 v3s16(0,-1,0), // bottom
225 v3s16(-1,0,0), // left
228 if(from_nodes.size() == 0)
231 u32 blockchangecount = 0;
233 core::map<v3s16, u8> unlighted_nodes;
234 core::map<v3s16, u8>::Iterator j;
235 j = from_nodes.getIterator();
238 Initialize block cache
241 MapBlock *block = NULL;
242 // Cache this a bit, too
243 bool block_checked_in_modified = false;
245 for(; j.atEnd() == false; j++)
247 v3s16 pos = j.getNode()->getKey();
248 v3s16 blockpos = getNodeBlockPos(pos);
250 // Only fetch a new block if the block position has changed
252 if(block == NULL || blockpos != blockpos_last){
253 block = getBlockNoCreate(blockpos);
254 blockpos_last = blockpos;
256 block_checked_in_modified = false;
260 catch(InvalidPositionException &e)
268 // Calculate relative position in block
269 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
271 // Get node straight from the block
272 MapNode n = block->getNode(relpos);
274 u8 oldlight = j.getNode()->getValue();
276 // Loop through 6 neighbors
277 for(u16 i=0; i<6; i++)
279 // Get the position of the neighbor node
280 v3s16 n2pos = pos + dirs[i];
282 // Get the block where the node is located
283 v3s16 blockpos = getNodeBlockPos(n2pos);
287 // Only fetch a new block if the block position has changed
289 if(block == NULL || blockpos != blockpos_last){
290 block = getBlockNoCreate(blockpos);
291 blockpos_last = blockpos;
293 block_checked_in_modified = false;
297 catch(InvalidPositionException &e)
302 // Calculate relative position in block
303 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
304 // Get node straight from the block
305 MapNode n2 = block->getNode(relpos);
307 bool changed = false;
309 //TODO: Optimize output by optimizing light_sources?
312 If the neighbor is dimmer than what was specified
313 as oldlight (the light of the previous node)
315 if(n2.getLight(bank) < oldlight)
318 And the neighbor is transparent and it has some light
320 if(n2.light_propagates() && n2.getLight(bank) != 0)
323 Set light to 0 and add to queue
326 u8 current_light = n2.getLight(bank);
327 n2.setLight(bank, 0);
328 block->setNode(relpos, n2);
330 unlighted_nodes.insert(n2pos, current_light);
334 Remove from light_sources if it is there
335 NOTE: This doesn't happen nearly at all
337 /*if(light_sources.find(n2pos))
339 std::cout<<"Removed from light_sources"<<std::endl;
340 light_sources.remove(n2pos);
345 if(light_sources.find(n2pos) != NULL)
346 light_sources.remove(n2pos);*/
349 light_sources.insert(n2pos, true);
352 // Add to modified_blocks
353 if(changed == true && block_checked_in_modified == false)
355 // If the block is not found in modified_blocks, add.
356 if(modified_blocks.find(blockpos) == NULL)
358 modified_blocks.insert(blockpos, block);
360 block_checked_in_modified = true;
363 catch(InvalidPositionException &e)
370 /*dstream<<"unspreadLight(): Changed block "
371 <<blockchangecount<<" times"
372 <<" for "<<from_nodes.size()<<" nodes"
375 if(unlighted_nodes.size() > 0)
376 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
380 A single-node wrapper of the above
382 void Map::unLightNeighbors(enum LightBank bank,
383 v3s16 pos, u8 lightwas,
384 core::map<v3s16, bool> & light_sources,
385 core::map<v3s16, MapBlock*> & modified_blocks)
387 core::map<v3s16, u8> from_nodes;
388 from_nodes.insert(pos, lightwas);
390 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
394 Lights neighbors of from_nodes, collects all them and then
397 void Map::spreadLight(enum LightBank bank,
398 core::map<v3s16, bool> & from_nodes,
399 core::map<v3s16, MapBlock*> & modified_blocks)
401 const v3s16 dirs[6] = {
402 v3s16(0,0,1), // back
404 v3s16(1,0,0), // right
405 v3s16(0,0,-1), // front
406 v3s16(0,-1,0), // bottom
407 v3s16(-1,0,0), // left
410 if(from_nodes.size() == 0)
413 u32 blockchangecount = 0;
415 core::map<v3s16, bool> lighted_nodes;
416 core::map<v3s16, bool>::Iterator j;
417 j = from_nodes.getIterator();
420 Initialize block cache
423 MapBlock *block = NULL;
424 // Cache this a bit, too
425 bool block_checked_in_modified = false;
427 for(; j.atEnd() == false; j++)
428 //for(; j != from_nodes.end(); j++)
430 v3s16 pos = j.getNode()->getKey();
432 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
433 v3s16 blockpos = getNodeBlockPos(pos);
435 // Only fetch a new block if the block position has changed
437 if(block == NULL || blockpos != blockpos_last){
438 block = getBlockNoCreate(blockpos);
439 blockpos_last = blockpos;
441 block_checked_in_modified = false;
445 catch(InvalidPositionException &e)
453 // Calculate relative position in block
454 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
456 // Get node straight from the block
457 MapNode n = block->getNode(relpos);
459 u8 oldlight = n.getLight(bank);
460 u8 newlight = diminish_light(oldlight);
462 // Loop through 6 neighbors
463 for(u16 i=0; i<6; i++){
464 // Get the position of the neighbor node
465 v3s16 n2pos = pos + dirs[i];
467 // Get the block where the node is located
468 v3s16 blockpos = getNodeBlockPos(n2pos);
472 // Only fetch a new block if the block position has changed
474 if(block == NULL || blockpos != blockpos_last){
475 block = getBlockNoCreate(blockpos);
476 blockpos_last = blockpos;
478 block_checked_in_modified = false;
482 catch(InvalidPositionException &e)
487 // Calculate relative position in block
488 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
489 // Get node straight from the block
490 MapNode n2 = block->getNode(relpos);
492 bool changed = false;
494 If the neighbor is brighter than the current node,
495 add to list (it will light up this node on its turn)
497 if(n2.getLight(bank) > undiminish_light(oldlight))
499 lighted_nodes.insert(n2pos, true);
500 //lighted_nodes.push_back(n2pos);
504 If the neighbor is dimmer than how much light this node
505 would spread on it, add to list
507 if(n2.getLight(bank) < newlight)
509 if(n2.light_propagates())
511 n2.setLight(bank, newlight);
512 block->setNode(relpos, n2);
513 lighted_nodes.insert(n2pos, true);
514 //lighted_nodes.push_back(n2pos);
519 // Add to modified_blocks
520 if(changed == true && block_checked_in_modified == false)
522 // If the block is not found in modified_blocks, add.
523 if(modified_blocks.find(blockpos) == NULL)
525 modified_blocks.insert(blockpos, block);
527 block_checked_in_modified = true;
530 catch(InvalidPositionException &e)
537 /*dstream<<"spreadLight(): Changed block "
538 <<blockchangecount<<" times"
539 <<" for "<<from_nodes.size()<<" nodes"
542 if(lighted_nodes.size() > 0)
543 spreadLight(bank, lighted_nodes, modified_blocks);
547 A single-node source variation of the above.
549 void Map::lightNeighbors(enum LightBank bank,
551 core::map<v3s16, MapBlock*> & modified_blocks)
553 core::map<v3s16, bool> from_nodes;
554 from_nodes.insert(pos, true);
555 spreadLight(bank, from_nodes, modified_blocks);
558 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
561 v3s16(0,0,1), // back
563 v3s16(1,0,0), // right
564 v3s16(0,0,-1), // front
565 v3s16(0,-1,0), // bottom
566 v3s16(-1,0,0), // left
569 u8 brightest_light = 0;
570 v3s16 brightest_pos(0,0,0);
571 bool found_something = false;
573 // Loop through 6 neighbors
574 for(u16 i=0; i<6; i++){
575 // Get the position of the neighbor node
576 v3s16 n2pos = p + dirs[i];
581 catch(InvalidPositionException &e)
585 if(n2.getLight(bank) > brightest_light || found_something == false){
586 brightest_light = n2.getLight(bank);
587 brightest_pos = n2pos;
588 found_something = true;
592 if(found_something == false)
593 throw InvalidPositionException();
595 return brightest_pos;
599 Propagates sunlight down from a node.
600 Starting point gets sunlight.
602 Returns the lowest y value of where the sunlight went.
604 Mud is turned into grass in where the sunlight stops.
606 s16 Map::propagateSunlight(v3s16 start,
607 core::map<v3s16, MapBlock*> & modified_blocks)
612 v3s16 pos(start.X, y, start.Z);
614 v3s16 blockpos = getNodeBlockPos(pos);
617 block = getBlockNoCreate(blockpos);
619 catch(InvalidPositionException &e)
624 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
625 MapNode n = block->getNode(relpos);
627 if(n.sunlight_propagates())
629 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
630 block->setNode(relpos, n);
632 modified_blocks.insert(blockpos, block);
636 /*// Turn mud into grass
637 if(n.d == CONTENT_MUD)
640 block->setNode(relpos, n);
641 modified_blocks.insert(blockpos, block);
644 // Sunlight goes no further
651 void Map::updateLighting(enum LightBank bank,
652 core::map<v3s16, MapBlock*> & a_blocks,
653 core::map<v3s16, MapBlock*> & modified_blocks)
655 /*m_dout<<DTIME<<"Map::updateLighting(): "
656 <<a_blocks.size()<<" blocks."<<std::endl;*/
658 //TimeTaker timer("updateLighting");
662 //u32 count_was = modified_blocks.size();
664 core::map<v3s16, MapBlock*> blocks_to_update;
666 core::map<v3s16, bool> light_sources;
668 core::map<v3s16, u8> unlight_from;
670 core::map<v3s16, MapBlock*>::Iterator i;
671 i = a_blocks.getIterator();
672 for(; i.atEnd() == false; i++)
674 MapBlock *block = i.getNode()->getValue();
678 // Don't bother with dummy blocks.
682 v3s16 pos = block->getPos();
683 modified_blocks.insert(pos, block);
685 blocks_to_update.insert(pos, block);
688 Clear all light from block
690 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
691 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
692 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
697 MapNode n = block->getNode(v3s16(x,y,z));
698 u8 oldlight = n.getLight(bank);
700 block->setNode(v3s16(x,y,z), n);
702 // Collect borders for unlighting
703 if(x==0 || x == MAP_BLOCKSIZE-1
704 || y==0 || y == MAP_BLOCKSIZE-1
705 || z==0 || z == MAP_BLOCKSIZE-1)
707 v3s16 p_map = p + v3s16(
710 MAP_BLOCKSIZE*pos.Z);
711 unlight_from.insert(p_map, oldlight);
714 catch(InvalidPositionException &e)
717 This would happen when dealing with a
721 dstream<<"updateLighting(): InvalidPositionException"
726 if(bank == LIGHTBANK_DAY)
728 bool bottom_valid = block->propagateSunlight(light_sources);
730 // If bottom is valid, we're done.
734 else if(bank == LIGHTBANK_NIGHT)
736 // For night lighting, sunlight is not propagated
741 // Invalid lighting bank
745 /*dstream<<"Bottom for sunlight-propagated block ("
746 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
749 // Bottom sunlight is not valid; get the block and loop to it
753 block = getBlockNoCreate(pos);
755 catch(InvalidPositionException &e)
765 TimeTaker timer("unspreadLight");
766 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
771 u32 diff = modified_blocks.size() - count_was;
772 count_was = modified_blocks.size();
773 dstream<<"unspreadLight modified "<<diff<<std::endl;
777 TimeTaker timer("spreadLight");
778 spreadLight(bank, light_sources, modified_blocks);
783 u32 diff = modified_blocks.size() - count_was;
784 count_was = modified_blocks.size();
785 dstream<<"spreadLight modified "<<diff<<std::endl;
790 //MapVoxelManipulator vmanip(this);
792 // Make a manual voxel manipulator and load all the blocks
793 // that touch the requested blocks
794 ManualMapVoxelManipulator vmanip(this);
795 core::map<v3s16, MapBlock*>::Iterator i;
796 i = blocks_to_update.getIterator();
797 for(; i.atEnd() == false; i++)
799 MapBlock *block = i.getNode()->getValue();
800 v3s16 p = block->getPos();
802 // Add all surrounding blocks
803 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
806 Add all surrounding blocks that have up-to-date lighting
807 NOTE: This doesn't quite do the job (not everything
808 appropriate is lighted)
810 /*for(s16 z=-1; z<=1; z++)
811 for(s16 y=-1; y<=1; y++)
812 for(s16 x=-1; x<=1; x++)
815 MapBlock *block = getBlockNoCreateNoEx(p);
820 if(block->getLightingExpired())
822 vmanip.initialEmerge(p, p);
825 // Lighting of block will be updated completely
826 block->setLightingExpired(false);
830 //TimeTaker timer("unSpreadLight");
831 vmanip.unspreadLight(bank, unlight_from, light_sources);
834 //TimeTaker timer("spreadLight");
835 vmanip.spreadLight(bank, light_sources);
838 //TimeTaker timer("blitBack");
839 vmanip.blitBack(modified_blocks);
841 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
845 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
848 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
849 core::map<v3s16, MapBlock*> & modified_blocks)
851 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
852 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
855 Update information about whether day and night light differ
857 for(core::map<v3s16, MapBlock*>::Iterator
858 i = modified_blocks.getIterator();
859 i.atEnd() == false; i++)
861 MapBlock *block = i.getNode()->getValue();
862 block->updateDayNightDiff();
868 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
869 core::map<v3s16, MapBlock*> &modified_blocks)
872 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
873 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
876 From this node to nodes underneath:
877 If lighting is sunlight (1.0), unlight neighbours and
882 v3s16 toppos = p + v3s16(0,1,0);
883 v3s16 bottompos = p + v3s16(0,-1,0);
885 bool node_under_sunlight = true;
886 core::map<v3s16, bool> light_sources;
889 If there is a node at top and it doesn't have sunlight,
890 there has not been any sunlight going down.
892 Otherwise there probably is.
895 MapNode topnode = getNode(toppos);
897 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
898 node_under_sunlight = false;
900 catch(InvalidPositionException &e)
906 If the new node is solid and there is grass below, change it to mud
908 if(content_features(n.d).walkable == true)
911 MapNode bottomnode = getNode(bottompos);
913 if(bottomnode.d == CONTENT_GRASS
914 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
916 bottomnode.d = CONTENT_MUD;
917 setNode(bottompos, bottomnode);
920 catch(InvalidPositionException &e)
928 If the new node is mud and it is under sunlight, change it
931 if(n.d == CONTENT_MUD && node_under_sunlight)
938 Remove all light that has come out of this node
941 enum LightBank banks[] =
946 for(s32 i=0; i<2; i++)
948 enum LightBank bank = banks[i];
950 u8 lightwas = getNode(p).getLight(bank);
952 // Add the block of the added node to modified_blocks
953 v3s16 blockpos = getNodeBlockPos(p);
954 MapBlock * block = getBlockNoCreate(blockpos);
955 assert(block != NULL);
956 modified_blocks.insert(blockpos, block);
958 assert(isValidPosition(p));
960 // Unlight neighbours of node.
961 // This means setting light of all consequent dimmer nodes
963 // This also collects the nodes at the border which will spread
964 // light again into this.
965 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
971 If node lets sunlight through and is under sunlight, it has
974 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
976 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
980 Set the node on the map
989 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
992 NodeMetadata *meta = meta_proto->clone();
993 setNodeMetadata(p, meta);
997 If node is under sunlight and doesn't let sunlight through,
998 take all sunlighted nodes under it and clear light from them
999 and from where the light has been spread.
1000 TODO: This could be optimized by mass-unlighting instead
1003 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
1007 //m_dout<<DTIME<<"y="<<y<<std::endl;
1008 v3s16 n2pos(p.X, y, p.Z);
1012 n2 = getNode(n2pos);
1014 catch(InvalidPositionException &e)
1019 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1021 unLightNeighbors(LIGHTBANK_DAY,
1022 n2pos, n2.getLight(LIGHTBANK_DAY),
1023 light_sources, modified_blocks);
1024 n2.setLight(LIGHTBANK_DAY, 0);
1032 for(s32 i=0; i<2; i++)
1034 enum LightBank bank = banks[i];
1037 Spread light from all nodes that might be capable of doing so
1039 spreadLight(bank, light_sources, modified_blocks);
1043 Update information about whether day and night light differ
1045 for(core::map<v3s16, MapBlock*>::Iterator
1046 i = modified_blocks.getIterator();
1047 i.atEnd() == false; i++)
1049 MapBlock *block = i.getNode()->getValue();
1050 block->updateDayNightDiff();
1054 Add neighboring liquid nodes and the node itself if it is
1055 liquid (=water node was added) to transform queue.
1058 v3s16(0,0,0), // self
1059 v3s16(0,0,1), // back
1060 v3s16(0,1,0), // top
1061 v3s16(1,0,0), // right
1062 v3s16(0,0,-1), // front
1063 v3s16(0,-1,0), // bottom
1064 v3s16(-1,0,0), // left
1066 for(u16 i=0; i<7; i++)
1071 v3s16 p2 = p + dirs[i];
1073 MapNode n2 = getNode(p2);
1074 if(content_liquid(n2.d))
1076 m_transforming_liquid.push_back(p2);
1079 }catch(InvalidPositionException &e)
1087 void Map::removeNodeAndUpdate(v3s16 p,
1088 core::map<v3s16, MapBlock*> &modified_blocks)
1090 /*PrintInfo(m_dout);
1091 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1092 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1094 bool node_under_sunlight = true;
1096 v3s16 toppos = p + v3s16(0,1,0);
1098 // Node will be replaced with this
1099 u8 replace_material = CONTENT_AIR;
1102 If there is a node at top and it doesn't have sunlight,
1103 there will be no sunlight going down.
1106 MapNode topnode = getNode(toppos);
1108 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1109 node_under_sunlight = false;
1111 catch(InvalidPositionException &e)
1115 core::map<v3s16, bool> light_sources;
1117 enum LightBank banks[] =
1122 for(s32 i=0; i<2; i++)
1124 enum LightBank bank = banks[i];
1127 Unlight neighbors (in case the node is a light source)
1129 unLightNeighbors(bank, p,
1130 getNode(p).getLight(bank),
1131 light_sources, modified_blocks);
1135 Remove node metadata
1138 removeNodeMetadata(p);
1142 This also clears the lighting.
1146 n.d = replace_material;
1149 for(s32 i=0; i<2; i++)
1151 enum LightBank bank = banks[i];
1154 Recalculate lighting
1156 spreadLight(bank, light_sources, modified_blocks);
1159 // Add the block of the removed node to modified_blocks
1160 v3s16 blockpos = getNodeBlockPos(p);
1161 MapBlock * block = getBlockNoCreate(blockpos);
1162 assert(block != NULL);
1163 modified_blocks.insert(blockpos, block);
1166 If the removed node was under sunlight, propagate the
1167 sunlight down from it and then light all neighbors
1168 of the propagated blocks.
1170 if(node_under_sunlight)
1172 s16 ybottom = propagateSunlight(p, modified_blocks);
1173 /*m_dout<<DTIME<<"Node was under sunlight. "
1174 "Propagating sunlight";
1175 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1177 for(; y >= ybottom; y--)
1179 v3s16 p2(p.X, y, p.Z);
1180 /*m_dout<<DTIME<<"lighting neighbors of node ("
1181 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1183 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1188 // Set the lighting of this node to 0
1189 // TODO: Is this needed? Lighting is cleared up there already.
1191 MapNode n = getNode(p);
1192 n.setLight(LIGHTBANK_DAY, 0);
1195 catch(InvalidPositionException &e)
1201 for(s32 i=0; i<2; i++)
1203 enum LightBank bank = banks[i];
1205 // Get the brightest neighbour node and propagate light from it
1206 v3s16 n2p = getBrightestNeighbour(bank, p);
1208 MapNode n2 = getNode(n2p);
1209 lightNeighbors(bank, n2p, modified_blocks);
1211 catch(InvalidPositionException &e)
1217 Update information about whether day and night light differ
1219 for(core::map<v3s16, MapBlock*>::Iterator
1220 i = modified_blocks.getIterator();
1221 i.atEnd() == false; i++)
1223 MapBlock *block = i.getNode()->getValue();
1224 block->updateDayNightDiff();
1228 Add neighboring liquid nodes to transform queue.
1231 v3s16(0,0,1), // back
1232 v3s16(0,1,0), // top
1233 v3s16(1,0,0), // right
1234 v3s16(0,0,-1), // front
1235 v3s16(0,-1,0), // bottom
1236 v3s16(-1,0,0), // left
1238 for(u16 i=0; i<6; i++)
1243 v3s16 p2 = p + dirs[i];
1245 MapNode n2 = getNode(p2);
1246 if(content_liquid(n2.d))
1248 m_transforming_liquid.push_back(p2);
1251 }catch(InvalidPositionException &e)
1257 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1260 event.type = MEET_ADDNODE;
1264 bool succeeded = true;
1266 core::map<v3s16, MapBlock*> modified_blocks;
1267 addNodeAndUpdate(p, n, modified_blocks);
1269 // Copy modified_blocks to event
1270 for(core::map<v3s16, MapBlock*>::Iterator
1271 i = modified_blocks.getIterator();
1272 i.atEnd()==false; i++)
1274 event.modified_blocks.insert(i.getNode()->getKey(), false);
1277 catch(InvalidPositionException &e){
1281 dispatchEvent(&event);
1286 bool Map::removeNodeWithEvent(v3s16 p)
1289 event.type = MEET_REMOVENODE;
1292 bool succeeded = true;
1294 core::map<v3s16, MapBlock*> modified_blocks;
1295 removeNodeAndUpdate(p, modified_blocks);
1297 // Copy modified_blocks to event
1298 for(core::map<v3s16, MapBlock*>::Iterator
1299 i = modified_blocks.getIterator();
1300 i.atEnd()==false; i++)
1302 event.modified_blocks.insert(i.getNode()->getKey(), false);
1305 catch(InvalidPositionException &e){
1309 dispatchEvent(&event);
1314 bool Map::dayNightDiffed(v3s16 blockpos)
1317 v3s16 p = blockpos + v3s16(0,0,0);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1325 v3s16 p = blockpos + v3s16(-1,0,0);
1326 MapBlock *b = getBlockNoCreate(p);
1327 if(b->dayNightDiffed())
1330 catch(InvalidPositionException &e){}
1332 v3s16 p = blockpos + v3s16(0,-1,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1339 v3s16 p = blockpos + v3s16(0,0,-1);
1340 MapBlock *b = getBlockNoCreate(p);
1341 if(b->dayNightDiffed())
1344 catch(InvalidPositionException &e){}
1347 v3s16 p = blockpos + v3s16(1,0,0);
1348 MapBlock *b = getBlockNoCreate(p);
1349 if(b->dayNightDiffed())
1352 catch(InvalidPositionException &e){}
1354 v3s16 p = blockpos + v3s16(0,1,0);
1355 MapBlock *b = getBlockNoCreate(p);
1356 if(b->dayNightDiffed())
1359 catch(InvalidPositionException &e){}
1361 v3s16 p = blockpos + v3s16(0,0,1);
1362 MapBlock *b = getBlockNoCreate(p);
1363 if(b->dayNightDiffed())
1366 catch(InvalidPositionException &e){}
1372 Updates usage timers
1374 void Map::timerUpdate(float dtime)
1376 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1378 core::map<v2s16, MapSector*>::Iterator si;
1380 si = m_sectors.getIterator();
1381 for(; si.atEnd() == false; si++)
1383 MapSector *sector = si.getNode()->getValue();
1385 core::list<MapBlock*> blocks;
1386 sector->getBlocks(blocks);
1387 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1388 i != blocks.end(); i++)
1390 (*i)->incrementUsageTimer(dtime);
1395 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1397 core::list<v2s16>::Iterator j;
1398 for(j=list.begin(); j!=list.end(); j++)
1400 MapSector *sector = m_sectors[*j];
1403 sector->deleteBlocks();
1408 If sector is in sector cache, remove it from there
1410 if(m_sector_cache == sector)
1412 m_sector_cache = NULL;
1415 Remove from map and delete
1417 m_sectors.remove(*j);
1423 u32 Map::unloadUnusedData(float timeout, bool only_blocks,
1424 core::list<v3s16> *deleted_blocks)
1426 core::list<v2s16> sector_deletion_queue;
1428 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1429 for(; si.atEnd() == false; si++)
1431 MapSector *sector = si.getNode()->getValue();
1433 bool all_blocks_deleted = true;
1435 core::list<MapBlock*> blocks;
1436 sector->getBlocks(blocks);
1437 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1438 i != blocks.end(); i++)
1440 MapBlock *block = (*i);
1442 if(block->getUsageTimer() > timeout)
1445 if(block->getModified() != MOD_STATE_CLEAN)
1447 // Delete from memory
1448 sector->deleteBlock(block);
1452 all_blocks_deleted = false;
1456 if(all_blocks_deleted)
1458 sector_deletion_queue.push_back(si.getNode()->getKey());
1463 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1464 for(; i.atEnd() == false; i++)
1466 MapSector *sector = i.getNode()->getValue();
1468 Delete sector from memory if it hasn't been used in a long time
1470 if(sector->usage_timer > timeout)
1472 sector_deletion_queue.push_back(i.getNode()->getKey());
1474 if(deleted_blocks != NULL)
1476 // Collect positions of blocks of sector
1477 MapSector *sector = i.getNode()->getValue();
1478 core::list<MapBlock*> blocks;
1479 sector->getBlocks(blocks);
1480 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1481 i != blocks.end(); i++)
1483 deleted_blocks->push_back((*i)->getPos());
1490 deleteSectors(sector_deletion_queue, only_blocks);
1491 return sector_deletion_queue.getSize();
1494 void Map::PrintInfo(std::ostream &out)
1499 #define WATER_DROP_BOOST 4
1501 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1503 DSTACK(__FUNCTION_NAME);
1504 //TimeTaker timer("transformLiquids()");
1507 u32 initial_size = m_transforming_liquid.size();
1509 /*if(initial_size != 0)
1510 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1512 while(m_transforming_liquid.size() != 0)
1515 Get a queued transforming liquid node
1517 v3s16 p0 = m_transforming_liquid.pop_front();
1519 MapNode n0 = getNode(p0);
1521 // Don't deal with non-liquids
1522 if(content_liquid(n0.d) == false)
1525 bool is_source = !content_flowing_liquid(n0.d);
1527 u8 liquid_level = 8;
1528 if(is_source == false)
1529 liquid_level = n0.param2 & 0x0f;
1531 // Turn possible source into non-source
1532 u8 nonsource_c = make_liquid_flowing(n0.d);
1535 If not source, check that some node flows into this one
1536 and what is the level of liquid in this one
1538 if(is_source == false)
1540 s8 new_liquid_level_max = -1;
1542 v3s16 dirs_from[5] = {
1543 v3s16(0,1,0), // top
1544 v3s16(0,0,1), // back
1545 v3s16(1,0,0), // right
1546 v3s16(0,0,-1), // front
1547 v3s16(-1,0,0), // left
1549 for(u16 i=0; i<5; i++)
1554 bool from_top = (i==0);
1556 v3s16 p2 = p0 + dirs_from[i];
1557 MapNode n2 = getNode(p2);
1559 if(content_liquid(n2.d))
1561 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1562 // Check that the liquids are the same type
1563 if(n2_nonsource_c != nonsource_c)
1565 dstream<<"WARNING: Not handling: different liquids"
1566 " collide"<<std::endl;
1569 bool n2_is_source = !content_flowing_liquid(n2.d);
1570 s8 n2_liquid_level = 8;
1571 if(n2_is_source == false)
1572 n2_liquid_level = n2.param2 & 0x07;
1574 s8 new_liquid_level = -1;
1577 //new_liquid_level = 7;
1578 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1579 new_liquid_level = 7;
1581 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1583 else if(n2_liquid_level > 0)
1585 new_liquid_level = n2_liquid_level - 1;
1588 if(new_liquid_level > new_liquid_level_max)
1589 new_liquid_level_max = new_liquid_level;
1592 }catch(InvalidPositionException &e)
1598 If liquid level should be something else, update it and
1599 add all the neighboring water nodes to the transform queue.
1601 if(new_liquid_level_max != liquid_level)
1603 if(new_liquid_level_max == -1)
1605 // Remove water alltoghether
1612 n0.param2 = new_liquid_level_max;
1616 // Block has been modified
1618 v3s16 blockpos = getNodeBlockPos(p0);
1619 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1621 modified_blocks.insert(blockpos, block);
1625 Add neighboring non-source liquid nodes to transform queue.
1628 v3s16(0,0,1), // back
1629 v3s16(0,1,0), // top
1630 v3s16(1,0,0), // right
1631 v3s16(0,0,-1), // front
1632 v3s16(0,-1,0), // bottom
1633 v3s16(-1,0,0), // left
1635 for(u16 i=0; i<6; i++)
1640 v3s16 p2 = p0 + dirs[i];
1642 MapNode n2 = getNode(p2);
1643 if(content_flowing_liquid(n2.d))
1645 m_transforming_liquid.push_back(p2);
1648 }catch(InvalidPositionException &e)
1655 // Get a new one from queue if the node has turned into non-water
1656 if(content_liquid(n0.d) == false)
1660 Flow water from this node
1662 v3s16 dirs_to[5] = {
1663 v3s16(0,-1,0), // bottom
1664 v3s16(0,0,1), // back
1665 v3s16(1,0,0), // right
1666 v3s16(0,0,-1), // front
1667 v3s16(-1,0,0), // left
1669 for(u16 i=0; i<5; i++)
1674 bool to_bottom = (i == 0);
1676 // If liquid is at lowest possible height, it's not going
1677 // anywhere except down
1678 if(liquid_level == 0 && to_bottom == false)
1681 u8 liquid_next_level = 0;
1682 // If going to bottom
1685 //liquid_next_level = 7;
1686 if(liquid_level >= 7 - WATER_DROP_BOOST)
1687 liquid_next_level = 7;
1689 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1692 liquid_next_level = liquid_level - 1;
1694 bool n2_changed = false;
1695 bool flowed = false;
1697 v3s16 p2 = p0 + dirs_to[i];
1699 MapNode n2 = getNode(p2);
1700 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1702 if(content_liquid(n2.d))
1704 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1705 // Check that the liquids are the same type
1706 if(n2_nonsource_c != nonsource_c)
1708 dstream<<"WARNING: Not handling: different liquids"
1709 " collide"<<std::endl;
1712 bool n2_is_source = !content_flowing_liquid(n2.d);
1713 u8 n2_liquid_level = 8;
1714 if(n2_is_source == false)
1715 n2_liquid_level = n2.param2 & 0x07;
1724 // Just flow into the source, nothing changes.
1725 // n2_changed is not set because destination didn't change
1730 if(liquid_next_level > liquid_level)
1732 n2.param2 = liquid_next_level;
1740 else if(n2.d == CONTENT_AIR)
1743 n2.param2 = liquid_next_level;
1750 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1754 m_transforming_liquid.push_back(p2);
1756 v3s16 blockpos = getNodeBlockPos(p2);
1757 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1759 modified_blocks.insert(blockpos, block);
1762 // If n2_changed to bottom, don't flow anywhere else
1763 if(to_bottom && flowed && !is_source)
1766 }catch(InvalidPositionException &e)
1772 //if(loopcount >= 100000)
1773 if(loopcount >= initial_size * 1)
1776 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1779 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1781 v3s16 blockpos = getNodeBlockPos(p);
1782 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1783 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1786 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1790 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1794 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1796 v3s16 blockpos = getNodeBlockPos(p);
1797 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1798 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1801 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1805 block->m_node_metadata.set(p_rel, meta);
1808 void Map::removeNodeMetadata(v3s16 p)
1810 v3s16 blockpos = getNodeBlockPos(p);
1811 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1812 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1815 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1819 block->m_node_metadata.remove(p_rel);
1822 void Map::nodeMetadataStep(float dtime,
1823 core::map<v3s16, MapBlock*> &changed_blocks)
1827 Currently there is no way to ensure that all the necessary
1828 blocks are loaded when this is run. (They might get unloaded)
1829 NOTE: ^- Actually, that might not be so. In a quick test it
1830 reloaded a block with a furnace when I walked back to it from
1833 core::map<v2s16, MapSector*>::Iterator si;
1834 si = m_sectors.getIterator();
1835 for(; si.atEnd() == false; si++)
1837 MapSector *sector = si.getNode()->getValue();
1838 core::list< MapBlock * > sectorblocks;
1839 sector->getBlocks(sectorblocks);
1840 core::list< MapBlock * >::Iterator i;
1841 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1843 MapBlock *block = *i;
1844 bool changed = block->m_node_metadata.step(dtime);
1846 changed_blocks[block->getPos()] = block;
1855 ServerMap::ServerMap(std::string savedir):
1858 m_map_metadata_changed(true)
1860 dstream<<__FUNCTION_NAME<<std::endl;
1862 //m_chunksize = 8; // Takes a few seconds
1864 m_seed = (((u64)(myrand()%0xffff)<<0)
1865 + ((u64)(myrand()%0xffff)<<16)
1866 + ((u64)(myrand()%0xffff)<<32)
1867 + ((u64)(myrand()%0xffff)<<48));
1870 Experimental and debug stuff
1877 Try to load map; if not found, create a new one.
1880 m_savedir = savedir;
1881 m_map_saving_enabled = false;
1885 // If directory exists, check contents and load if possible
1886 if(fs::PathExists(m_savedir))
1888 // If directory is empty, it is safe to save into it.
1889 if(fs::GetDirListing(m_savedir).size() == 0)
1891 dstream<<DTIME<<"Server: Empty save directory is valid."
1893 m_map_saving_enabled = true;
1898 // Load map metadata (seed, chunksize)
1901 catch(FileNotGoodException &e){
1902 dstream<<DTIME<<"WARNING: Could not load map metadata"
1903 //<<" Disabling chunk-based generator."
1909 // Load chunk metadata
1912 catch(FileNotGoodException &e){
1913 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1914 <<" Disabling chunk-based generator."
1919 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1920 "metadata and sector (0,0) from "<<savedir<<
1921 ", assuming valid save directory."
1924 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1925 <<"and chunk metadata from "<<savedir
1926 <<", assuming valid save directory."
1929 m_map_saving_enabled = true;
1930 // Map loaded, not creating new one
1934 // If directory doesn't exist, it is safe to save to it
1936 m_map_saving_enabled = true;
1939 catch(std::exception &e)
1941 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1942 <<", exception: "<<e.what()<<std::endl;
1943 dstream<<"Please remove the map or fix it."<<std::endl;
1944 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1947 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1949 // Create zero sector
1950 emergeSector(v2s16(0,0));
1952 // Initially write whole map
1956 ServerMap::~ServerMap()
1958 dstream<<__FUNCTION_NAME<<std::endl;
1962 if(m_map_saving_enabled)
1965 // Save only changed parts
1967 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1971 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1974 catch(std::exception &e)
1976 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1977 <<", exception: "<<e.what()<<std::endl;
1984 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1985 for(; i.atEnd() == false; i++)
1987 MapChunk *chunk = i.getNode()->getValue();
1993 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
1995 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
1996 <<blockpos.Z<<")"<<std::endl;*/
1998 data->no_op = false;
1999 data->seed = m_seed;
2000 data->blockpos = blockpos;
2003 Create the whole area of this and the neighboring blocks
2006 //TimeTaker timer("initBlockMake() create area");
2008 for(s16 x=-1; x<=1; x++)
2009 for(s16 z=-1; z<=1; z++)
2011 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2012 // Sector metadata is loaded from disk if not already loaded.
2013 ServerMapSector *sector = createSector(sectorpos);
2016 for(s16 y=-1; y<=1; y++)
2018 MapBlock *block = createBlock(blockpos);
2020 // Lighting won't be calculated
2021 block->setLightingExpired(true);
2022 // Lighting will be calculated
2023 //block->setLightingExpired(false);
2026 Block gets sunlight if this is true.
2028 This should be set to true when the top side of a block
2029 is completely exposed to the sky.
2031 block->setIsUnderground(false);
2037 Now we have a big empty area.
2039 Make a ManualMapVoxelManipulator that contains this and the
2043 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2044 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2046 data->vmanip = new ManualMapVoxelManipulator(this);
2047 //data->vmanip->setMap(this);
2051 //TimeTaker timer("initBlockMake() initialEmerge");
2052 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2055 // Data is ready now.
2058 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2059 core::map<v3s16, MapBlock*> &changed_blocks)
2061 v3s16 blockpos = data->blockpos;
2062 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2063 <<blockpos.Z<<")"<<std::endl;*/
2067 dstream<<"finishBlockMake(): no-op"<<std::endl;
2071 /*dstream<<"Resulting vmanip:"<<std::endl;
2072 data->vmanip.print(dstream);*/
2075 Blit generated stuff to map
2076 NOTE: blitBackAll adds nearly everything to changed_blocks
2080 //TimeTaker timer("finishBlockMake() blitBackAll");
2081 data->vmanip->blitBackAll(&changed_blocks);
2084 dstream<<"finishBlockMake: changed_blocks.size()="
2085 <<changed_blocks.size()<<std::endl;
2088 Copy transforming liquid information
2090 while(data->transforming_liquid.size() > 0)
2092 v3s16 p = data->transforming_liquid.pop_front();
2093 m_transforming_liquid.push_back(p);
2099 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2103 Set is_underground flag for lighting with sunlight
2106 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2109 Add sunlight to central block.
2110 This makes in-dark-spawning monsters to not flood the whole thing.
2111 Do not spread the light, though.
2113 /*core::map<v3s16, bool> light_sources;
2114 bool black_air_left = false;
2115 block->propagateSunlight(light_sources, true, &black_air_left);*/
2118 NOTE: Lighting and object adding shouldn't really be here, but
2119 lighting is a bit tricky to move properly to makeBlock.
2120 TODO: Do this the right way anyway.
2127 TimeTaker t("finishBlockMake lighting update");
2129 core::map<v3s16, MapBlock*> lighting_update_blocks;
2131 lighting_update_blocks.insert(block->getPos(), block);
2133 // All modified blocks
2134 for(core::map<v3s16, MapBlock*>::Iterator
2135 i = changed_blocks.getIterator();
2136 i.atEnd() == false; i++)
2138 lighting_update_blocks.insert(i.getNode()->getKey(),
2139 i.getNode()->getValue());
2142 updateLighting(lighting_update_blocks, changed_blocks);
2146 Add random objects to block
2148 mapgen::add_random_objects(block);
2151 Go through changed blocks
2153 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2154 i.atEnd() == false; i++)
2156 MapBlock *block = i.getNode()->getValue();
2159 Update day/night difference cache of the MapBlocks
2161 block->updateDayNightDiff();
2163 Set block as modified
2165 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2169 Set central block as generated
2171 block->setGenerated(true);
2174 Save changed parts of map
2175 NOTE: Will be saved later.
2179 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2180 <<blockpos.Z<<")"<<std::endl;*/
2185 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2187 DSTACKF("%s: p2d=(%d,%d)",
2192 Check if it exists already in memory
2194 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2199 Try to load it from disk (with blocks)
2201 //if(loadSectorFull(p2d) == true)
2204 Try to load metadata from disk
2206 if(loadSectorMeta(p2d) == true)
2208 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2211 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2212 throw InvalidPositionException("");
2218 Do not create over-limit
2220 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2221 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2222 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2223 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2224 throw InvalidPositionException("createSector(): pos. over limit");
2227 Generate blank sector
2230 sector = new ServerMapSector(this, p2d);
2232 // Sector position on map in nodes
2233 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2238 m_sectors.insert(p2d, sector);
2244 This is a quick-hand function for calling makeBlock().
2246 MapBlock * ServerMap::generateBlock(
2248 core::map<v3s16, MapBlock*> &modified_blocks
2251 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2253 /*dstream<<"generateBlock(): "
2254 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2257 TimeTaker timer("generateBlock");
2259 //MapBlock *block = original_dummy;
2261 v2s16 p2d(p.X, p.Z);
2262 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2265 Do not generate over-limit
2267 if(blockpos_over_limit(p))
2269 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2270 throw InvalidPositionException("generateBlock(): pos. over limit");
2274 Create block make data
2276 mapgen::BlockMakeData data;
2277 initBlockMake(&data, p);
2283 TimeTaker t("mapgen::make_block()");
2284 mapgen::make_block(&data);
2288 Blit data back on map, update lighting, add mobs and whatever this does
2290 finishBlockMake(&data, modified_blocks);
2295 MapBlock *block = getBlockNoCreateNoEx(p);
2302 bool erroneus_content = false;
2303 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2304 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2305 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2308 MapNode n = block->getNode(p);
2309 if(n.d == CONTENT_IGNORE)
2311 dstream<<"CONTENT_IGNORE at "
2312 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2314 erroneus_content = true;
2318 if(erroneus_content)
2326 Generate a completely empty block
2328 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2329 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2331 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2337 n.d = CONTENT_STONE;
2338 block->setNode(v3s16(x0,y0,z0), n);
2346 MapBlock * ServerMap::createBlock(v3s16 p)
2348 DSTACKF("%s: p=(%d,%d,%d)",
2349 __FUNCTION_NAME, p.X, p.Y, p.Z);
2352 Do not create over-limit
2354 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2355 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2356 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2357 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2358 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2359 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2360 throw InvalidPositionException("createBlock(): pos. over limit");
2362 v2s16 p2d(p.X, p.Z);
2365 This will create or load a sector if not found in memory.
2366 If block exists on disk, it will be loaded.
2368 NOTE: On old save formats, this will be slow, as it generates
2369 lighting on blocks for them.
2371 ServerMapSector *sector;
2373 sector = (ServerMapSector*)createSector(p2d);
2374 assert(sector->getId() == MAPSECTOR_SERVER);
2376 catch(InvalidPositionException &e)
2378 dstream<<"createBlock: createSector() failed"<<std::endl;
2382 NOTE: This should not be done, or at least the exception
2383 should not be passed on as std::exception, because it
2384 won't be catched at all.
2386 /*catch(std::exception &e)
2388 dstream<<"createBlock: createSector() failed: "
2389 <<e.what()<<std::endl;
2394 Try to get a block from the sector
2397 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2400 if(block->isDummy())
2405 block = sector->createBlankBlock(block_y);
2410 MapBlock * ServerMap::emergeBlock(
2412 bool only_from_disk,
2413 core::map<v3s16, MapBlock*> &changed_blocks,
2414 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2417 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
2419 p.X, p.Y, p.Z, only_from_disk);
2421 // This has to be redone or removed
2429 Do not generate over-limit
2431 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2432 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2433 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2434 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2435 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2436 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2437 throw InvalidPositionException("emergeBlock(): pos. over limit");
2439 v2s16 p2d(p.X, p.Z);
2442 This will create or load a sector if not found in memory.
2443 If block exists on disk, it will be loaded.
2445 ServerMapSector *sector;
2447 sector = createSector(p2d);
2448 //sector = emergeSector(p2d, changed_blocks);
2450 catch(InvalidPositionException &e)
2452 dstream<<"emergeBlock: createSector() failed: "
2453 <<e.what()<<std::endl;
2454 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2456 <<"You could try to delete it."<<std::endl;
2459 catch(VersionMismatchException &e)
2461 dstream<<"emergeBlock: createSector() failed: "
2462 <<e.what()<<std::endl;
2463 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2465 <<"You could try to delete it."<<std::endl;
2470 Try to get a block from the sector
2473 bool does_not_exist = false;
2474 bool lighting_expired = false;
2475 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2477 // If not found, try loading from disk
2480 block = loadBlock(p);
2486 does_not_exist = true;
2488 else if(block->isDummy() == true)
2490 does_not_exist = true;
2492 else if(block->getLightingExpired())
2494 lighting_expired = true;
2499 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2504 If block was not found on disk and not going to generate a
2505 new one, make sure there is a dummy block in place.
2507 if(only_from_disk && (does_not_exist || lighting_expired))
2509 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2513 // Create dummy block
2514 block = new MapBlock(this, p, true);
2516 // Add block to sector
2517 sector->insertBlock(block);
2523 //dstream<<"Not found on disk, generating."<<std::endl;
2525 //TimeTaker("emergeBlock() generate");
2527 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2530 If the block doesn't exist, generate the block.
2534 block = generateBlock(p, block, sector, changed_blocks,
2535 lighting_invalidated_blocks);
2538 if(lighting_expired)
2540 lighting_invalidated_blocks.insert(p, block);
2545 Initially update sunlight
2548 core::map<v3s16, bool> light_sources;
2549 bool black_air_left = false;
2550 bool bottom_invalid =
2551 block->propagateSunlight(light_sources, true,
2554 // If sunlight didn't reach everywhere and part of block is
2555 // above ground, lighting has to be properly updated
2556 //if(black_air_left && some_part_underground)
2559 lighting_invalidated_blocks[block->getPos()] = block;
2564 lighting_invalidated_blocks[block->getPos()] = block;
2573 s16 ServerMap::findGroundLevel(v2s16 p2d)
2577 Uh, just do something random...
2579 // Find existing map from top to down
2582 v3s16 p(p2d.X, max, p2d.Y);
2583 for(; p.Y>min; p.Y--)
2585 MapNode n = getNodeNoEx(p);
2586 if(n.d != CONTENT_IGNORE)
2591 // If this node is not air, go to plan b
2592 if(getNodeNoEx(p).d != CONTENT_AIR)
2594 // Search existing walkable and return it
2595 for(; p.Y>min; p.Y--)
2597 MapNode n = getNodeNoEx(p);
2598 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2607 Determine from map generator noise functions
2610 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2613 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2614 //return (s16)level;
2617 void ServerMap::createDirs(std::string path)
2619 if(fs::CreateAllDirs(path) == false)
2621 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2622 <<"\""<<path<<"\""<<std::endl;
2623 throw BaseException("ServerMap failed to create directory");
2627 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2633 snprintf(cc, 9, "%.4x%.4x",
2634 (unsigned int)pos.X&0xffff,
2635 (unsigned int)pos.Y&0xffff);
2637 return m_savedir + "/sectors/" + cc;
2639 snprintf(cc, 9, "%.3x/%.3x",
2640 (unsigned int)pos.X&0xfff,
2641 (unsigned int)pos.Y&0xfff);
2643 return m_savedir + "/sectors2/" + cc;
2649 v2s16 ServerMap::getSectorPos(std::string dirname)
2653 size_t spos = dirname.rfind('/') + 1;
2654 assert(spos != std::string::npos);
2655 if(dirname.size() - spos == 8)
2658 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2660 else if(dirname.size() - spos == 3)
2663 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2664 // Sign-extend the 12 bit values up to 16 bits...
2665 if(x&0x800) x|=0xF000;
2666 if(y&0x800) y|=0xF000;
2673 v2s16 pos((s16)x, (s16)y);
2677 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2679 v2s16 p2d = getSectorPos(sectordir);
2681 if(blockfile.size() != 4){
2682 throw InvalidFilenameException("Invalid block filename");
2685 int r = sscanf(blockfile.c_str(), "%4x", &y);
2687 throw InvalidFilenameException("Invalid block filename");
2688 return v3s16(p2d.X, y, p2d.Y);
2691 std::string ServerMap::getBlockFilename(v3s16 p)
2694 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2698 void ServerMap::save(bool only_changed)
2700 DSTACK(__FUNCTION_NAME);
2701 if(m_map_saving_enabled == false)
2703 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2707 if(only_changed == false)
2708 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2711 if(only_changed == false || m_map_metadata_changed)
2716 u32 sector_meta_count = 0;
2717 u32 block_count = 0;
2718 u32 block_count_all = 0; // Number of blocks in memory
2720 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2721 for(; i.atEnd() == false; i++)
2723 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2724 assert(sector->getId() == MAPSECTOR_SERVER);
2726 if(sector->differs_from_disk || only_changed == false)
2728 saveSectorMeta(sector);
2729 sector_meta_count++;
2731 core::list<MapBlock*> blocks;
2732 sector->getBlocks(blocks);
2733 core::list<MapBlock*>::Iterator j;
2734 for(j=blocks.begin(); j!=blocks.end(); j++)
2736 MapBlock *block = *j;
2740 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2741 || only_changed == false)
2746 /*dstream<<"ServerMap: Written block ("
2747 <<block->getPos().X<<","
2748 <<block->getPos().Y<<","
2749 <<block->getPos().Z<<")"
2756 Only print if something happened or saved whole map
2758 if(only_changed == false || sector_meta_count != 0
2759 || block_count != 0)
2761 dstream<<DTIME<<"ServerMap: Written: "
2762 <<sector_meta_count<<" sector metadata files, "
2763 <<block_count<<" block files"
2764 <<", "<<block_count_all<<" blocks in memory."
2769 void ServerMap::saveMapMeta()
2771 DSTACK(__FUNCTION_NAME);
2773 dstream<<"INFO: ServerMap::saveMapMeta(): "
2777 createDirs(m_savedir);
2779 std::string fullpath = m_savedir + "/map_meta.txt";
2780 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2781 if(os.good() == false)
2783 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2784 <<"could not open"<<fullpath<<std::endl;
2785 throw FileNotGoodException("Cannot open chunk metadata");
2789 params.setU64("seed", m_seed);
2791 params.writeLines(os);
2793 os<<"[end_of_params]\n";
2795 m_map_metadata_changed = false;
2798 void ServerMap::loadMapMeta()
2800 DSTACK(__FUNCTION_NAME);
2802 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2805 std::string fullpath = m_savedir + "/map_meta.txt";
2806 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2807 if(is.good() == false)
2809 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2810 <<"could not open"<<fullpath<<std::endl;
2811 throw FileNotGoodException("Cannot open map metadata");
2819 throw SerializationError
2820 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2822 std::getline(is, line);
2823 std::string trimmedline = trim(line);
2824 if(trimmedline == "[end_of_params]")
2826 params.parseConfigLine(line);
2829 m_seed = params.getU64("seed");
2831 dstream<<"INFO: ServerMap::loadMapMeta(): "
2836 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2838 DSTACK(__FUNCTION_NAME);
2839 // Format used for writing
2840 u8 version = SER_FMT_VER_HIGHEST;
2842 v2s16 pos = sector->getPos();
2843 std::string dir = getSectorDir(pos);
2846 std::string fullpath = dir + "/meta";
2847 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2848 if(o.good() == false)
2849 throw FileNotGoodException("Cannot open sector metafile");
2851 sector->serialize(o, version);
2853 sector->differs_from_disk = false;
2856 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2858 DSTACK(__FUNCTION_NAME);
2860 v2s16 p2d = getSectorPos(sectordir);
2862 ServerMapSector *sector = NULL;
2864 std::string fullpath = sectordir + "/meta";
2865 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2866 if(is.good() == false)
2868 // If the directory exists anyway, it probably is in some old
2869 // format. Just go ahead and create the sector.
2870 if(fs::PathExists(sectordir))
2872 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2873 <<fullpath<<" doesn't exist but directory does."
2874 <<" Continuing with a sector with no metadata."
2876 sector = new ServerMapSector(this, p2d);
2877 m_sectors.insert(p2d, sector);
2881 throw FileNotGoodException("Cannot open sector metafile");
2886 sector = ServerMapSector::deSerialize
2887 (is, this, p2d, m_sectors);
2889 saveSectorMeta(sector);
2892 sector->differs_from_disk = false;
2897 bool ServerMap::loadSectorMeta(v2s16 p2d)
2899 DSTACK(__FUNCTION_NAME);
2901 MapSector *sector = NULL;
2903 // The directory layout we're going to load from.
2904 // 1 - original sectors/xxxxzzzz/
2905 // 2 - new sectors2/xxx/zzz/
2906 // If we load from anything but the latest structure, we will
2907 // immediately save to the new one, and remove the old.
2909 std::string sectordir1 = getSectorDir(p2d, 1);
2910 std::string sectordir;
2911 if(fs::PathExists(sectordir1))
2913 sectordir = sectordir1;
2918 sectordir = getSectorDir(p2d, 2);
2922 sector = loadSectorMeta(sectordir, loadlayout != 2);
2924 catch(InvalidFilenameException &e)
2928 catch(FileNotGoodException &e)
2932 catch(std::exception &e)
2941 bool ServerMap::loadSectorFull(v2s16 p2d)
2943 DSTACK(__FUNCTION_NAME);
2945 MapSector *sector = NULL;
2947 // The directory layout we're going to load from.
2948 // 1 - original sectors/xxxxzzzz/
2949 // 2 - new sectors2/xxx/zzz/
2950 // If we load from anything but the latest structure, we will
2951 // immediately save to the new one, and remove the old.
2953 std::string sectordir1 = getSectorDir(p2d, 1);
2954 std::string sectordir;
2955 if(fs::PathExists(sectordir1))
2957 sectordir = sectordir1;
2962 sectordir = getSectorDir(p2d, 2);
2966 sector = loadSectorMeta(sectordir, loadlayout != 2);
2968 catch(InvalidFilenameException &e)
2972 catch(FileNotGoodException &e)
2976 catch(std::exception &e)
2984 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2986 std::vector<fs::DirListNode>::iterator i2;
2987 for(i2=list2.begin(); i2!=list2.end(); i2++)
2993 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2995 catch(InvalidFilenameException &e)
2997 // This catches unknown crap in directory
3003 dstream<<"Sector converted to new layout - deleting "<<
3004 sectordir1<<std::endl;
3005 fs::RecursiveDelete(sectordir1);
3012 void ServerMap::saveBlock(MapBlock *block)
3014 DSTACK(__FUNCTION_NAME);
3016 Dummy blocks are not written
3018 if(block->isDummy())
3020 /*v3s16 p = block->getPos();
3021 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3022 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3026 // Format used for writing
3027 u8 version = SER_FMT_VER_HIGHEST;
3029 v3s16 p3d = block->getPos();
3031 v2s16 p2d(p3d.X, p3d.Z);
3032 std::string sectordir = getSectorDir(p2d);
3034 createDirs(sectordir);
3036 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3037 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3038 if(o.good() == false)
3039 throw FileNotGoodException("Cannot open block data");
3042 [0] u8 serialization version
3045 o.write((char*)&version, 1);
3048 block->serialize(o, version);
3050 // Write extra data stored on disk
3051 block->serializeDiskExtra(o, version);
3053 // We just wrote it to the disk so clear modified flag
3054 block->resetModified();
3057 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3059 DSTACK(__FUNCTION_NAME);
3061 std::string fullpath = sectordir+"/"+blockfile;
3064 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3065 if(is.good() == false)
3066 throw FileNotGoodException("Cannot open block file");
3068 v3s16 p3d = getBlockPos(sectordir, blockfile);
3069 v2s16 p2d(p3d.X, p3d.Z);
3071 assert(sector->getPos() == p2d);
3073 u8 version = SER_FMT_VER_INVALID;
3074 is.read((char*)&version, 1);
3077 throw SerializationError("ServerMap::loadBlock(): Failed"
3078 " to read MapBlock version");
3080 /*u32 block_size = MapBlock::serializedLength(version);
3081 SharedBuffer<u8> data(block_size);
3082 is.read((char*)*data, block_size);*/
3084 // This will always return a sector because we're the server
3085 //MapSector *sector = emergeSector(p2d);
3087 MapBlock *block = NULL;
3088 bool created_new = false;
3089 block = sector->getBlockNoCreateNoEx(p3d.Y);
3092 block = sector->createBlankBlockNoInsert(p3d.Y);
3097 block->deSerialize(is, version);
3099 // Read extra data stored on disk
3100 block->deSerializeDiskExtra(is, version);
3102 // If it's a new block, insert it to the map
3104 sector->insertBlock(block);
3107 Save blocks loaded in old format in new format
3110 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3115 // We just loaded it from the disk, so it's up-to-date.
3116 block->resetModified();
3119 catch(SerializationError &e)
3121 dstream<<"WARNING: Invalid block data on disk "
3122 <<"fullpath="<<fullpath
3123 <<" (SerializationError). "
3124 <<"what()="<<e.what()
3126 //" Ignoring. A new one will be generated.
3129 // TODO: Backup file; name is in fullpath.
3133 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3135 DSTACK(__FUNCTION_NAME);
3137 v2s16 p2d(blockpos.X, blockpos.Z);
3139 // The directory layout we're going to load from.
3140 // 1 - original sectors/xxxxzzzz/
3141 // 2 - new sectors2/xxx/zzz/
3142 // If we load from anything but the latest structure, we will
3143 // immediately save to the new one, and remove the old.
3145 std::string sectordir1 = getSectorDir(p2d, 1);
3146 std::string sectordir;
3147 if(fs::PathExists(sectordir1))
3149 sectordir = sectordir1;
3154 sectordir = getSectorDir(p2d, 2);
3158 Make sure sector is loaded
3160 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3164 sector = loadSectorMeta(sectordir, loadlayout != 2);
3166 catch(InvalidFilenameException &e)
3170 catch(FileNotGoodException &e)
3174 catch(std::exception &e)
3181 Make sure file exists
3184 std::string blockfilename = getBlockFilename(blockpos);
3185 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3191 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3192 return getBlockNoCreateNoEx(blockpos);
3195 void ServerMap::PrintInfo(std::ostream &out)
3206 ClientMap::ClientMap(
3208 MapDrawControl &control,
3209 scene::ISceneNode* parent,
3210 scene::ISceneManager* mgr,
3214 scene::ISceneNode(parent, mgr, id),
3217 m_camera_position(0,0,0),
3218 m_camera_direction(0,0,1)
3220 m_camera_mutex.Init();
3221 assert(m_camera_mutex.IsInitialized());
3223 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3224 BS*1000000,BS*1000000,BS*1000000);
3227 ClientMap::~ClientMap()
3229 /*JMutexAutoLock lock(mesh_mutex);
3238 MapSector * ClientMap::emergeSector(v2s16 p2d)
3240 DSTACK(__FUNCTION_NAME);
3241 // Check that it doesn't exist already
3243 return getSectorNoGenerate(p2d);
3245 catch(InvalidPositionException &e)
3250 ClientMapSector *sector = new ClientMapSector(this, p2d);
3253 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3254 m_sectors.insert(p2d, sector);
3261 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3263 DSTACK(__FUNCTION_NAME);
3264 ClientMapSector *sector = NULL;
3266 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3268 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3272 sector = (ClientMapSector*)n->getValue();
3273 assert(sector->getId() == MAPSECTOR_CLIENT);
3277 sector = new ClientMapSector(this, p2d);
3279 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3280 m_sectors.insert(p2d, sector);
3284 sector->deSerialize(is);
3288 void ClientMap::OnRegisterSceneNode()
3292 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3293 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3296 ISceneNode::OnRegisterSceneNode();
3299 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3301 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3302 DSTACK(__FUNCTION_NAME);
3304 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3307 This is called two times per frame, reset on the non-transparent one
3309 if(pass == scene::ESNRP_SOLID)
3311 m_last_drawn_sectors.clear();
3315 Get time for measuring timeout.
3317 Measuring time is very useful for long delays when the
3318 machine is swapping a lot.
3320 int time1 = time(0);
3322 //u32 daynight_ratio = m_client->getDayNightRatio();
3324 m_camera_mutex.Lock();
3325 v3f camera_position = m_camera_position;
3326 v3f camera_direction = m_camera_direction;
3327 m_camera_mutex.Unlock();
3330 Get all blocks and draw all visible ones
3333 v3s16 cam_pos_nodes(
3334 camera_position.X / BS,
3335 camera_position.Y / BS,
3336 camera_position.Z / BS);
3338 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3340 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3341 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3343 // Take a fair amount as we will be dropping more out later
3345 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3346 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3347 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3349 p_nodes_max.X / MAP_BLOCKSIZE,
3350 p_nodes_max.Y / MAP_BLOCKSIZE,
3351 p_nodes_max.Z / MAP_BLOCKSIZE);
3353 u32 vertex_count = 0;
3355 // For limiting number of mesh updates per frame
3356 u32 mesh_update_count = 0;
3358 u32 blocks_would_have_drawn = 0;
3359 u32 blocks_drawn = 0;
3361 int timecheck_counter = 0;
3362 core::map<v2s16, MapSector*>::Iterator si;
3363 si = m_sectors.getIterator();
3364 for(; si.atEnd() == false; si++)
3367 timecheck_counter++;
3368 if(timecheck_counter > 50)
3370 timecheck_counter = 0;
3371 int time2 = time(0);
3372 if(time2 > time1 + 4)
3374 dstream<<"ClientMap::renderMap(): "
3375 "Rendering takes ages, returning."
3382 MapSector *sector = si.getNode()->getValue();
3383 v2s16 sp = sector->getPos();
3385 if(m_control.range_all == false)
3387 if(sp.X < p_blocks_min.X
3388 || sp.X > p_blocks_max.X
3389 || sp.Y < p_blocks_min.Z
3390 || sp.Y > p_blocks_max.Z)
3394 core::list< MapBlock * > sectorblocks;
3395 sector->getBlocks(sectorblocks);
3401 u32 sector_blocks_drawn = 0;
3403 core::list< MapBlock * >::Iterator i;
3404 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3406 MapBlock *block = *i;
3409 Compare block position to camera position, skip
3410 if not seen on display
3413 float range = 100000 * BS;
3414 if(m_control.range_all == false)
3415 range = m_control.wanted_range * BS;
3418 if(isBlockInSight(block->getPos(), camera_position,
3419 camera_direction, range, &d) == false)
3424 // This is ugly (spherical distance limit?)
3425 /*if(m_control.range_all == false &&
3426 d - 0.5*BS*MAP_BLOCKSIZE > range)
3431 Update expired mesh (used for day/night change)
3433 It doesn't work exactly like it should now with the
3434 tasked mesh update but whatever.
3437 bool mesh_expired = false;
3440 JMutexAutoLock lock(block->mesh_mutex);
3442 mesh_expired = block->getMeshExpired();
3444 // Mesh has not been expired and there is no mesh:
3445 // block has no content
3446 if(block->mesh == NULL && mesh_expired == false)
3450 f32 faraway = BS*50;
3451 //f32 faraway = m_control.wanted_range * BS;
3454 This has to be done with the mesh_mutex unlocked
3456 // Pretty random but this should work somewhat nicely
3457 if(mesh_expired && (
3458 (mesh_update_count < 3
3459 && (d < faraway || mesh_update_count < 2)
3462 (m_control.range_all && mesh_update_count < 20)
3465 /*if(mesh_expired && mesh_update_count < 6
3466 && (d < faraway || mesh_update_count < 3))*/
3468 mesh_update_count++;
3470 // Mesh has been expired: generate new mesh
3471 //block->updateMesh(daynight_ratio);
3472 m_client->addUpdateMeshTask(block->getPos());
3474 mesh_expired = false;
3479 Draw the faces of the block
3482 JMutexAutoLock lock(block->mesh_mutex);
3484 scene::SMesh *mesh = block->mesh;
3489 blocks_would_have_drawn++;
3490 if(blocks_drawn >= m_control.wanted_max_blocks
3491 && m_control.range_all == false
3492 && d > m_control.wanted_min_range * BS)
3496 sector_blocks_drawn++;
3498 u32 c = mesh->getMeshBufferCount();
3500 for(u32 i=0; i<c; i++)
3502 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3503 const video::SMaterial& material = buf->getMaterial();
3504 video::IMaterialRenderer* rnd =
3505 driver->getMaterialRenderer(material.MaterialType);
3506 bool transparent = (rnd && rnd->isTransparent());
3507 // Render transparent on transparent pass and likewise.
3508 if(transparent == is_transparent_pass)
3511 This *shouldn't* hurt too much because Irrlicht
3512 doesn't change opengl textures if the old
3513 material is set again.
3515 driver->setMaterial(buf->getMaterial());
3516 driver->drawMeshBuffer(buf);
3517 vertex_count += buf->getVertexCount();
3521 } // foreach sectorblocks
3523 if(sector_blocks_drawn != 0)
3525 m_last_drawn_sectors[sp] = true;
3529 m_control.blocks_drawn = blocks_drawn;
3530 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3532 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3533 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3536 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3537 core::map<v3s16, MapBlock*> *affected_blocks)
3539 bool changed = false;
3541 Add it to all blocks touching it
3544 v3s16(0,0,0), // this
3545 v3s16(0,0,1), // back
3546 v3s16(0,1,0), // top
3547 v3s16(1,0,0), // right
3548 v3s16(0,0,-1), // front
3549 v3s16(0,-1,0), // bottom
3550 v3s16(-1,0,0), // left
3552 for(u16 i=0; i<7; i++)
3554 v3s16 p2 = p + dirs[i];
3555 // Block position of neighbor (or requested) node
3556 v3s16 blockpos = getNodeBlockPos(p2);
3557 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3558 if(blockref == NULL)
3560 // Relative position of requested node
3561 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3562 if(blockref->setTempMod(relpos, mod))
3567 if(changed && affected_blocks!=NULL)
3569 for(u16 i=0; i<7; i++)
3571 v3s16 p2 = p + dirs[i];
3572 // Block position of neighbor (or requested) node
3573 v3s16 blockpos = getNodeBlockPos(p2);
3574 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3575 if(blockref == NULL)
3577 affected_blocks->insert(blockpos, blockref);
3583 bool ClientMap::clearTempMod(v3s16 p,
3584 core::map<v3s16, MapBlock*> *affected_blocks)
3586 bool changed = false;
3588 v3s16(0,0,0), // this
3589 v3s16(0,0,1), // back
3590 v3s16(0,1,0), // top
3591 v3s16(1,0,0), // right
3592 v3s16(0,0,-1), // front
3593 v3s16(0,-1,0), // bottom
3594 v3s16(-1,0,0), // left
3596 for(u16 i=0; i<7; i++)
3598 v3s16 p2 = p + dirs[i];
3599 // Block position of neighbor (or requested) node
3600 v3s16 blockpos = getNodeBlockPos(p2);
3601 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3602 if(blockref == NULL)
3604 // Relative position of requested node
3605 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3606 if(blockref->clearTempMod(relpos))
3611 if(changed && affected_blocks!=NULL)
3613 for(u16 i=0; i<7; i++)
3615 v3s16 p2 = p + dirs[i];
3616 // Block position of neighbor (or requested) node
3617 v3s16 blockpos = getNodeBlockPos(p2);
3618 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3619 if(blockref == NULL)
3621 affected_blocks->insert(blockpos, blockref);
3627 void ClientMap::expireMeshes(bool only_daynight_diffed)
3629 TimeTaker timer("expireMeshes()");
3631 core::map<v2s16, MapSector*>::Iterator si;
3632 si = m_sectors.getIterator();
3633 for(; si.atEnd() == false; si++)
3635 MapSector *sector = si.getNode()->getValue();
3637 core::list< MapBlock * > sectorblocks;
3638 sector->getBlocks(sectorblocks);
3640 core::list< MapBlock * >::Iterator i;
3641 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3643 MapBlock *block = *i;
3645 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3651 JMutexAutoLock lock(block->mesh_mutex);
3652 if(block->mesh != NULL)
3654 /*block->mesh->drop();
3655 block->mesh = NULL;*/
3656 block->setMeshExpired(true);
3663 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3665 assert(mapType() == MAPTYPE_CLIENT);
3668 v3s16 p = blockpos + v3s16(0,0,0);
3669 MapBlock *b = getBlockNoCreate(p);
3670 b->updateMesh(daynight_ratio);
3671 //b->setMeshExpired(true);
3673 catch(InvalidPositionException &e){}
3676 v3s16 p = blockpos + v3s16(-1,0,0);
3677 MapBlock *b = getBlockNoCreate(p);
3678 b->updateMesh(daynight_ratio);
3679 //b->setMeshExpired(true);
3681 catch(InvalidPositionException &e){}
3683 v3s16 p = blockpos + v3s16(0,-1,0);
3684 MapBlock *b = getBlockNoCreate(p);
3685 b->updateMesh(daynight_ratio);
3686 //b->setMeshExpired(true);
3688 catch(InvalidPositionException &e){}
3690 v3s16 p = blockpos + v3s16(0,0,-1);
3691 MapBlock *b = getBlockNoCreate(p);
3692 b->updateMesh(daynight_ratio);
3693 //b->setMeshExpired(true);
3695 catch(InvalidPositionException &e){}
3700 Update mesh of block in which the node is, and if the node is at the
3701 leading edge, update the appropriate leading blocks too.
3703 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3711 v3s16 blockposes[4];
3712 for(u32 i=0; i<4; i++)
3714 v3s16 np = nodepos + dirs[i];
3715 blockposes[i] = getNodeBlockPos(np);
3716 // Don't update mesh of block if it has been done already
3717 bool already_updated = false;
3718 for(u32 j=0; j<i; j++)
3720 if(blockposes[j] == blockposes[i])
3722 already_updated = true;
3729 MapBlock *b = getBlockNoCreate(blockposes[i]);
3730 b->updateMesh(daynight_ratio);
3735 void ClientMap::PrintInfo(std::ostream &out)
3746 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3751 MapVoxelManipulator::~MapVoxelManipulator()
3753 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3757 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3759 TimeTaker timer1("emerge", &emerge_time);
3761 // Units of these are MapBlocks
3762 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3763 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3765 VoxelArea block_area_nodes
3766 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3768 addArea(block_area_nodes);
3770 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3771 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3772 for(s32 x=p_min.X; x<=p_max.X; x++)
3775 core::map<v3s16, bool>::Node *n;
3776 n = m_loaded_blocks.find(p);
3780 bool block_data_inexistent = false;
3783 TimeTaker timer1("emerge load", &emerge_load_time);
3785 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3786 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3789 dstream<<std::endl;*/
3791 MapBlock *block = m_map->getBlockNoCreate(p);
3792 if(block->isDummy())
3793 block_data_inexistent = true;
3795 block->copyTo(*this);
3797 catch(InvalidPositionException &e)
3799 block_data_inexistent = true;
3802 if(block_data_inexistent)
3804 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3805 // Fill with VOXELFLAG_INEXISTENT
3806 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3807 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3809 s32 i = m_area.index(a.MinEdge.X,y,z);
3810 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3814 m_loaded_blocks.insert(p, !block_data_inexistent);
3817 //dstream<<"emerge done"<<std::endl;
3821 SUGG: Add an option to only update eg. water and air nodes.
3822 This will make it interfere less with important stuff if
3825 void MapVoxelManipulator::blitBack
3826 (core::map<v3s16, MapBlock*> & modified_blocks)
3828 if(m_area.getExtent() == v3s16(0,0,0))
3831 //TimeTaker timer1("blitBack");
3833 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3834 <<m_loaded_blocks.size()<<std::endl;*/
3837 Initialize block cache
3839 v3s16 blockpos_last;
3840 MapBlock *block = NULL;
3841 bool block_checked_in_modified = false;
3843 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3844 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3845 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3849 u8 f = m_flags[m_area.index(p)];
3850 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3853 MapNode &n = m_data[m_area.index(p)];
3855 v3s16 blockpos = getNodeBlockPos(p);
3860 if(block == NULL || blockpos != blockpos_last){
3861 block = m_map->getBlockNoCreate(blockpos);
3862 blockpos_last = blockpos;
3863 block_checked_in_modified = false;
3866 // Calculate relative position in block
3867 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3869 // Don't continue if nothing has changed here
3870 if(block->getNode(relpos) == n)
3873 //m_map->setNode(m_area.MinEdge + p, n);
3874 block->setNode(relpos, n);
3877 Make sure block is in modified_blocks
3879 if(block_checked_in_modified == false)
3881 modified_blocks[blockpos] = block;
3882 block_checked_in_modified = true;
3885 catch(InvalidPositionException &e)
3891 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3892 MapVoxelManipulator(map),
3893 m_create_area(false)
3897 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3901 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3903 // Just create the area so that it can be pointed to
3904 VoxelManipulator::emerge(a, caller_id);
3907 void ManualMapVoxelManipulator::initialEmerge(
3908 v3s16 blockpos_min, v3s16 blockpos_max)
3910 TimeTaker timer1("initialEmerge", &emerge_time);
3912 // Units of these are MapBlocks
3913 v3s16 p_min = blockpos_min;
3914 v3s16 p_max = blockpos_max;
3916 VoxelArea block_area_nodes
3917 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3919 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3922 dstream<<"initialEmerge: area: ";
3923 block_area_nodes.print(dstream);
3924 dstream<<" ("<<size_MB<<"MB)";
3928 addArea(block_area_nodes);
3930 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3931 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3932 for(s32 x=p_min.X; x<=p_max.X; x++)
3935 core::map<v3s16, bool>::Node *n;
3936 n = m_loaded_blocks.find(p);
3940 bool block_data_inexistent = false;
3943 TimeTaker timer1("emerge load", &emerge_load_time);
3945 MapBlock *block = m_map->getBlockNoCreate(p);
3946 if(block->isDummy())
3947 block_data_inexistent = true;
3949 block->copyTo(*this);
3951 catch(InvalidPositionException &e)
3953 block_data_inexistent = true;
3956 if(block_data_inexistent)
3959 Mark area inexistent
3961 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3962 // Fill with VOXELFLAG_INEXISTENT
3963 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3964 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3966 s32 i = m_area.index(a.MinEdge.X,y,z);
3967 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3971 m_loaded_blocks.insert(p, !block_data_inexistent);
3975 void ManualMapVoxelManipulator::blitBackAll(
3976 core::map<v3s16, MapBlock*> * modified_blocks)
3978 if(m_area.getExtent() == v3s16(0,0,0))
3982 Copy data of all blocks
3984 for(core::map<v3s16, bool>::Iterator
3985 i = m_loaded_blocks.getIterator();
3986 i.atEnd() == false; i++)
3988 bool existed = i.getNode()->getValue();
3989 if(existed == false)
3991 v3s16 p = i.getNode()->getKey();
3992 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3995 dstream<<"WARNING: "<<__FUNCTION_NAME
3996 <<": got NULL block "
3997 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4002 block->copyFrom(*this);
4005 modified_blocks->insert(p, block);