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.
22 #include "jmutexautolock.h"
30 #include "serverobject.h"
31 #include "content_mapnode.h"
38 SQLite format specification:
39 - Initially only replaces sectors/ and sectors2/
46 Map::Map(std::ostream &dout):
50 /*m_sector_mutex.Init();
51 assert(m_sector_mutex.IsInitialized());*/
59 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
60 for(; i.atEnd() == false; i++)
62 MapSector *sector = i.getNode()->getValue();
67 void Map::addEventReceiver(MapEventReceiver *event_receiver)
69 m_event_receivers.insert(event_receiver, false);
72 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
74 if(m_event_receivers.find(event_receiver) == NULL)
76 m_event_receivers.remove(event_receiver);
79 void Map::dispatchEvent(MapEditEvent *event)
81 for(core::map<MapEventReceiver*, bool>::Iterator
82 i = m_event_receivers.getIterator();
83 i.atEnd()==false; i++)
85 MapEventReceiver* event_receiver = i.getNode()->getKey();
86 event_receiver->onMapEditEvent(event);
90 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
92 if(m_sector_cache != NULL && p == m_sector_cache_p){
93 MapSector * sector = m_sector_cache;
97 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
102 MapSector *sector = n->getValue();
104 // Cache the last result
105 m_sector_cache_p = p;
106 m_sector_cache = sector;
111 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
113 return getSectorNoGenerateNoExNoLock(p);
116 MapSector * Map::getSectorNoGenerate(v2s16 p)
118 MapSector *sector = getSectorNoGenerateNoEx(p);
120 throw InvalidPositionException();
125 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
127 v2s16 p2d(p3d.X, p3d.Z);
128 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
135 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
139 v2s16 p2d(p3d.X, p3d.Z);
140 MapSector * sector = getSectorNoGenerate(p2d);
141 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
144 catch(InvalidPositionException &e)
150 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
152 v2s16 p2d(p3d.X, p3d.Z);
153 MapSector * sector = getSectorCreate(p2d);
155 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
158 block = sector->createBlankBlock(p3d.Y);
162 bool Map::isNodeUnderground(v3s16 p)
164 v3s16 blockpos = getNodeBlockPos(p);
166 MapBlock * block = getBlockNoCreate(blockpos);
167 return block->getIsUnderground();
169 catch(InvalidPositionException &e)
176 Goes recursively through the neighbours of the node.
178 Alters only transparent nodes.
180 If the lighting of the neighbour is lower than the lighting of
181 the node was (before changing it to 0 at the step before), the
182 lighting of the neighbour is set to 0 and then the same stuff
183 repeats for the neighbour.
185 The ending nodes of the routine are stored in light_sources.
186 This is useful when a light is removed. In such case, this
187 routine can be called for the light node and then again for
188 light_sources to re-light the area without the removed light.
190 values of from_nodes are lighting values.
192 void Map::unspreadLight(enum LightBank bank,
193 core::map<v3s16, u8> & from_nodes,
194 core::map<v3s16, bool> & light_sources,
195 core::map<v3s16, MapBlock*> & modified_blocks)
198 v3s16(0,0,1), // back
200 v3s16(1,0,0), // right
201 v3s16(0,0,-1), // front
202 v3s16(0,-1,0), // bottom
203 v3s16(-1,0,0), // left
206 if(from_nodes.size() == 0)
209 u32 blockchangecount = 0;
211 core::map<v3s16, u8> unlighted_nodes;
212 core::map<v3s16, u8>::Iterator j;
213 j = from_nodes.getIterator();
216 Initialize block cache
219 MapBlock *block = NULL;
220 // Cache this a bit, too
221 bool block_checked_in_modified = false;
223 for(; j.atEnd() == false; j++)
225 v3s16 pos = j.getNode()->getKey();
226 v3s16 blockpos = getNodeBlockPos(pos);
228 // Only fetch a new block if the block position has changed
230 if(block == NULL || blockpos != blockpos_last){
231 block = getBlockNoCreate(blockpos);
232 blockpos_last = blockpos;
234 block_checked_in_modified = false;
238 catch(InvalidPositionException &e)
246 // Calculate relative position in block
247 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
249 // Get node straight from the block
250 MapNode n = block->getNode(relpos);
252 u8 oldlight = j.getNode()->getValue();
254 // Loop through 6 neighbors
255 for(u16 i=0; i<6; i++)
257 // Get the position of the neighbor node
258 v3s16 n2pos = pos + dirs[i];
260 // Get the block where the node is located
261 v3s16 blockpos = getNodeBlockPos(n2pos);
265 // Only fetch a new block if the block position has changed
267 if(block == NULL || blockpos != blockpos_last){
268 block = getBlockNoCreate(blockpos);
269 blockpos_last = blockpos;
271 block_checked_in_modified = false;
275 catch(InvalidPositionException &e)
280 // Calculate relative position in block
281 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
282 // Get node straight from the block
283 MapNode n2 = block->getNode(relpos);
285 bool changed = false;
287 //TODO: Optimize output by optimizing light_sources?
290 If the neighbor is dimmer than what was specified
291 as oldlight (the light of the previous node)
293 if(n2.getLight(bank) < oldlight)
296 And the neighbor is transparent and it has some light
298 if(n2.light_propagates() && n2.getLight(bank) != 0)
301 Set light to 0 and add to queue
304 u8 current_light = n2.getLight(bank);
305 n2.setLight(bank, 0);
306 block->setNode(relpos, n2);
308 unlighted_nodes.insert(n2pos, current_light);
312 Remove from light_sources if it is there
313 NOTE: This doesn't happen nearly at all
315 /*if(light_sources.find(n2pos))
317 std::cout<<"Removed from light_sources"<<std::endl;
318 light_sources.remove(n2pos);
323 if(light_sources.find(n2pos) != NULL)
324 light_sources.remove(n2pos);*/
327 light_sources.insert(n2pos, true);
330 // Add to modified_blocks
331 if(changed == true && block_checked_in_modified == false)
333 // If the block is not found in modified_blocks, add.
334 if(modified_blocks.find(blockpos) == NULL)
336 modified_blocks.insert(blockpos, block);
338 block_checked_in_modified = true;
341 catch(InvalidPositionException &e)
348 /*dstream<<"unspreadLight(): Changed block "
349 <<blockchangecount<<" times"
350 <<" for "<<from_nodes.size()<<" nodes"
353 if(unlighted_nodes.size() > 0)
354 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
358 A single-node wrapper of the above
360 void Map::unLightNeighbors(enum LightBank bank,
361 v3s16 pos, u8 lightwas,
362 core::map<v3s16, bool> & light_sources,
363 core::map<v3s16, MapBlock*> & modified_blocks)
365 core::map<v3s16, u8> from_nodes;
366 from_nodes.insert(pos, lightwas);
368 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
372 Lights neighbors of from_nodes, collects all them and then
375 void Map::spreadLight(enum LightBank bank,
376 core::map<v3s16, bool> & from_nodes,
377 core::map<v3s16, MapBlock*> & modified_blocks)
379 const v3s16 dirs[6] = {
380 v3s16(0,0,1), // back
382 v3s16(1,0,0), // right
383 v3s16(0,0,-1), // front
384 v3s16(0,-1,0), // bottom
385 v3s16(-1,0,0), // left
388 if(from_nodes.size() == 0)
391 u32 blockchangecount = 0;
393 core::map<v3s16, bool> lighted_nodes;
394 core::map<v3s16, bool>::Iterator j;
395 j = from_nodes.getIterator();
398 Initialize block cache
401 MapBlock *block = NULL;
402 // Cache this a bit, too
403 bool block_checked_in_modified = false;
405 for(; j.atEnd() == false; j++)
406 //for(; j != from_nodes.end(); j++)
408 v3s16 pos = j.getNode()->getKey();
410 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
411 v3s16 blockpos = getNodeBlockPos(pos);
413 // Only fetch a new block if the block position has changed
415 if(block == NULL || blockpos != blockpos_last){
416 block = getBlockNoCreate(blockpos);
417 blockpos_last = blockpos;
419 block_checked_in_modified = false;
423 catch(InvalidPositionException &e)
431 // Calculate relative position in block
432 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
434 // Get node straight from the block
435 MapNode n = block->getNode(relpos);
437 u8 oldlight = n.getLight(bank);
438 u8 newlight = diminish_light(oldlight);
440 // Loop through 6 neighbors
441 for(u16 i=0; i<6; i++){
442 // Get the position of the neighbor node
443 v3s16 n2pos = pos + dirs[i];
445 // Get the block where the node is located
446 v3s16 blockpos = getNodeBlockPos(n2pos);
450 // Only fetch a new block if the block position has changed
452 if(block == NULL || blockpos != blockpos_last){
453 block = getBlockNoCreate(blockpos);
454 blockpos_last = blockpos;
456 block_checked_in_modified = false;
460 catch(InvalidPositionException &e)
465 // Calculate relative position in block
466 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
467 // Get node straight from the block
468 MapNode n2 = block->getNode(relpos);
470 bool changed = false;
472 If the neighbor is brighter than the current node,
473 add to list (it will light up this node on its turn)
475 if(n2.getLight(bank) > undiminish_light(oldlight))
477 lighted_nodes.insert(n2pos, true);
478 //lighted_nodes.push_back(n2pos);
482 If the neighbor is dimmer than how much light this node
483 would spread on it, add to list
485 if(n2.getLight(bank) < newlight)
487 if(n2.light_propagates())
489 n2.setLight(bank, newlight);
490 block->setNode(relpos, n2);
491 lighted_nodes.insert(n2pos, true);
492 //lighted_nodes.push_back(n2pos);
497 // Add to modified_blocks
498 if(changed == true && block_checked_in_modified == false)
500 // If the block is not found in modified_blocks, add.
501 if(modified_blocks.find(blockpos) == NULL)
503 modified_blocks.insert(blockpos, block);
505 block_checked_in_modified = true;
508 catch(InvalidPositionException &e)
515 /*dstream<<"spreadLight(): Changed block "
516 <<blockchangecount<<" times"
517 <<" for "<<from_nodes.size()<<" nodes"
520 if(lighted_nodes.size() > 0)
521 spreadLight(bank, lighted_nodes, modified_blocks);
525 A single-node source variation of the above.
527 void Map::lightNeighbors(enum LightBank bank,
529 core::map<v3s16, MapBlock*> & modified_blocks)
531 core::map<v3s16, bool> from_nodes;
532 from_nodes.insert(pos, true);
533 spreadLight(bank, from_nodes, modified_blocks);
536 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
539 v3s16(0,0,1), // back
541 v3s16(1,0,0), // right
542 v3s16(0,0,-1), // front
543 v3s16(0,-1,0), // bottom
544 v3s16(-1,0,0), // left
547 u8 brightest_light = 0;
548 v3s16 brightest_pos(0,0,0);
549 bool found_something = false;
551 // Loop through 6 neighbors
552 for(u16 i=0; i<6; i++){
553 // Get the position of the neighbor node
554 v3s16 n2pos = p + dirs[i];
559 catch(InvalidPositionException &e)
563 if(n2.getLight(bank) > brightest_light || found_something == false){
564 brightest_light = n2.getLight(bank);
565 brightest_pos = n2pos;
566 found_something = true;
570 if(found_something == false)
571 throw InvalidPositionException();
573 return brightest_pos;
577 Propagates sunlight down from a node.
578 Starting point gets sunlight.
580 Returns the lowest y value of where the sunlight went.
582 Mud is turned into grass in where the sunlight stops.
584 s16 Map::propagateSunlight(v3s16 start,
585 core::map<v3s16, MapBlock*> & modified_blocks)
590 v3s16 pos(start.X, y, start.Z);
592 v3s16 blockpos = getNodeBlockPos(pos);
595 block = getBlockNoCreate(blockpos);
597 catch(InvalidPositionException &e)
602 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
603 MapNode n = block->getNode(relpos);
605 if(n.sunlight_propagates())
607 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
608 block->setNode(relpos, n);
610 modified_blocks.insert(blockpos, block);
614 /*// Turn mud into grass
615 if(n.d == CONTENT_MUD)
618 block->setNode(relpos, n);
619 modified_blocks.insert(blockpos, block);
622 // Sunlight goes no further
629 void Map::updateLighting(enum LightBank bank,
630 core::map<v3s16, MapBlock*> & a_blocks,
631 core::map<v3s16, MapBlock*> & modified_blocks)
633 /*m_dout<<DTIME<<"Map::updateLighting(): "
634 <<a_blocks.size()<<" blocks."<<std::endl;*/
636 //TimeTaker timer("updateLighting");
640 //u32 count_was = modified_blocks.size();
642 core::map<v3s16, MapBlock*> blocks_to_update;
644 core::map<v3s16, bool> light_sources;
646 core::map<v3s16, u8> unlight_from;
648 core::map<v3s16, MapBlock*>::Iterator i;
649 i = a_blocks.getIterator();
650 for(; i.atEnd() == false; i++)
652 MapBlock *block = i.getNode()->getValue();
656 // Don't bother with dummy blocks.
660 v3s16 pos = block->getPos();
661 modified_blocks.insert(pos, block);
663 blocks_to_update.insert(pos, block);
666 Clear all light from block
668 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
669 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
670 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
675 MapNode n = block->getNode(v3s16(x,y,z));
676 u8 oldlight = n.getLight(bank);
678 block->setNode(v3s16(x,y,z), n);
680 // Collect borders for unlighting
681 if(x==0 || x == MAP_BLOCKSIZE-1
682 || y==0 || y == MAP_BLOCKSIZE-1
683 || z==0 || z == MAP_BLOCKSIZE-1)
685 v3s16 p_map = p + v3s16(
688 MAP_BLOCKSIZE*pos.Z);
689 unlight_from.insert(p_map, oldlight);
692 catch(InvalidPositionException &e)
695 This would happen when dealing with a
699 dstream<<"updateLighting(): InvalidPositionException"
704 if(bank == LIGHTBANK_DAY)
706 bool bottom_valid = block->propagateSunlight(light_sources);
708 // If bottom is valid, we're done.
712 else if(bank == LIGHTBANK_NIGHT)
714 // For night lighting, sunlight is not propagated
719 // Invalid lighting bank
723 /*dstream<<"Bottom for sunlight-propagated block ("
724 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
727 // Bottom sunlight is not valid; get the block and loop to it
731 block = getBlockNoCreate(pos);
733 catch(InvalidPositionException &e)
743 TimeTaker timer("unspreadLight");
744 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
749 u32 diff = modified_blocks.size() - count_was;
750 count_was = modified_blocks.size();
751 dstream<<"unspreadLight modified "<<diff<<std::endl;
755 TimeTaker timer("spreadLight");
756 spreadLight(bank, light_sources, modified_blocks);
761 u32 diff = modified_blocks.size() - count_was;
762 count_was = modified_blocks.size();
763 dstream<<"spreadLight modified "<<diff<<std::endl;
768 //MapVoxelManipulator vmanip(this);
770 // Make a manual voxel manipulator and load all the blocks
771 // that touch the requested blocks
772 ManualMapVoxelManipulator vmanip(this);
773 core::map<v3s16, MapBlock*>::Iterator i;
774 i = blocks_to_update.getIterator();
775 for(; i.atEnd() == false; i++)
777 MapBlock *block = i.getNode()->getValue();
778 v3s16 p = block->getPos();
780 // Add all surrounding blocks
781 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
784 Add all surrounding blocks that have up-to-date lighting
785 NOTE: This doesn't quite do the job (not everything
786 appropriate is lighted)
788 /*for(s16 z=-1; z<=1; z++)
789 for(s16 y=-1; y<=1; y++)
790 for(s16 x=-1; x<=1; x++)
793 MapBlock *block = getBlockNoCreateNoEx(p);
798 if(block->getLightingExpired())
800 vmanip.initialEmerge(p, p);
803 // Lighting of block will be updated completely
804 block->setLightingExpired(false);
808 //TimeTaker timer("unSpreadLight");
809 vmanip.unspreadLight(bank, unlight_from, light_sources);
812 //TimeTaker timer("spreadLight");
813 vmanip.spreadLight(bank, light_sources);
816 //TimeTaker timer("blitBack");
817 vmanip.blitBack(modified_blocks);
819 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
823 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
826 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
827 core::map<v3s16, MapBlock*> & modified_blocks)
829 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
830 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
833 Update information about whether day and night light differ
835 for(core::map<v3s16, MapBlock*>::Iterator
836 i = modified_blocks.getIterator();
837 i.atEnd() == false; i++)
839 MapBlock *block = i.getNode()->getValue();
840 block->updateDayNightDiff();
846 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
847 core::map<v3s16, MapBlock*> &modified_blocks)
850 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
851 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
854 From this node to nodes underneath:
855 If lighting is sunlight (1.0), unlight neighbours and
860 v3s16 toppos = p + v3s16(0,1,0);
861 v3s16 bottompos = p + v3s16(0,-1,0);
863 bool node_under_sunlight = true;
864 core::map<v3s16, bool> light_sources;
867 If there is a node at top and it doesn't have sunlight,
868 there has not been any sunlight going down.
870 Otherwise there probably is.
873 MapNode topnode = getNode(toppos);
875 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
876 node_under_sunlight = false;
878 catch(InvalidPositionException &e)
884 If the new node is solid and there is grass below, change it to mud
886 if(content_features(n.d).walkable == true)
889 MapNode bottomnode = getNode(bottompos);
891 if(bottomnode.d == CONTENT_GRASS
892 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
894 bottomnode.d = CONTENT_MUD;
895 setNode(bottompos, bottomnode);
898 catch(InvalidPositionException &e)
906 If the new node is mud and it is under sunlight, change it
909 if(n.d == CONTENT_MUD && node_under_sunlight)
916 Remove all light that has come out of this node
919 enum LightBank banks[] =
924 for(s32 i=0; i<2; i++)
926 enum LightBank bank = banks[i];
928 u8 lightwas = getNode(p).getLight(bank);
930 // Add the block of the added node to modified_blocks
931 v3s16 blockpos = getNodeBlockPos(p);
932 MapBlock * block = getBlockNoCreate(blockpos);
933 assert(block != NULL);
934 modified_blocks.insert(blockpos, block);
936 assert(isValidPosition(p));
938 // Unlight neighbours of node.
939 // This means setting light of all consequent dimmer nodes
941 // This also collects the nodes at the border which will spread
942 // light again into this.
943 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
949 If node lets sunlight through and is under sunlight, it has
952 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
954 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
958 Set the node on the map
967 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
970 NodeMetadata *meta = meta_proto->clone();
971 setNodeMetadata(p, meta);
975 If node is under sunlight and doesn't let sunlight through,
976 take all sunlighted nodes under it and clear light from them
977 and from where the light has been spread.
978 TODO: This could be optimized by mass-unlighting instead
981 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
985 //m_dout<<DTIME<<"y="<<y<<std::endl;
986 v3s16 n2pos(p.X, y, p.Z);
992 catch(InvalidPositionException &e)
997 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
999 unLightNeighbors(LIGHTBANK_DAY,
1000 n2pos, n2.getLight(LIGHTBANK_DAY),
1001 light_sources, modified_blocks);
1002 n2.setLight(LIGHTBANK_DAY, 0);
1010 for(s32 i=0; i<2; i++)
1012 enum LightBank bank = banks[i];
1015 Spread light from all nodes that might be capable of doing so
1017 spreadLight(bank, light_sources, modified_blocks);
1021 Update information about whether day and night light differ
1023 for(core::map<v3s16, MapBlock*>::Iterator
1024 i = modified_blocks.getIterator();
1025 i.atEnd() == false; i++)
1027 MapBlock *block = i.getNode()->getValue();
1028 block->updateDayNightDiff();
1032 Add neighboring liquid nodes and the node itself if it is
1033 liquid (=water node was added) to transform queue.
1036 v3s16(0,0,0), // self
1037 v3s16(0,0,1), // back
1038 v3s16(0,1,0), // top
1039 v3s16(1,0,0), // right
1040 v3s16(0,0,-1), // front
1041 v3s16(0,-1,0), // bottom
1042 v3s16(-1,0,0), // left
1044 for(u16 i=0; i<7; i++)
1049 v3s16 p2 = p + dirs[i];
1051 MapNode n2 = getNode(p2);
1052 if(content_liquid(n2.d))
1054 m_transforming_liquid.push_back(p2);
1057 }catch(InvalidPositionException &e)
1065 void Map::removeNodeAndUpdate(v3s16 p,
1066 core::map<v3s16, MapBlock*> &modified_blocks)
1068 /*PrintInfo(m_dout);
1069 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1070 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1072 bool node_under_sunlight = true;
1074 v3s16 toppos = p + v3s16(0,1,0);
1076 // Node will be replaced with this
1077 u8 replace_material = CONTENT_AIR;
1080 If there is a node at top and it doesn't have sunlight,
1081 there will be no sunlight going down.
1084 MapNode topnode = getNode(toppos);
1086 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1087 node_under_sunlight = false;
1089 catch(InvalidPositionException &e)
1093 core::map<v3s16, bool> light_sources;
1095 enum LightBank banks[] =
1100 for(s32 i=0; i<2; i++)
1102 enum LightBank bank = banks[i];
1105 Unlight neighbors (in case the node is a light source)
1107 unLightNeighbors(bank, p,
1108 getNode(p).getLight(bank),
1109 light_sources, modified_blocks);
1113 Remove node metadata
1116 removeNodeMetadata(p);
1120 This also clears the lighting.
1124 n.d = replace_material;
1127 for(s32 i=0; i<2; i++)
1129 enum LightBank bank = banks[i];
1132 Recalculate lighting
1134 spreadLight(bank, light_sources, modified_blocks);
1137 // Add the block of the removed node to modified_blocks
1138 v3s16 blockpos = getNodeBlockPos(p);
1139 MapBlock * block = getBlockNoCreate(blockpos);
1140 assert(block != NULL);
1141 modified_blocks.insert(blockpos, block);
1144 If the removed node was under sunlight, propagate the
1145 sunlight down from it and then light all neighbors
1146 of the propagated blocks.
1148 if(node_under_sunlight)
1150 s16 ybottom = propagateSunlight(p, modified_blocks);
1151 /*m_dout<<DTIME<<"Node was under sunlight. "
1152 "Propagating sunlight";
1153 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1155 for(; y >= ybottom; y--)
1157 v3s16 p2(p.X, y, p.Z);
1158 /*m_dout<<DTIME<<"lighting neighbors of node ("
1159 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1161 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1166 // Set the lighting of this node to 0
1167 // TODO: Is this needed? Lighting is cleared up there already.
1169 MapNode n = getNode(p);
1170 n.setLight(LIGHTBANK_DAY, 0);
1173 catch(InvalidPositionException &e)
1179 for(s32 i=0; i<2; i++)
1181 enum LightBank bank = banks[i];
1183 // Get the brightest neighbour node and propagate light from it
1184 v3s16 n2p = getBrightestNeighbour(bank, p);
1186 MapNode n2 = getNode(n2p);
1187 lightNeighbors(bank, n2p, modified_blocks);
1189 catch(InvalidPositionException &e)
1195 Update information about whether day and night light differ
1197 for(core::map<v3s16, MapBlock*>::Iterator
1198 i = modified_blocks.getIterator();
1199 i.atEnd() == false; i++)
1201 MapBlock *block = i.getNode()->getValue();
1202 block->updateDayNightDiff();
1206 Add neighboring liquid nodes to transform queue.
1208 Also add horizontal neighbors of node on top of removed node
1209 because they could be affected of the water on top flowing
1210 down instead of into them.
1213 v3s16(0,0,1), // back
1214 v3s16(0,1,0), // top
1215 v3s16(1,1,0), // topright
1216 v3s16(-1,1,0), // topleft
1217 v3s16(0,1,1), // topback
1218 v3s16(0,1,-1), // topfront
1219 v3s16(1,0,0), // right
1220 v3s16(0,0,-1), // front
1221 v3s16(0,-1,0), // bottom
1222 v3s16(-1,0,0), // left
1224 for(u16 i=0; i<10; i++)
1229 v3s16 p2 = p + dirs[i];
1231 MapNode n2 = getNode(p2);
1232 if(content_liquid(n2.d))
1234 m_transforming_liquid.push_back(p2);
1237 }catch(InvalidPositionException &e)
1243 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1246 event.type = MEET_ADDNODE;
1250 bool succeeded = true;
1252 core::map<v3s16, MapBlock*> modified_blocks;
1253 addNodeAndUpdate(p, n, modified_blocks);
1255 // Copy modified_blocks to event
1256 for(core::map<v3s16, MapBlock*>::Iterator
1257 i = modified_blocks.getIterator();
1258 i.atEnd()==false; i++)
1260 event.modified_blocks.insert(i.getNode()->getKey(), false);
1263 catch(InvalidPositionException &e){
1267 dispatchEvent(&event);
1272 bool Map::removeNodeWithEvent(v3s16 p)
1275 event.type = MEET_REMOVENODE;
1278 bool succeeded = true;
1280 core::map<v3s16, MapBlock*> modified_blocks;
1281 removeNodeAndUpdate(p, modified_blocks);
1283 // Copy modified_blocks to event
1284 for(core::map<v3s16, MapBlock*>::Iterator
1285 i = modified_blocks.getIterator();
1286 i.atEnd()==false; i++)
1288 event.modified_blocks.insert(i.getNode()->getKey(), false);
1291 catch(InvalidPositionException &e){
1295 dispatchEvent(&event);
1300 bool Map::dayNightDiffed(v3s16 blockpos)
1303 v3s16 p = blockpos + v3s16(0,0,0);
1304 MapBlock *b = getBlockNoCreate(p);
1305 if(b->dayNightDiffed())
1308 catch(InvalidPositionException &e){}
1311 v3s16 p = blockpos + v3s16(-1,0,0);
1312 MapBlock *b = getBlockNoCreate(p);
1313 if(b->dayNightDiffed())
1316 catch(InvalidPositionException &e){}
1318 v3s16 p = blockpos + v3s16(0,-1,0);
1319 MapBlock *b = getBlockNoCreate(p);
1320 if(b->dayNightDiffed())
1323 catch(InvalidPositionException &e){}
1325 v3s16 p = blockpos + v3s16(0,0,-1);
1326 MapBlock *b = getBlockNoCreate(p);
1327 if(b->dayNightDiffed())
1330 catch(InvalidPositionException &e){}
1333 v3s16 p = blockpos + v3s16(1,0,0);
1334 MapBlock *b = getBlockNoCreate(p);
1335 if(b->dayNightDiffed())
1338 catch(InvalidPositionException &e){}
1340 v3s16 p = blockpos + v3s16(0,1,0);
1341 MapBlock *b = getBlockNoCreate(p);
1342 if(b->dayNightDiffed())
1345 catch(InvalidPositionException &e){}
1347 v3s16 p = blockpos + v3s16(0,0,1);
1348 MapBlock *b = getBlockNoCreate(p);
1349 if(b->dayNightDiffed())
1352 catch(InvalidPositionException &e){}
1358 Updates usage timers
1360 void Map::timerUpdate(float dtime)
1362 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1364 core::map<v2s16, MapSector*>::Iterator si;
1366 si = m_sectors.getIterator();
1367 for(; si.atEnd() == false; si++)
1369 MapSector *sector = si.getNode()->getValue();
1371 core::list<MapBlock*> blocks;
1372 sector->getBlocks(blocks);
1373 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1374 i != blocks.end(); i++)
1376 (*i)->incrementUsageTimer(dtime);
1381 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1383 core::list<v2s16>::Iterator j;
1384 for(j=list.begin(); j!=list.end(); j++)
1386 MapSector *sector = m_sectors[*j];
1389 sector->deleteBlocks();
1394 If sector is in sector cache, remove it from there
1396 if(m_sector_cache == sector)
1398 m_sector_cache = NULL;
1401 Remove from map and delete
1403 m_sectors.remove(*j);
1409 u32 Map::unloadUnusedData(float timeout, bool only_blocks,
1410 core::list<v3s16> *deleted_blocks)
1412 core::list<v2s16> sector_deletion_queue;
1414 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1415 for(; si.atEnd() == false; si++)
1417 MapSector *sector = si.getNode()->getValue();
1419 bool all_blocks_deleted = true;
1421 core::list<MapBlock*> blocks;
1422 sector->getBlocks(blocks);
1423 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1424 i != blocks.end(); i++)
1426 MapBlock *block = (*i);
1428 if(block->getUsageTimer() > timeout)
1431 if(block->getModified() != MOD_STATE_CLEAN)
1434 sector->removeBlock(block);
1439 all_blocks_deleted = false;
1443 if(all_blocks_deleted)
1445 sector_deletion_queue.push_back(si.getNode()->getKey());
1450 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1451 for(; i.atEnd() == false; i++)
1453 MapSector *sector = i.getNode()->getValue();
1455 Delete sector from memory if it hasn't been used in a long time
1457 if(sector->usage_timer > timeout)
1459 sector_deletion_queue.push_back(i.getNode()->getKey());
1461 if(deleted_blocks != NULL)
1463 // Collect positions of blocks of sector
1464 MapSector *sector = i.getNode()->getValue();
1465 core::list<MapBlock*> blocks;
1466 sector->getBlocks(blocks);
1467 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1468 i != blocks.end(); i++)
1470 deleted_blocks->push_back((*i)->getPos());
1477 deleteSectors(sector_deletion_queue, only_blocks);
1478 return sector_deletion_queue.getSize();
1481 void Map::PrintInfo(std::ostream &out)
1486 #define WATER_DROP_BOOST 4
1488 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1490 DSTACK(__FUNCTION_NAME);
1491 //TimeTaker timer("transformLiquids()");
1494 u32 initial_size = m_transforming_liquid.size();
1496 /*if(initial_size != 0)
1497 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1499 while(m_transforming_liquid.size() != 0)
1502 Get a queued transforming liquid node
1504 v3s16 p0 = m_transforming_liquid.pop_front();
1506 MapNode n0 = getNode(p0);
1508 // Don't deal with non-liquids
1509 if(content_liquid(n0.d) == false)
1512 bool is_source = !content_flowing_liquid(n0.d);
1514 u8 liquid_level = 8;
1515 if(is_source == false)
1516 liquid_level = n0.param2 & 0x0f;
1518 // Turn possible source into non-source
1519 u8 nonsource_c = make_liquid_flowing(n0.d);
1521 // Counts surrounding liquid source blocks
1522 u8 surrounding_sources = 0;
1525 If not source, check that some node flows into this one
1526 and what is the level of liquid in this one
1528 if(is_source == false)
1530 s8 new_liquid_level_max = -1;
1532 v3s16 dirs_from[5] = {
1533 v3s16(0,1,0), // top
1534 v3s16(0,0,1), // back
1535 v3s16(1,0,0), // right
1536 v3s16(0,0,-1), // front
1537 v3s16(-1,0,0), // left
1539 for(u16 i=0; i<5; i++)
1544 bool from_top = (i==0);
1546 v3s16 p2 = p0 + dirs_from[i];
1547 MapNode n2 = getNode(p2);
1549 if(content_liquid(n2.d))
1551 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1552 // Check that the liquids are the same type
1553 if(n2_nonsource_c != nonsource_c)
1555 dstream<<"WARNING: Not handling: different liquids"
1556 " collide"<<std::endl;
1559 bool n2_is_source = !content_flowing_liquid(n2.d);
1560 s8 n2_liquid_level = 8;
1562 surrounding_sources++;
1564 n2_liquid_level = n2.param2 & 0x07;
1566 s8 new_liquid_level = -1;
1569 //new_liquid_level = 7;
1570 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1571 new_liquid_level = 7;
1573 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1575 else if(n2_liquid_level > 0)
1577 // If the neighbor node isn't a source and flows downwards,
1578 // it doesn't flow into this node
1581 new_liquid_level = n2_liquid_level - 1;
1586 MapNode n3 = getNodeNoEx(p2 + v3s16(0,-1,0));
1587 // NOTE: collision of different liquids not yet handled here.
1588 if (content_features(n3.d).liquid_type != LIQUID_FLOWING)
1589 new_liquid_level = n2_liquid_level - 1;
1593 if(new_liquid_level > new_liquid_level_max)
1594 new_liquid_level_max = new_liquid_level;
1597 }catch(InvalidPositionException &e)
1603 If liquid level should be something else, update it and
1604 add all the neighboring water nodes to the transform queue.
1606 if(new_liquid_level_max != liquid_level || (!is_source && surrounding_sources >= 2))
1608 if (surrounding_sources >= 2)
1610 n0.d = content_features(n0.d).liquid_alternative_source;
1613 else if(new_liquid_level_max == -1)
1615 // Remove water alltoghether
1622 n0.param2 = new_liquid_level_max;
1623 liquid_level = new_liquid_level_max;
1627 // Block has been modified
1629 v3s16 blockpos = getNodeBlockPos(p0);
1630 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1632 modified_blocks.insert(blockpos, block);
1636 Add neighboring non-source liquid nodes to transform queue.
1639 v3s16(0,0,1), // back
1640 v3s16(0,1,0), // top
1641 v3s16(1,0,0), // right
1642 v3s16(0,0,-1), // front
1643 v3s16(0,-1,0), // bottom
1644 v3s16(-1,0,0), // left
1646 for(u16 i=0; i<6; i++)
1651 v3s16 p2 = p0 + dirs[i];
1653 MapNode n2 = getNode(p2);
1654 if(content_flowing_liquid(n2.d))
1656 m_transforming_liquid.push_back(p2);
1659 }catch(InvalidPositionException &e)
1666 // Get a new one from queue if the node has turned into non-water
1667 if(content_liquid(n0.d) == false)
1671 Flow water from this node
1673 v3s16 dirs_to[5] = {
1674 v3s16(0,-1,0), // bottom
1675 v3s16(0,0,1), // back
1676 v3s16(1,0,0), // right
1677 v3s16(0,0,-1), // front
1678 v3s16(-1,0,0), // left
1680 for(u16 i=0; i<5; i++)
1685 bool to_bottom = (i == 0);
1687 // If liquid is at lowest possible height, it's not going
1688 // anywhere except down
1689 if(liquid_level == 0 && to_bottom == false)
1692 u8 liquid_next_level = 0;
1693 // If going to bottom
1696 //liquid_next_level = 7;
1697 if(liquid_level >= 7 - WATER_DROP_BOOST)
1698 liquid_next_level = 7;
1700 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1703 liquid_next_level = liquid_level - 1;
1705 bool n2_changed = false;
1706 bool flowed = false;
1708 v3s16 p2 = p0 + dirs_to[i];
1710 MapNode n2 = getNode(p2);
1711 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1713 if(content_liquid(n2.d))
1715 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1716 // Check that the liquids are the same type
1717 if(n2_nonsource_c != nonsource_c)
1719 dstream<<"WARNING: Not handling: different liquids"
1720 " collide"<<std::endl;
1723 bool n2_is_source = !content_flowing_liquid(n2.d);
1724 u8 n2_liquid_level = 8;
1725 if(n2_is_source == false)
1726 n2_liquid_level = n2.param2 & 0x07;
1735 // Just flow into the source, nothing changes.
1736 // n2_changed is not set because destination didn't change
1741 if(liquid_next_level > n2_liquid_level)
1743 n2.param2 = liquid_next_level;
1751 else if(n2.d == CONTENT_AIR)
1754 n2.param2 = liquid_next_level;
1761 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1765 m_transforming_liquid.push_back(p2);
1767 v3s16 blockpos = getNodeBlockPos(p2);
1768 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1770 modified_blocks.insert(blockpos, block);
1773 // If n2_changed to bottom, don't flow anywhere else
1774 if(to_bottom && flowed && !is_source)
1777 }catch(InvalidPositionException &e)
1783 //if(loopcount >= 100000)
1784 if(loopcount >= initial_size * 1)
1787 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1790 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1792 v3s16 blockpos = getNodeBlockPos(p);
1793 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1794 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1797 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1801 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1805 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1807 v3s16 blockpos = getNodeBlockPos(p);
1808 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1809 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1812 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1816 block->m_node_metadata.set(p_rel, meta);
1819 void Map::removeNodeMetadata(v3s16 p)
1821 v3s16 blockpos = getNodeBlockPos(p);
1822 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1823 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1826 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1830 block->m_node_metadata.remove(p_rel);
1833 void Map::nodeMetadataStep(float dtime,
1834 core::map<v3s16, MapBlock*> &changed_blocks)
1838 Currently there is no way to ensure that all the necessary
1839 blocks are loaded when this is run. (They might get unloaded)
1840 NOTE: ^- Actually, that might not be so. In a quick test it
1841 reloaded a block with a furnace when I walked back to it from
1844 core::map<v2s16, MapSector*>::Iterator si;
1845 si = m_sectors.getIterator();
1846 for(; si.atEnd() == false; si++)
1848 MapSector *sector = si.getNode()->getValue();
1849 core::list< MapBlock * > sectorblocks;
1850 sector->getBlocks(sectorblocks);
1851 core::list< MapBlock * >::Iterator i;
1852 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1854 MapBlock *block = *i;
1855 bool changed = block->m_node_metadata.step(dtime);
1857 changed_blocks[block->getPos()] = block;
1866 ServerMap::ServerMap(std::string savedir):
1869 m_map_metadata_changed(true)
1871 dstream<<__FUNCTION_NAME<<std::endl;
1873 //m_chunksize = 8; // Takes a few seconds
1875 m_seed = (((u64)(myrand()%0xffff)<<0)
1876 + ((u64)(myrand()%0xffff)<<16)
1877 + ((u64)(myrand()%0xffff)<<32)
1878 + ((u64)(myrand()%0xffff)<<48));
1881 Experimental and debug stuff
1888 Try to load map; if not found, create a new one.
1891 m_savedir = savedir;
1892 m_map_saving_enabled = false;
1896 // If directory exists, check contents and load if possible
1897 if(fs::PathExists(m_savedir))
1899 // If directory is empty, it is safe to save into it.
1900 if(fs::GetDirListing(m_savedir).size() == 0)
1902 dstream<<DTIME<<"Server: Empty save directory is valid."
1904 m_map_saving_enabled = true;
1909 // Load map metadata (seed, chunksize)
1912 catch(FileNotGoodException &e){
1913 dstream<<DTIME<<"WARNING: Could not load map metadata"
1914 //<<" Disabling chunk-based generator."
1920 // Load chunk metadata
1923 catch(FileNotGoodException &e){
1924 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1925 <<" Disabling chunk-based generator."
1930 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1931 "metadata and sector (0,0) from "<<savedir<<
1932 ", assuming valid save directory."
1935 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1936 <<"and chunk metadata from "<<savedir
1937 <<", assuming valid save directory."
1940 m_map_saving_enabled = true;
1941 // Map loaded, not creating new one
1945 // If directory doesn't exist, it is safe to save to it
1947 m_map_saving_enabled = true;
1950 catch(std::exception &e)
1952 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1953 <<", exception: "<<e.what()<<std::endl;
1954 dstream<<"Please remove the map or fix it."<<std::endl;
1955 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1958 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1960 // Create zero sector
1961 emergeSector(v2s16(0,0));
1963 // Initially write whole map
1967 ServerMap::~ServerMap()
1969 dstream<<__FUNCTION_NAME<<std::endl;
1973 if(m_map_saving_enabled)
1976 // Save only changed parts
1978 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1982 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1985 catch(std::exception &e)
1987 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1988 <<", exception: "<<e.what()<<std::endl;
1995 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1996 for(; i.atEnd() == false; i++)
1998 MapChunk *chunk = i.getNode()->getValue();
2004 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2006 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2007 <<blockpos.Z<<")"<<std::endl;*/
2009 data->no_op = false;
2010 data->seed = m_seed;
2011 data->blockpos = blockpos;
2014 Create the whole area of this and the neighboring blocks
2017 //TimeTaker timer("initBlockMake() create area");
2019 for(s16 x=-1; x<=1; x++)
2020 for(s16 z=-1; z<=1; z++)
2022 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2023 // Sector metadata is loaded from disk if not already loaded.
2024 ServerMapSector *sector = createSector(sectorpos);
2027 for(s16 y=-1; y<=1; y++)
2029 MapBlock *block = createBlock(blockpos);
2031 // Lighting won't be calculated
2032 block->setLightingExpired(true);
2033 // Lighting will be calculated
2034 //block->setLightingExpired(false);
2037 Block gets sunlight if this is true.
2039 This should be set to true when the top side of a block
2040 is completely exposed to the sky.
2042 block->setIsUnderground(false);
2048 Now we have a big empty area.
2050 Make a ManualMapVoxelManipulator that contains this and the
2054 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2055 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2057 data->vmanip = new ManualMapVoxelManipulator(this);
2058 //data->vmanip->setMap(this);
2062 //TimeTaker timer("initBlockMake() initialEmerge");
2063 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2066 // Data is ready now.
2069 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2070 core::map<v3s16, MapBlock*> &changed_blocks)
2072 v3s16 blockpos = data->blockpos;
2073 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2074 <<blockpos.Z<<")"<<std::endl;*/
2078 dstream<<"finishBlockMake(): no-op"<<std::endl;
2082 /*dstream<<"Resulting vmanip:"<<std::endl;
2083 data->vmanip.print(dstream);*/
2086 Blit generated stuff to map
2087 NOTE: blitBackAll adds nearly everything to changed_blocks
2091 //TimeTaker timer("finishBlockMake() blitBackAll");
2092 data->vmanip->blitBackAll(&changed_blocks);
2095 dstream<<"finishBlockMake: changed_blocks.size()="
2096 <<changed_blocks.size()<<std::endl;
2099 Copy transforming liquid information
2101 while(data->transforming_liquid.size() > 0)
2103 v3s16 p = data->transforming_liquid.pop_front();
2104 m_transforming_liquid.push_back(p);
2110 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2114 Set is_underground flag for lighting with sunlight
2117 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2120 Add sunlight to central block.
2121 This makes in-dark-spawning monsters to not flood the whole thing.
2122 Do not spread the light, though.
2124 /*core::map<v3s16, bool> light_sources;
2125 bool black_air_left = false;
2126 block->propagateSunlight(light_sources, true, &black_air_left);*/
2129 NOTE: Lighting and object adding shouldn't really be here, but
2130 lighting is a bit tricky to move properly to makeBlock.
2131 TODO: Do this the right way anyway.
2138 core::map<v3s16, MapBlock*> lighting_update_blocks;
2140 lighting_update_blocks.insert(block->getPos(), block);
2142 // All modified blocks
2143 for(core::map<v3s16, MapBlock*>::Iterator
2144 i = changed_blocks.getIterator();
2145 i.atEnd() == false; i++)
2147 lighting_update_blocks.insert(i.getNode()->getKey(),
2148 i.getNode()->getValue());
2151 updateLighting(lighting_update_blocks, changed_blocks);
2154 Add random objects to block
2156 mapgen::add_random_objects(block);
2159 Go through changed blocks
2161 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2162 i.atEnd() == false; i++)
2164 MapBlock *block = i.getNode()->getValue();
2167 Update day/night difference cache of the MapBlocks
2169 block->updateDayNightDiff();
2171 Set block as modified
2173 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2177 Set central block as generated
2179 block->setGenerated(true);
2182 Save changed parts of map
2183 NOTE: Will be saved later.
2187 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2188 <<blockpos.Z<<")"<<std::endl;*/
2193 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2195 DSTACKF("%s: p2d=(%d,%d)",
2200 Check if it exists already in memory
2202 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2207 Try to load it from disk (with blocks)
2209 //if(loadSectorFull(p2d) == true)
2212 Try to load metadata from disk
2214 if(loadSectorMeta(p2d) == true)
2216 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2219 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2220 throw InvalidPositionException("");
2226 Do not create over-limit
2228 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2229 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2230 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2231 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2232 throw InvalidPositionException("createSector(): pos. over limit");
2235 Generate blank sector
2238 sector = new ServerMapSector(this, p2d);
2240 // Sector position on map in nodes
2241 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2246 m_sectors.insert(p2d, sector);
2252 This is a quick-hand function for calling makeBlock().
2254 MapBlock * ServerMap::generateBlock(
2256 core::map<v3s16, MapBlock*> &modified_blocks
2259 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2261 /*dstream<<"generateBlock(): "
2262 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2265 TimeTaker timer("generateBlock");
2267 //MapBlock *block = original_dummy;
2269 v2s16 p2d(p.X, p.Z);
2270 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2273 Do not generate over-limit
2275 if(blockpos_over_limit(p))
2277 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2278 throw InvalidPositionException("generateBlock(): pos. over limit");
2282 Create block make data
2284 mapgen::BlockMakeData data;
2285 initBlockMake(&data, p);
2291 TimeTaker t("mapgen::make_block()");
2292 mapgen::make_block(&data);
2296 Blit data back on map, update lighting, add mobs and whatever this does
2298 finishBlockMake(&data, modified_blocks);
2303 MapBlock *block = getBlockNoCreateNoEx(p);
2310 bool erroneus_content = false;
2311 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2312 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2313 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2316 MapNode n = block->getNode(p);
2317 if(n.d == CONTENT_IGNORE)
2319 dstream<<"CONTENT_IGNORE at "
2320 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2322 erroneus_content = true;
2326 if(erroneus_content)
2334 Generate a completely empty block
2336 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2337 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2339 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2345 n.d = CONTENT_STONE;
2346 block->setNode(v3s16(x0,y0,z0), n);
2354 MapBlock * ServerMap::createBlock(v3s16 p)
2356 DSTACKF("%s: p=(%d,%d,%d)",
2357 __FUNCTION_NAME, p.X, p.Y, p.Z);
2360 Do not create over-limit
2362 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2363 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2364 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2365 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2366 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2367 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2368 throw InvalidPositionException("createBlock(): pos. over limit");
2370 v2s16 p2d(p.X, p.Z);
2373 This will create or load a sector if not found in memory.
2374 If block exists on disk, it will be loaded.
2376 NOTE: On old save formats, this will be slow, as it generates
2377 lighting on blocks for them.
2379 ServerMapSector *sector;
2381 sector = (ServerMapSector*)createSector(p2d);
2382 assert(sector->getId() == MAPSECTOR_SERVER);
2384 catch(InvalidPositionException &e)
2386 dstream<<"createBlock: createSector() failed"<<std::endl;
2390 NOTE: This should not be done, or at least the exception
2391 should not be passed on as std::exception, because it
2392 won't be catched at all.
2394 /*catch(std::exception &e)
2396 dstream<<"createBlock: createSector() failed: "
2397 <<e.what()<<std::endl;
2402 Try to get a block from the sector
2405 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2408 if(block->isDummy())
2413 block = sector->createBlankBlock(block_y);
2418 MapBlock * ServerMap::emergeBlock(
2420 bool only_from_disk,
2421 core::map<v3s16, MapBlock*> &changed_blocks,
2422 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2425 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
2427 p.X, p.Y, p.Z, only_from_disk);
2429 // This has to be redone or removed
2437 Do not generate over-limit
2439 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2440 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2441 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2442 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2443 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2444 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2445 throw InvalidPositionException("emergeBlock(): pos. over limit");
2447 v2s16 p2d(p.X, p.Z);
2450 This will create or load a sector if not found in memory.
2451 If block exists on disk, it will be loaded.
2453 ServerMapSector *sector;
2455 sector = createSector(p2d);
2456 //sector = emergeSector(p2d, changed_blocks);
2458 catch(InvalidPositionException &e)
2460 dstream<<"emergeBlock: createSector() failed: "
2461 <<e.what()<<std::endl;
2462 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2464 <<"You could try to delete it."<<std::endl;
2467 catch(VersionMismatchException &e)
2469 dstream<<"emergeBlock: createSector() failed: "
2470 <<e.what()<<std::endl;
2471 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2473 <<"You could try to delete it."<<std::endl;
2478 Try to get a block from the sector
2481 bool does_not_exist = false;
2482 bool lighting_expired = false;
2483 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2485 // If not found, try loading from disk
2488 block = loadBlock(p);
2494 does_not_exist = true;
2496 else if(block->isDummy() == true)
2498 does_not_exist = true;
2500 else if(block->getLightingExpired())
2502 lighting_expired = true;
2507 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2512 If block was not found on disk and not going to generate a
2513 new one, make sure there is a dummy block in place.
2515 if(only_from_disk && (does_not_exist || lighting_expired))
2517 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2521 // Create dummy block
2522 block = new MapBlock(this, p, true);
2524 // Add block to sector
2525 sector->insertBlock(block);
2531 //dstream<<"Not found on disk, generating."<<std::endl;
2533 //TimeTaker("emergeBlock() generate");
2535 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2538 If the block doesn't exist, generate the block.
2542 block = generateBlock(p, block, sector, changed_blocks,
2543 lighting_invalidated_blocks);
2546 if(lighting_expired)
2548 lighting_invalidated_blocks.insert(p, block);
2553 Initially update sunlight
2556 core::map<v3s16, bool> light_sources;
2557 bool black_air_left = false;
2558 bool bottom_invalid =
2559 block->propagateSunlight(light_sources, true,
2562 // If sunlight didn't reach everywhere and part of block is
2563 // above ground, lighting has to be properly updated
2564 //if(black_air_left && some_part_underground)
2567 lighting_invalidated_blocks[block->getPos()] = block;
2572 lighting_invalidated_blocks[block->getPos()] = block;
2581 s16 ServerMap::findGroundLevel(v2s16 p2d)
2585 Uh, just do something random...
2587 // Find existing map from top to down
2590 v3s16 p(p2d.X, max, p2d.Y);
2591 for(; p.Y>min; p.Y--)
2593 MapNode n = getNodeNoEx(p);
2594 if(n.d != CONTENT_IGNORE)
2599 // If this node is not air, go to plan b
2600 if(getNodeNoEx(p).d != CONTENT_AIR)
2602 // Search existing walkable and return it
2603 for(; p.Y>min; p.Y--)
2605 MapNode n = getNodeNoEx(p);
2606 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2615 Determine from map generator noise functions
2618 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2621 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2622 //return (s16)level;
2625 void ServerMap::createDirs(std::string path)
2627 if(fs::CreateAllDirs(path) == false)
2629 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2630 <<"\""<<path<<"\""<<std::endl;
2631 throw BaseException("ServerMap failed to create directory");
2635 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2641 snprintf(cc, 9, "%.4x%.4x",
2642 (unsigned int)pos.X&0xffff,
2643 (unsigned int)pos.Y&0xffff);
2645 return m_savedir + "/sectors/" + cc;
2647 snprintf(cc, 9, "%.3x/%.3x",
2648 (unsigned int)pos.X&0xfff,
2649 (unsigned int)pos.Y&0xfff);
2651 return m_savedir + "/sectors2/" + cc;
2657 v2s16 ServerMap::getSectorPos(std::string dirname)
2661 size_t spos = dirname.rfind('/') + 1;
2662 assert(spos != std::string::npos);
2663 if(dirname.size() - spos == 8)
2666 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2668 else if(dirname.size() - spos == 3)
2671 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2672 // Sign-extend the 12 bit values up to 16 bits...
2673 if(x&0x800) x|=0xF000;
2674 if(y&0x800) y|=0xF000;
2681 v2s16 pos((s16)x, (s16)y);
2685 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2687 v2s16 p2d = getSectorPos(sectordir);
2689 if(blockfile.size() != 4){
2690 throw InvalidFilenameException("Invalid block filename");
2693 int r = sscanf(blockfile.c_str(), "%4x", &y);
2695 throw InvalidFilenameException("Invalid block filename");
2696 return v3s16(p2d.X, y, p2d.Y);
2699 std::string ServerMap::getBlockFilename(v3s16 p)
2702 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2706 void ServerMap::save(bool only_changed)
2708 DSTACK(__FUNCTION_NAME);
2709 if(m_map_saving_enabled == false)
2711 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2715 if(only_changed == false)
2716 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2719 if(only_changed == false || m_map_metadata_changed)
2724 u32 sector_meta_count = 0;
2725 u32 block_count = 0;
2726 u32 block_count_all = 0; // Number of blocks in memory
2728 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2729 for(; i.atEnd() == false; i++)
2731 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2732 assert(sector->getId() == MAPSECTOR_SERVER);
2734 if(sector->differs_from_disk || only_changed == false)
2736 saveSectorMeta(sector);
2737 sector_meta_count++;
2739 core::list<MapBlock*> blocks;
2740 sector->getBlocks(blocks);
2741 core::list<MapBlock*>::Iterator j;
2742 for(j=blocks.begin(); j!=blocks.end(); j++)
2744 MapBlock *block = *j;
2748 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2749 || only_changed == false)
2754 /*dstream<<"ServerMap: Written block ("
2755 <<block->getPos().X<<","
2756 <<block->getPos().Y<<","
2757 <<block->getPos().Z<<")"
2764 Only print if something happened or saved whole map
2766 if(only_changed == false || sector_meta_count != 0
2767 || block_count != 0)
2769 dstream<<DTIME<<"ServerMap: Written: "
2770 <<sector_meta_count<<" sector metadata files, "
2771 <<block_count<<" block files"
2772 <<", "<<block_count_all<<" blocks in memory."
2777 void ServerMap::saveMapMeta()
2779 DSTACK(__FUNCTION_NAME);
2781 dstream<<"INFO: ServerMap::saveMapMeta(): "
2785 createDirs(m_savedir);
2787 std::string fullpath = m_savedir + "/map_meta.txt";
2788 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2789 if(os.good() == false)
2791 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2792 <<"could not open"<<fullpath<<std::endl;
2793 throw FileNotGoodException("Cannot open chunk metadata");
2797 params.setU64("seed", m_seed);
2799 params.writeLines(os);
2801 os<<"[end_of_params]\n";
2803 m_map_metadata_changed = false;
2806 void ServerMap::loadMapMeta()
2808 DSTACK(__FUNCTION_NAME);
2810 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2813 std::string fullpath = m_savedir + "/map_meta.txt";
2814 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2815 if(is.good() == false)
2817 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2818 <<"could not open"<<fullpath<<std::endl;
2819 throw FileNotGoodException("Cannot open map metadata");
2827 throw SerializationError
2828 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2830 std::getline(is, line);
2831 std::string trimmedline = trim(line);
2832 if(trimmedline == "[end_of_params]")
2834 params.parseConfigLine(line);
2837 m_seed = params.getU64("seed");
2839 dstream<<"INFO: ServerMap::loadMapMeta(): "
2844 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2846 DSTACK(__FUNCTION_NAME);
2847 // Format used for writing
2848 u8 version = SER_FMT_VER_HIGHEST;
2850 v2s16 pos = sector->getPos();
2851 std::string dir = getSectorDir(pos);
2854 std::string fullpath = dir + "/meta";
2855 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2856 if(o.good() == false)
2857 throw FileNotGoodException("Cannot open sector metafile");
2859 sector->serialize(o, version);
2861 sector->differs_from_disk = false;
2864 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2866 DSTACK(__FUNCTION_NAME);
2868 v2s16 p2d = getSectorPos(sectordir);
2870 ServerMapSector *sector = NULL;
2872 std::string fullpath = sectordir + "/meta";
2873 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2874 if(is.good() == false)
2876 // If the directory exists anyway, it probably is in some old
2877 // format. Just go ahead and create the sector.
2878 if(fs::PathExists(sectordir))
2880 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2881 <<fullpath<<" doesn't exist but directory does."
2882 <<" Continuing with a sector with no metadata."
2884 sector = new ServerMapSector(this, p2d);
2885 m_sectors.insert(p2d, sector);
2889 throw FileNotGoodException("Cannot open sector metafile");
2894 sector = ServerMapSector::deSerialize
2895 (is, this, p2d, m_sectors);
2897 saveSectorMeta(sector);
2900 sector->differs_from_disk = false;
2905 bool ServerMap::loadSectorMeta(v2s16 p2d)
2907 DSTACK(__FUNCTION_NAME);
2909 MapSector *sector = NULL;
2911 // The directory layout we're going to load from.
2912 // 1 - original sectors/xxxxzzzz/
2913 // 2 - new sectors2/xxx/zzz/
2914 // If we load from anything but the latest structure, we will
2915 // immediately save to the new one, and remove the old.
2917 std::string sectordir1 = getSectorDir(p2d, 1);
2918 std::string sectordir;
2919 if(fs::PathExists(sectordir1))
2921 sectordir = sectordir1;
2926 sectordir = getSectorDir(p2d, 2);
2930 sector = loadSectorMeta(sectordir, loadlayout != 2);
2932 catch(InvalidFilenameException &e)
2936 catch(FileNotGoodException &e)
2940 catch(std::exception &e)
2949 bool ServerMap::loadSectorFull(v2s16 p2d)
2951 DSTACK(__FUNCTION_NAME);
2953 MapSector *sector = NULL;
2955 // The directory layout we're going to load from.
2956 // 1 - original sectors/xxxxzzzz/
2957 // 2 - new sectors2/xxx/zzz/
2958 // If we load from anything but the latest structure, we will
2959 // immediately save to the new one, and remove the old.
2961 std::string sectordir1 = getSectorDir(p2d, 1);
2962 std::string sectordir;
2963 if(fs::PathExists(sectordir1))
2965 sectordir = sectordir1;
2970 sectordir = getSectorDir(p2d, 2);
2974 sector = loadSectorMeta(sectordir, loadlayout != 2);
2976 catch(InvalidFilenameException &e)
2980 catch(FileNotGoodException &e)
2984 catch(std::exception &e)
2992 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2994 std::vector<fs::DirListNode>::iterator i2;
2995 for(i2=list2.begin(); i2!=list2.end(); i2++)
3001 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3003 catch(InvalidFilenameException &e)
3005 // This catches unknown crap in directory
3011 dstream<<"Sector converted to new layout - deleting "<<
3012 sectordir1<<std::endl;
3013 fs::RecursiveDelete(sectordir1);
3020 void ServerMap::saveBlock(MapBlock *block)
3022 DSTACK(__FUNCTION_NAME);
3024 Dummy blocks are not written
3026 if(block->isDummy())
3028 /*v3s16 p = block->getPos();
3029 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3030 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3034 // Format used for writing
3035 u8 version = SER_FMT_VER_HIGHEST;
3037 v3s16 p3d = block->getPos();
3039 v2s16 p2d(p3d.X, p3d.Z);
3040 std::string sectordir = getSectorDir(p2d);
3042 createDirs(sectordir);
3044 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3045 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3046 if(o.good() == false)
3047 throw FileNotGoodException("Cannot open block data");
3050 [0] u8 serialization version
3053 o.write((char*)&version, 1);
3056 block->serialize(o, version);
3058 // Write extra data stored on disk
3059 block->serializeDiskExtra(o, version);
3061 // We just wrote it to the disk so clear modified flag
3062 block->resetModified();
3065 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3067 DSTACK(__FUNCTION_NAME);
3069 std::string fullpath = sectordir+"/"+blockfile;
3072 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3073 if(is.good() == false)
3074 throw FileNotGoodException("Cannot open block file");
3076 v3s16 p3d = getBlockPos(sectordir, blockfile);
3077 v2s16 p2d(p3d.X, p3d.Z);
3079 assert(sector->getPos() == p2d);
3081 u8 version = SER_FMT_VER_INVALID;
3082 is.read((char*)&version, 1);
3085 throw SerializationError("ServerMap::loadBlock(): Failed"
3086 " to read MapBlock version");
3088 /*u32 block_size = MapBlock::serializedLength(version);
3089 SharedBuffer<u8> data(block_size);
3090 is.read((char*)*data, block_size);*/
3092 // This will always return a sector because we're the server
3093 //MapSector *sector = emergeSector(p2d);
3095 MapBlock *block = NULL;
3096 bool created_new = false;
3098 block = sector->getBlockNoCreate(p3d.Y);
3100 catch(InvalidPositionException &e)
3102 block = sector->createBlankBlockNoInsert(p3d.Y);
3107 block->deSerialize(is, version);
3109 // Read extra data stored on disk
3110 block->deSerializeDiskExtra(is, version);
3112 // If it's a new block, insert it to the map
3114 sector->insertBlock(block);
3117 Save blocks loaded in old format in new format
3120 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3125 // We just loaded it from the disk, so it's up-to-date.
3126 block->resetModified();
3129 catch(SerializationError &e)
3131 dstream<<"WARNING: Invalid block data on disk "
3132 <<"fullpath="<<fullpath
3133 <<" (SerializationError). "
3134 <<"what()="<<e.what()
3136 //" Ignoring. A new one will be generated.
3139 // TODO: Backup file; name is in fullpath.
3143 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3145 DSTACK(__FUNCTION_NAME);
3147 v2s16 p2d(blockpos.X, blockpos.Z);
3149 // The directory layout we're going to load from.
3150 // 1 - original sectors/xxxxzzzz/
3151 // 2 - new sectors2/xxx/zzz/
3152 // If we load from anything but the latest structure, we will
3153 // immediately save to the new one, and remove the old.
3155 std::string sectordir1 = getSectorDir(p2d, 1);
3156 std::string sectordir;
3157 if(fs::PathExists(sectordir1))
3159 sectordir = sectordir1;
3164 sectordir = getSectorDir(p2d, 2);
3168 Make sure sector is loaded
3170 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3174 sector = loadSectorMeta(sectordir, loadlayout != 2);
3176 catch(InvalidFilenameException &e)
3180 catch(FileNotGoodException &e)
3184 catch(std::exception &e)
3191 Make sure file exists
3194 std::string blockfilename = getBlockFilename(blockpos);
3195 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3201 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3202 return getBlockNoCreateNoEx(blockpos);
3205 void ServerMap::PrintInfo(std::ostream &out)
3216 ClientMap::ClientMap(
3218 MapDrawControl &control,
3219 scene::ISceneNode* parent,
3220 scene::ISceneManager* mgr,
3224 scene::ISceneNode(parent, mgr, id),
3227 m_camera_position(0,0,0),
3228 m_camera_direction(0,0,1)
3230 m_camera_mutex.Init();
3231 assert(m_camera_mutex.IsInitialized());
3233 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3234 BS*1000000,BS*1000000,BS*1000000);
3237 ClientMap::~ClientMap()
3239 /*JMutexAutoLock lock(mesh_mutex);
3248 MapSector * ClientMap::emergeSector(v2s16 p2d)
3250 DSTACK(__FUNCTION_NAME);
3251 // Check that it doesn't exist already
3253 return getSectorNoGenerate(p2d);
3255 catch(InvalidPositionException &e)
3260 ClientMapSector *sector = new ClientMapSector(this, p2d);
3263 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3264 m_sectors.insert(p2d, sector);
3270 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3272 DSTACK(__FUNCTION_NAME);
3273 ClientMapSector *sector = NULL;
3275 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3277 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3281 sector = (ClientMapSector*)n->getValue();
3282 assert(sector->getId() == MAPSECTOR_CLIENT);
3286 sector = new ClientMapSector(this, p2d);
3288 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3289 m_sectors.insert(p2d, sector);
3293 sector->deSerialize(is);
3296 void ClientMap::OnRegisterSceneNode()
3300 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3301 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3304 ISceneNode::OnRegisterSceneNode();
3307 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3309 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3310 DSTACK(__FUNCTION_NAME);
3312 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3315 This is called two times per frame, reset on the non-transparent one
3317 if(pass == scene::ESNRP_SOLID)
3319 m_last_drawn_sectors.clear();
3323 Get time for measuring timeout.
3325 Measuring time is very useful for long delays when the
3326 machine is swapping a lot.
3328 int time1 = time(0);
3330 //u32 daynight_ratio = m_client->getDayNightRatio();
3332 m_camera_mutex.Lock();
3333 v3f camera_position = m_camera_position;
3334 v3f camera_direction = m_camera_direction;
3335 m_camera_mutex.Unlock();
3338 Get all blocks and draw all visible ones
3341 v3s16 cam_pos_nodes(
3342 camera_position.X / BS,
3343 camera_position.Y / BS,
3344 camera_position.Z / BS);
3346 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3348 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3349 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3351 // Take a fair amount as we will be dropping more out later
3353 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3354 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3355 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3357 p_nodes_max.X / MAP_BLOCKSIZE,
3358 p_nodes_max.Y / MAP_BLOCKSIZE,
3359 p_nodes_max.Z / MAP_BLOCKSIZE);
3361 u32 vertex_count = 0;
3363 // For limiting number of mesh updates per frame
3364 u32 mesh_update_count = 0;
3366 u32 blocks_would_have_drawn = 0;
3367 u32 blocks_drawn = 0;
3369 int timecheck_counter = 0;
3370 core::map<v2s16, MapSector*>::Iterator si;
3371 si = m_sectors.getIterator();
3372 for(; si.atEnd() == false; si++)
3375 timecheck_counter++;
3376 if(timecheck_counter > 50)
3378 timecheck_counter = 0;
3379 int time2 = time(0);
3380 if(time2 > time1 + 4)
3382 dstream<<"ClientMap::renderMap(): "
3383 "Rendering takes ages, returning."
3390 MapSector *sector = si.getNode()->getValue();
3391 v2s16 sp = sector->getPos();
3393 if(m_control.range_all == false)
3395 if(sp.X < p_blocks_min.X
3396 || sp.X > p_blocks_max.X
3397 || sp.Y < p_blocks_min.Z
3398 || sp.Y > p_blocks_max.Z)
3402 core::list< MapBlock * > sectorblocks;
3403 sector->getBlocks(sectorblocks);
3409 u32 sector_blocks_drawn = 0;
3411 core::list< MapBlock * >::Iterator i;
3412 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3414 MapBlock *block = *i;
3417 Compare block position to camera position, skip
3418 if not seen on display
3421 float range = 100000 * BS;
3422 if(m_control.range_all == false)
3423 range = m_control.wanted_range * BS;
3426 if(isBlockInSight(block->getPos(), camera_position,
3427 camera_direction, range, &d) == false)
3432 // This is ugly (spherical distance limit?)
3433 /*if(m_control.range_all == false &&
3434 d - 0.5*BS*MAP_BLOCKSIZE > range)
3439 Update expired mesh (used for day/night change)
3441 It doesn't work exactly like it should now with the
3442 tasked mesh update but whatever.
3445 bool mesh_expired = false;
3448 JMutexAutoLock lock(block->mesh_mutex);
3450 mesh_expired = block->getMeshExpired();
3452 // Mesh has not been expired and there is no mesh:
3453 // block has no content
3454 if(block->mesh == NULL && mesh_expired == false)
3458 f32 faraway = BS*50;
3459 //f32 faraway = m_control.wanted_range * BS;
3462 This has to be done with the mesh_mutex unlocked
3464 // Pretty random but this should work somewhat nicely
3465 if(mesh_expired && (
3466 (mesh_update_count < 3
3467 && (d < faraway || mesh_update_count < 2)
3470 (m_control.range_all && mesh_update_count < 20)
3473 /*if(mesh_expired && mesh_update_count < 6
3474 && (d < faraway || mesh_update_count < 3))*/
3476 mesh_update_count++;
3478 // Mesh has been expired: generate new mesh
3479 //block->updateMesh(daynight_ratio);
3480 m_client->addUpdateMeshTask(block->getPos());
3482 mesh_expired = false;
3487 Draw the faces of the block
3490 JMutexAutoLock lock(block->mesh_mutex);
3492 scene::SMesh *mesh = block->mesh;
3497 blocks_would_have_drawn++;
3498 if(blocks_drawn >= m_control.wanted_max_blocks
3499 && m_control.range_all == false
3500 && d > m_control.wanted_min_range * BS)
3504 sector_blocks_drawn++;
3506 u32 c = mesh->getMeshBufferCount();
3508 for(u32 i=0; i<c; i++)
3510 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3511 const video::SMaterial& material = buf->getMaterial();
3512 video::IMaterialRenderer* rnd =
3513 driver->getMaterialRenderer(material.MaterialType);
3514 bool transparent = (rnd && rnd->isTransparent());
3515 // Render transparent on transparent pass and likewise.
3516 if(transparent == is_transparent_pass)
3519 This *shouldn't* hurt too much because Irrlicht
3520 doesn't change opengl textures if the old
3521 material is set again.
3523 driver->setMaterial(buf->getMaterial());
3524 driver->drawMeshBuffer(buf);
3525 vertex_count += buf->getVertexCount();
3529 } // foreach sectorblocks
3531 if(sector_blocks_drawn != 0)
3533 m_last_drawn_sectors[sp] = true;
3537 m_control.blocks_drawn = blocks_drawn;
3538 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3540 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3541 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3544 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3545 core::map<v3s16, MapBlock*> *affected_blocks)
3547 bool changed = false;
3549 Add it to all blocks touching it
3552 v3s16(0,0,0), // this
3553 v3s16(0,0,1), // back
3554 v3s16(0,1,0), // top
3555 v3s16(1,0,0), // right
3556 v3s16(0,0,-1), // front
3557 v3s16(0,-1,0), // bottom
3558 v3s16(-1,0,0), // left
3560 for(u16 i=0; i<7; i++)
3562 v3s16 p2 = p + dirs[i];
3563 // Block position of neighbor (or requested) node
3564 v3s16 blockpos = getNodeBlockPos(p2);
3565 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3566 if(blockref == NULL)
3568 // Relative position of requested node
3569 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3570 if(blockref->setTempMod(relpos, mod))
3575 if(changed && affected_blocks!=NULL)
3577 for(u16 i=0; i<7; i++)
3579 v3s16 p2 = p + dirs[i];
3580 // Block position of neighbor (or requested) node
3581 v3s16 blockpos = getNodeBlockPos(p2);
3582 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3583 if(blockref == NULL)
3585 affected_blocks->insert(blockpos, blockref);
3591 bool ClientMap::clearTempMod(v3s16 p,
3592 core::map<v3s16, MapBlock*> *affected_blocks)
3594 bool changed = false;
3596 v3s16(0,0,0), // this
3597 v3s16(0,0,1), // back
3598 v3s16(0,1,0), // top
3599 v3s16(1,0,0), // right
3600 v3s16(0,0,-1), // front
3601 v3s16(0,-1,0), // bottom
3602 v3s16(-1,0,0), // left
3604 for(u16 i=0; i<7; i++)
3606 v3s16 p2 = p + dirs[i];
3607 // Block position of neighbor (or requested) node
3608 v3s16 blockpos = getNodeBlockPos(p2);
3609 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3610 if(blockref == NULL)
3612 // Relative position of requested node
3613 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3614 if(blockref->clearTempMod(relpos))
3619 if(changed && affected_blocks!=NULL)
3621 for(u16 i=0; i<7; i++)
3623 v3s16 p2 = p + dirs[i];
3624 // Block position of neighbor (or requested) node
3625 v3s16 blockpos = getNodeBlockPos(p2);
3626 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3627 if(blockref == NULL)
3629 affected_blocks->insert(blockpos, blockref);
3635 void ClientMap::expireMeshes(bool only_daynight_diffed)
3637 TimeTaker timer("expireMeshes()");
3639 core::map<v2s16, MapSector*>::Iterator si;
3640 si = m_sectors.getIterator();
3641 for(; si.atEnd() == false; si++)
3643 MapSector *sector = si.getNode()->getValue();
3645 core::list< MapBlock * > sectorblocks;
3646 sector->getBlocks(sectorblocks);
3648 core::list< MapBlock * >::Iterator i;
3649 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3651 MapBlock *block = *i;
3653 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3659 JMutexAutoLock lock(block->mesh_mutex);
3660 if(block->mesh != NULL)
3662 /*block->mesh->drop();
3663 block->mesh = NULL;*/
3664 block->setMeshExpired(true);
3671 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3673 assert(mapType() == MAPTYPE_CLIENT);
3676 v3s16 p = blockpos + v3s16(0,0,0);
3677 MapBlock *b = getBlockNoCreate(p);
3678 b->updateMesh(daynight_ratio);
3679 //b->setMeshExpired(true);
3681 catch(InvalidPositionException &e){}
3684 v3s16 p = blockpos + v3s16(-1,0,0);
3685 MapBlock *b = getBlockNoCreate(p);
3686 b->updateMesh(daynight_ratio);
3687 //b->setMeshExpired(true);
3689 catch(InvalidPositionException &e){}
3691 v3s16 p = blockpos + v3s16(0,-1,0);
3692 MapBlock *b = getBlockNoCreate(p);
3693 b->updateMesh(daynight_ratio);
3694 //b->setMeshExpired(true);
3696 catch(InvalidPositionException &e){}
3698 v3s16 p = blockpos + v3s16(0,0,-1);
3699 MapBlock *b = getBlockNoCreate(p);
3700 b->updateMesh(daynight_ratio);
3701 //b->setMeshExpired(true);
3703 catch(InvalidPositionException &e){}
3708 Update mesh of block in which the node is, and if the node is at the
3709 leading edge, update the appropriate leading blocks too.
3711 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3719 v3s16 blockposes[4];
3720 for(u32 i=0; i<4; i++)
3722 v3s16 np = nodepos + dirs[i];
3723 blockposes[i] = getNodeBlockPos(np);
3724 // Don't update mesh of block if it has been done already
3725 bool already_updated = false;
3726 for(u32 j=0; j<i; j++)
3728 if(blockposes[j] == blockposes[i])
3730 already_updated = true;
3737 MapBlock *b = getBlockNoCreate(blockposes[i]);
3738 b->updateMesh(daynight_ratio);
3743 void ClientMap::PrintInfo(std::ostream &out)
3754 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3759 MapVoxelManipulator::~MapVoxelManipulator()
3761 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3765 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3767 TimeTaker timer1("emerge", &emerge_time);
3769 // Units of these are MapBlocks
3770 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3771 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3773 VoxelArea block_area_nodes
3774 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3776 addArea(block_area_nodes);
3778 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3779 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3780 for(s32 x=p_min.X; x<=p_max.X; x++)
3783 core::map<v3s16, bool>::Node *n;
3784 n = m_loaded_blocks.find(p);
3788 bool block_data_inexistent = false;
3791 TimeTaker timer1("emerge load", &emerge_load_time);
3793 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3794 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3797 dstream<<std::endl;*/
3799 MapBlock *block = m_map->getBlockNoCreate(p);
3800 if(block->isDummy())
3801 block_data_inexistent = true;
3803 block->copyTo(*this);
3805 catch(InvalidPositionException &e)
3807 block_data_inexistent = true;
3810 if(block_data_inexistent)
3812 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3813 // Fill with VOXELFLAG_INEXISTENT
3814 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3815 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3817 s32 i = m_area.index(a.MinEdge.X,y,z);
3818 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3822 m_loaded_blocks.insert(p, !block_data_inexistent);
3825 //dstream<<"emerge done"<<std::endl;
3829 SUGG: Add an option to only update eg. water and air nodes.
3830 This will make it interfere less with important stuff if
3833 void MapVoxelManipulator::blitBack
3834 (core::map<v3s16, MapBlock*> & modified_blocks)
3836 if(m_area.getExtent() == v3s16(0,0,0))
3839 //TimeTaker timer1("blitBack");
3841 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3842 <<m_loaded_blocks.size()<<std::endl;*/
3845 Initialize block cache
3847 v3s16 blockpos_last;
3848 MapBlock *block = NULL;
3849 bool block_checked_in_modified = false;
3851 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3852 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3853 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3857 u8 f = m_flags[m_area.index(p)];
3858 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3861 MapNode &n = m_data[m_area.index(p)];
3863 v3s16 blockpos = getNodeBlockPos(p);
3868 if(block == NULL || blockpos != blockpos_last){
3869 block = m_map->getBlockNoCreate(blockpos);
3870 blockpos_last = blockpos;
3871 block_checked_in_modified = false;
3874 // Calculate relative position in block
3875 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3877 // Don't continue if nothing has changed here
3878 if(block->getNode(relpos) == n)
3881 //m_map->setNode(m_area.MinEdge + p, n);
3882 block->setNode(relpos, n);
3885 Make sure block is in modified_blocks
3887 if(block_checked_in_modified == false)
3889 modified_blocks[blockpos] = block;
3890 block_checked_in_modified = true;
3893 catch(InvalidPositionException &e)
3899 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3900 MapVoxelManipulator(map),
3901 m_create_area(false)
3905 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3909 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3911 // Just create the area so that it can be pointed to
3912 VoxelManipulator::emerge(a, caller_id);
3915 void ManualMapVoxelManipulator::initialEmerge(
3916 v3s16 blockpos_min, v3s16 blockpos_max)
3918 TimeTaker timer1("initialEmerge", &emerge_time);
3920 // Units of these are MapBlocks
3921 v3s16 p_min = blockpos_min;
3922 v3s16 p_max = blockpos_max;
3924 VoxelArea block_area_nodes
3925 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3927 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3930 dstream<<"initialEmerge: area: ";
3931 block_area_nodes.print(dstream);
3932 dstream<<" ("<<size_MB<<"MB)";
3936 addArea(block_area_nodes);
3938 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3939 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3940 for(s32 x=p_min.X; x<=p_max.X; x++)
3943 core::map<v3s16, bool>::Node *n;
3944 n = m_loaded_blocks.find(p);
3948 bool block_data_inexistent = false;
3951 TimeTaker timer1("emerge load", &emerge_load_time);
3953 MapBlock *block = m_map->getBlockNoCreate(p);
3954 if(block->isDummy())
3955 block_data_inexistent = true;
3957 block->copyTo(*this);
3959 catch(InvalidPositionException &e)
3961 block_data_inexistent = true;
3964 if(block_data_inexistent)
3967 Mark area inexistent
3969 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3970 // Fill with VOXELFLAG_INEXISTENT
3971 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3972 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3974 s32 i = m_area.index(a.MinEdge.X,y,z);
3975 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3979 m_loaded_blocks.insert(p, !block_data_inexistent);
3983 void ManualMapVoxelManipulator::blitBackAll(
3984 core::map<v3s16, MapBlock*> * modified_blocks)
3986 if(m_area.getExtent() == v3s16(0,0,0))
3990 Copy data of all blocks
3992 for(core::map<v3s16, bool>::Iterator
3993 i = m_loaded_blocks.getIterator();
3994 i.atEnd() == false; i++)
3996 bool existed = i.getNode()->getValue();
3997 if(existed == false)
3999 v3s16 p = i.getNode()->getKey();
4000 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4003 dstream<<"WARNING: "<<__FUNCTION_NAME
4004 <<": got NULL block "
4005 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4010 block->copyFrom(*this);
4013 modified_blocks->insert(p, block);