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"
32 #include "nodemetadata.h"
33 #include "content_mapnode.h"
35 #include <IMaterialRenderer.h>
40 #include "mapnode_contentfeatures.h"
42 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
45 SQLite format specification:
46 - Initially only replaces sectors/ and sectors2/
48 If map.sqlite does not exist in the save dir
49 or the block was not found in the database
50 the map will try to load from sectors folder.
51 In either case, map.sqlite will be created
52 and all future saves will save there.
54 Structure of map.sqlite:
65 Map::Map(std::ostream &dout):
69 /*m_sector_mutex.Init();
70 assert(m_sector_mutex.IsInitialized());*/
78 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
79 for(; i.atEnd() == false; i++)
81 MapSector *sector = i.getNode()->getValue();
86 void Map::addEventReceiver(MapEventReceiver *event_receiver)
88 m_event_receivers.insert(event_receiver, false);
91 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
93 if(m_event_receivers.find(event_receiver) == NULL)
95 m_event_receivers.remove(event_receiver);
98 void Map::dispatchEvent(MapEditEvent *event)
100 for(core::map<MapEventReceiver*, bool>::Iterator
101 i = m_event_receivers.getIterator();
102 i.atEnd()==false; i++)
104 MapEventReceiver* event_receiver = i.getNode()->getKey();
105 event_receiver->onMapEditEvent(event);
109 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
111 if(m_sector_cache != NULL && p == m_sector_cache_p){
112 MapSector * sector = m_sector_cache;
116 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
121 MapSector *sector = n->getValue();
123 // Cache the last result
124 m_sector_cache_p = p;
125 m_sector_cache = sector;
130 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
132 return getSectorNoGenerateNoExNoLock(p);
135 MapSector * Map::getSectorNoGenerate(v2s16 p)
137 MapSector *sector = getSectorNoGenerateNoEx(p);
139 throw InvalidPositionException();
144 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
146 v2s16 p2d(p3d.X, p3d.Z);
147 MapSector * sector = getSectorNoGenerateNoEx(p2d);
150 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
154 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
156 MapBlock *block = getBlockNoCreateNoEx(p3d);
158 throw InvalidPositionException();
162 bool Map::isNodeUnderground(v3s16 p)
164 v3s16 blockpos = getNodeBlockPos(p);
166 MapBlock * block = getBlockNoCreate(blockpos);
167 return block->getIsUnderground();
169 catch(InvalidPositionException &e)
175 bool Map::isValidPosition(v3s16 p)
177 v3s16 blockpos = getNodeBlockPos(p);
178 MapBlock *block = getBlockNoCreate(blockpos);
179 return (block != NULL);
182 // Returns a CONTENT_IGNORE node if not found
183 MapNode Map::getNodeNoEx(v3s16 p)
185 v3s16 blockpos = getNodeBlockPos(p);
186 MapBlock *block = getBlockNoCreateNoEx(blockpos);
188 return MapNode(CONTENT_IGNORE);
189 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
190 return block->getNodeNoCheck(relpos);
193 // throws InvalidPositionException if not found
194 MapNode Map::getNode(v3s16 p)
196 v3s16 blockpos = getNodeBlockPos(p);
197 MapBlock *block = getBlockNoCreateNoEx(blockpos);
199 throw InvalidPositionException();
200 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
201 return block->getNodeNoCheck(relpos);
204 // throws InvalidPositionException if not found
205 void Map::setNode(v3s16 p, MapNode & n)
207 v3s16 blockpos = getNodeBlockPos(p);
208 MapBlock *block = getBlockNoCreate(blockpos);
209 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
210 block->setNodeNoCheck(relpos, n);
215 Goes recursively through the neighbours of the node.
217 Alters only transparent nodes.
219 If the lighting of the neighbour is lower than the lighting of
220 the node was (before changing it to 0 at the step before), the
221 lighting of the neighbour is set to 0 and then the same stuff
222 repeats for the neighbour.
224 The ending nodes of the routine are stored in light_sources.
225 This is useful when a light is removed. In such case, this
226 routine can be called for the light node and then again for
227 light_sources to re-light the area without the removed light.
229 values of from_nodes are lighting values.
231 void Map::unspreadLight(enum LightBank bank,
232 core::map<v3s16, u8> & from_nodes,
233 core::map<v3s16, bool> & light_sources,
234 core::map<v3s16, MapBlock*> & modified_blocks)
237 v3s16(0,0,1), // back
239 v3s16(1,0,0), // right
240 v3s16(0,0,-1), // front
241 v3s16(0,-1,0), // bottom
242 v3s16(-1,0,0), // left
245 if(from_nodes.size() == 0)
248 u32 blockchangecount = 0;
250 core::map<v3s16, u8> unlighted_nodes;
251 core::map<v3s16, u8>::Iterator j;
252 j = from_nodes.getIterator();
255 Initialize block cache
258 MapBlock *block = NULL;
259 // Cache this a bit, too
260 bool block_checked_in_modified = false;
262 for(; j.atEnd() == false; j++)
264 v3s16 pos = j.getNode()->getKey();
265 v3s16 blockpos = getNodeBlockPos(pos);
267 // Only fetch a new block if the block position has changed
269 if(block == NULL || blockpos != blockpos_last){
270 block = getBlockNoCreate(blockpos);
271 blockpos_last = blockpos;
273 block_checked_in_modified = false;
277 catch(InvalidPositionException &e)
285 // Calculate relative position in block
286 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
288 // Get node straight from the block
289 MapNode n = block->getNode(relpos);
291 u8 oldlight = j.getNode()->getValue();
293 // Loop through 6 neighbors
294 for(u16 i=0; i<6; i++)
296 // Get the position of the neighbor node
297 v3s16 n2pos = pos + dirs[i];
299 // Get the block where the node is located
300 v3s16 blockpos = getNodeBlockPos(n2pos);
304 // Only fetch a new block if the block position has changed
306 if(block == NULL || blockpos != blockpos_last){
307 block = getBlockNoCreate(blockpos);
308 blockpos_last = blockpos;
310 block_checked_in_modified = false;
314 catch(InvalidPositionException &e)
319 // Calculate relative position in block
320 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
321 // Get node straight from the block
322 MapNode n2 = block->getNode(relpos);
324 bool changed = false;
326 //TODO: Optimize output by optimizing light_sources?
329 If the neighbor is dimmer than what was specified
330 as oldlight (the light of the previous node)
332 if(n2.getLight(bank) < oldlight)
335 And the neighbor is transparent and it has some light
337 if(n2.light_propagates() && n2.getLight(bank) != 0)
340 Set light to 0 and add to queue
343 u8 current_light = n2.getLight(bank);
344 n2.setLight(bank, 0);
345 block->setNode(relpos, n2);
347 unlighted_nodes.insert(n2pos, current_light);
351 Remove from light_sources if it is there
352 NOTE: This doesn't happen nearly at all
354 /*if(light_sources.find(n2pos))
356 infostream<<"Removed from light_sources"<<std::endl;
357 light_sources.remove(n2pos);
362 if(light_sources.find(n2pos) != NULL)
363 light_sources.remove(n2pos);*/
366 light_sources.insert(n2pos, true);
369 // Add to modified_blocks
370 if(changed == true && block_checked_in_modified == false)
372 // If the block is not found in modified_blocks, add.
373 if(modified_blocks.find(blockpos) == NULL)
375 modified_blocks.insert(blockpos, block);
377 block_checked_in_modified = true;
380 catch(InvalidPositionException &e)
387 /*infostream<<"unspreadLight(): Changed block "
388 <<blockchangecount<<" times"
389 <<" for "<<from_nodes.size()<<" nodes"
392 if(unlighted_nodes.size() > 0)
393 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
397 A single-node wrapper of the above
399 void Map::unLightNeighbors(enum LightBank bank,
400 v3s16 pos, u8 lightwas,
401 core::map<v3s16, bool> & light_sources,
402 core::map<v3s16, MapBlock*> & modified_blocks)
404 core::map<v3s16, u8> from_nodes;
405 from_nodes.insert(pos, lightwas);
407 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
411 Lights neighbors of from_nodes, collects all them and then
414 void Map::spreadLight(enum LightBank bank,
415 core::map<v3s16, bool> & from_nodes,
416 core::map<v3s16, MapBlock*> & modified_blocks)
418 const v3s16 dirs[6] = {
419 v3s16(0,0,1), // back
421 v3s16(1,0,0), // right
422 v3s16(0,0,-1), // front
423 v3s16(0,-1,0), // bottom
424 v3s16(-1,0,0), // left
427 if(from_nodes.size() == 0)
430 u32 blockchangecount = 0;
432 core::map<v3s16, bool> lighted_nodes;
433 core::map<v3s16, bool>::Iterator j;
434 j = from_nodes.getIterator();
437 Initialize block cache
440 MapBlock *block = NULL;
441 // Cache this a bit, too
442 bool block_checked_in_modified = false;
444 for(; j.atEnd() == false; j++)
445 //for(; j != from_nodes.end(); j++)
447 v3s16 pos = j.getNode()->getKey();
449 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
450 v3s16 blockpos = getNodeBlockPos(pos);
452 // Only fetch a new block if the block position has changed
454 if(block == NULL || blockpos != blockpos_last){
455 block = getBlockNoCreate(blockpos);
456 blockpos_last = blockpos;
458 block_checked_in_modified = false;
462 catch(InvalidPositionException &e)
470 // Calculate relative position in block
471 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
473 // Get node straight from the block
474 MapNode n = block->getNode(relpos);
476 u8 oldlight = n.getLight(bank);
477 u8 newlight = diminish_light(oldlight);
479 // Loop through 6 neighbors
480 for(u16 i=0; i<6; i++){
481 // Get the position of the neighbor node
482 v3s16 n2pos = pos + dirs[i];
484 // Get the block where the node is located
485 v3s16 blockpos = getNodeBlockPos(n2pos);
489 // Only fetch a new block if the block position has changed
491 if(block == NULL || blockpos != blockpos_last){
492 block = getBlockNoCreate(blockpos);
493 blockpos_last = blockpos;
495 block_checked_in_modified = false;
499 catch(InvalidPositionException &e)
504 // Calculate relative position in block
505 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
506 // Get node straight from the block
507 MapNode n2 = block->getNode(relpos);
509 bool changed = false;
511 If the neighbor is brighter than the current node,
512 add to list (it will light up this node on its turn)
514 if(n2.getLight(bank) > undiminish_light(oldlight))
516 lighted_nodes.insert(n2pos, true);
517 //lighted_nodes.push_back(n2pos);
521 If the neighbor is dimmer than how much light this node
522 would spread on it, add to list
524 if(n2.getLight(bank) < newlight)
526 if(n2.light_propagates())
528 n2.setLight(bank, newlight);
529 block->setNode(relpos, n2);
530 lighted_nodes.insert(n2pos, true);
531 //lighted_nodes.push_back(n2pos);
536 // Add to modified_blocks
537 if(changed == true && block_checked_in_modified == false)
539 // If the block is not found in modified_blocks, add.
540 if(modified_blocks.find(blockpos) == NULL)
542 modified_blocks.insert(blockpos, block);
544 block_checked_in_modified = true;
547 catch(InvalidPositionException &e)
554 /*infostream<<"spreadLight(): Changed block "
555 <<blockchangecount<<" times"
556 <<" for "<<from_nodes.size()<<" nodes"
559 if(lighted_nodes.size() > 0)
560 spreadLight(bank, lighted_nodes, modified_blocks);
564 A single-node source variation of the above.
566 void Map::lightNeighbors(enum LightBank bank,
568 core::map<v3s16, MapBlock*> & modified_blocks)
570 core::map<v3s16, bool> from_nodes;
571 from_nodes.insert(pos, true);
572 spreadLight(bank, from_nodes, modified_blocks);
575 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
578 v3s16(0,0,1), // back
580 v3s16(1,0,0), // right
581 v3s16(0,0,-1), // front
582 v3s16(0,-1,0), // bottom
583 v3s16(-1,0,0), // left
586 u8 brightest_light = 0;
587 v3s16 brightest_pos(0,0,0);
588 bool found_something = false;
590 // Loop through 6 neighbors
591 for(u16 i=0; i<6; i++){
592 // Get the position of the neighbor node
593 v3s16 n2pos = p + dirs[i];
598 catch(InvalidPositionException &e)
602 if(n2.getLight(bank) > brightest_light || found_something == false){
603 brightest_light = n2.getLight(bank);
604 brightest_pos = n2pos;
605 found_something = true;
609 if(found_something == false)
610 throw InvalidPositionException();
612 return brightest_pos;
616 Propagates sunlight down from a node.
617 Starting point gets sunlight.
619 Returns the lowest y value of where the sunlight went.
621 Mud is turned into grass in where the sunlight stops.
623 s16 Map::propagateSunlight(v3s16 start,
624 core::map<v3s16, MapBlock*> & modified_blocks)
629 v3s16 pos(start.X, y, start.Z);
631 v3s16 blockpos = getNodeBlockPos(pos);
634 block = getBlockNoCreate(blockpos);
636 catch(InvalidPositionException &e)
641 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
642 MapNode n = block->getNode(relpos);
644 if(n.sunlight_propagates())
646 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
647 block->setNode(relpos, n);
649 modified_blocks.insert(blockpos, block);
653 /*// Turn mud into grass
654 if(n.getContent() == CONTENT_MUD)
656 n.setContent(CONTENT_GRASS);
657 block->setNode(relpos, n);
658 modified_blocks.insert(blockpos, block);
661 // Sunlight goes no further
668 void Map::updateLighting(enum LightBank bank,
669 core::map<v3s16, MapBlock*> & a_blocks,
670 core::map<v3s16, MapBlock*> & modified_blocks)
672 /*m_dout<<DTIME<<"Map::updateLighting(): "
673 <<a_blocks.size()<<" blocks."<<std::endl;*/
675 //TimeTaker timer("updateLighting");
679 //u32 count_was = modified_blocks.size();
681 core::map<v3s16, MapBlock*> blocks_to_update;
683 core::map<v3s16, bool> light_sources;
685 core::map<v3s16, u8> unlight_from;
687 core::map<v3s16, MapBlock*>::Iterator i;
688 i = a_blocks.getIterator();
689 for(; i.atEnd() == false; i++)
691 MapBlock *block = i.getNode()->getValue();
695 // Don't bother with dummy blocks.
699 v3s16 pos = block->getPos();
700 modified_blocks.insert(pos, block);
702 blocks_to_update.insert(pos, block);
705 Clear all light from block
707 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
708 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
709 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
714 MapNode n = block->getNode(v3s16(x,y,z));
715 u8 oldlight = n.getLight(bank);
717 block->setNode(v3s16(x,y,z), n);
719 // Collect borders for unlighting
720 if(x==0 || x == MAP_BLOCKSIZE-1
721 || y==0 || y == MAP_BLOCKSIZE-1
722 || z==0 || z == MAP_BLOCKSIZE-1)
724 v3s16 p_map = p + v3s16(
727 MAP_BLOCKSIZE*pos.Z);
728 unlight_from.insert(p_map, oldlight);
731 catch(InvalidPositionException &e)
734 This would happen when dealing with a
738 infostream<<"updateLighting(): InvalidPositionException"
743 if(bank == LIGHTBANK_DAY)
745 bool bottom_valid = block->propagateSunlight(light_sources);
747 // If bottom is valid, we're done.
751 else if(bank == LIGHTBANK_NIGHT)
753 // For night lighting, sunlight is not propagated
758 // Invalid lighting bank
762 /*infostream<<"Bottom for sunlight-propagated block ("
763 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
766 // Bottom sunlight is not valid; get the block and loop to it
770 block = getBlockNoCreate(pos);
772 catch(InvalidPositionException &e)
781 Enable this to disable proper lighting for speeding up map
782 generation for testing or whatever
785 //if(g_settings->get(""))
787 core::map<v3s16, MapBlock*>::Iterator i;
788 i = blocks_to_update.getIterator();
789 for(; i.atEnd() == false; i++)
791 MapBlock *block = i.getNode()->getValue();
792 v3s16 p = block->getPos();
793 block->setLightingExpired(false);
801 TimeTaker timer("unspreadLight");
802 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
807 u32 diff = modified_blocks.size() - count_was;
808 count_was = modified_blocks.size();
809 infostream<<"unspreadLight modified "<<diff<<std::endl;
813 TimeTaker timer("spreadLight");
814 spreadLight(bank, light_sources, modified_blocks);
819 u32 diff = modified_blocks.size() - count_was;
820 count_was = modified_blocks.size();
821 infostream<<"spreadLight modified "<<diff<<std::endl;
826 //MapVoxelManipulator vmanip(this);
828 // Make a manual voxel manipulator and load all the blocks
829 // that touch the requested blocks
830 ManualMapVoxelManipulator vmanip(this);
831 core::map<v3s16, MapBlock*>::Iterator i;
832 i = blocks_to_update.getIterator();
833 for(; i.atEnd() == false; i++)
835 MapBlock *block = i.getNode()->getValue();
836 v3s16 p = block->getPos();
838 // Add all surrounding blocks
839 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
842 Add all surrounding blocks that have up-to-date lighting
843 NOTE: This doesn't quite do the job (not everything
844 appropriate is lighted)
846 /*for(s16 z=-1; z<=1; z++)
847 for(s16 y=-1; y<=1; y++)
848 for(s16 x=-1; x<=1; x++)
851 MapBlock *block = getBlockNoCreateNoEx(p);
856 if(block->getLightingExpired())
858 vmanip.initialEmerge(p, p);
861 // Lighting of block will be updated completely
862 block->setLightingExpired(false);
866 //TimeTaker timer("unSpreadLight");
867 vmanip.unspreadLight(bank, unlight_from, light_sources);
870 //TimeTaker timer("spreadLight");
871 vmanip.spreadLight(bank, light_sources);
874 //TimeTaker timer("blitBack");
875 vmanip.blitBack(modified_blocks);
877 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
881 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
884 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
885 core::map<v3s16, MapBlock*> & modified_blocks)
887 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
888 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
891 Update information about whether day and night light differ
893 for(core::map<v3s16, MapBlock*>::Iterator
894 i = modified_blocks.getIterator();
895 i.atEnd() == false; i++)
897 MapBlock *block = i.getNode()->getValue();
898 block->updateDayNightDiff();
904 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
905 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
908 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
909 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
912 From this node to nodes underneath:
913 If lighting is sunlight (1.0), unlight neighbours and
918 v3s16 toppos = p + v3s16(0,1,0);
919 v3s16 bottompos = p + v3s16(0,-1,0);
921 bool node_under_sunlight = true;
922 core::map<v3s16, bool> light_sources;
925 If there is a node at top and it doesn't have sunlight,
926 there has not been any sunlight going down.
928 Otherwise there probably is.
931 MapNode topnode = getNode(toppos);
933 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
934 node_under_sunlight = false;
936 catch(InvalidPositionException &e)
942 If the new node is solid and there is grass below, change it to mud
944 if(content_features(n).walkable == true)
947 MapNode bottomnode = getNode(bottompos);
949 if(bottomnode.getContent() == CONTENT_GRASS
950 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
952 bottomnode.setContent(CONTENT_MUD);
953 setNode(bottompos, bottomnode);
956 catch(InvalidPositionException &e)
964 If the new node is mud and it is under sunlight, change it
967 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
969 n.setContent(CONTENT_GRASS);
974 Remove all light that has come out of this node
977 enum LightBank banks[] =
982 for(s32 i=0; i<2; i++)
984 enum LightBank bank = banks[i];
986 u8 lightwas = getNode(p).getLight(bank);
988 // Add the block of the added node to modified_blocks
989 v3s16 blockpos = getNodeBlockPos(p);
990 MapBlock * block = getBlockNoCreate(blockpos);
991 assert(block != NULL);
992 modified_blocks.insert(blockpos, block);
994 assert(isValidPosition(p));
996 // Unlight neighbours of node.
997 // This means setting light of all consequent dimmer nodes
999 // This also collects the nodes at the border which will spread
1000 // light again into this.
1001 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1003 n.setLight(bank, 0);
1007 If node lets sunlight through and is under sunlight, it has
1010 if(node_under_sunlight && content_features(n).sunlight_propagates)
1012 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1016 Set the node on the map
1025 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1028 NodeMetadata *meta = meta_proto->clone();
1029 meta->setOwner(player_name);
1030 setNodeMetadata(p, meta);
1034 If node is under sunlight and doesn't let sunlight through,
1035 take all sunlighted nodes under it and clear light from them
1036 and from where the light has been spread.
1037 TODO: This could be optimized by mass-unlighting instead
1040 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1044 //m_dout<<DTIME<<"y="<<y<<std::endl;
1045 v3s16 n2pos(p.X, y, p.Z);
1049 n2 = getNode(n2pos);
1051 catch(InvalidPositionException &e)
1056 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1058 unLightNeighbors(LIGHTBANK_DAY,
1059 n2pos, n2.getLight(LIGHTBANK_DAY),
1060 light_sources, modified_blocks);
1061 n2.setLight(LIGHTBANK_DAY, 0);
1069 for(s32 i=0; i<2; i++)
1071 enum LightBank bank = banks[i];
1074 Spread light from all nodes that might be capable of doing so
1076 spreadLight(bank, light_sources, modified_blocks);
1080 Update information about whether day and night light differ
1082 for(core::map<v3s16, MapBlock*>::Iterator
1083 i = modified_blocks.getIterator();
1084 i.atEnd() == false; i++)
1086 MapBlock *block = i.getNode()->getValue();
1087 block->updateDayNightDiff();
1091 Add neighboring liquid nodes and the node itself if it is
1092 liquid (=water node was added) to transform queue.
1095 v3s16(0,0,0), // self
1096 v3s16(0,0,1), // back
1097 v3s16(0,1,0), // top
1098 v3s16(1,0,0), // right
1099 v3s16(0,0,-1), // front
1100 v3s16(0,-1,0), // bottom
1101 v3s16(-1,0,0), // left
1103 for(u16 i=0; i<7; i++)
1108 v3s16 p2 = p + dirs[i];
1110 MapNode n2 = getNode(p2);
1111 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1113 m_transforming_liquid.push_back(p2);
1116 }catch(InvalidPositionException &e)
1124 void Map::removeNodeAndUpdate(v3s16 p,
1125 core::map<v3s16, MapBlock*> &modified_blocks)
1127 /*PrintInfo(m_dout);
1128 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1129 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1131 bool node_under_sunlight = true;
1133 v3s16 toppos = p + v3s16(0,1,0);
1135 // Node will be replaced with this
1136 content_t replace_material = CONTENT_AIR;
1139 If there is a node at top and it doesn't have sunlight,
1140 there will be no sunlight going down.
1143 MapNode topnode = getNode(toppos);
1145 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1146 node_under_sunlight = false;
1148 catch(InvalidPositionException &e)
1152 core::map<v3s16, bool> light_sources;
1154 enum LightBank banks[] =
1159 for(s32 i=0; i<2; i++)
1161 enum LightBank bank = banks[i];
1164 Unlight neighbors (in case the node is a light source)
1166 unLightNeighbors(bank, p,
1167 getNode(p).getLight(bank),
1168 light_sources, modified_blocks);
1172 Remove node metadata
1175 removeNodeMetadata(p);
1179 This also clears the lighting.
1183 n.setContent(replace_material);
1186 for(s32 i=0; i<2; i++)
1188 enum LightBank bank = banks[i];
1191 Recalculate lighting
1193 spreadLight(bank, light_sources, modified_blocks);
1196 // Add the block of the removed node to modified_blocks
1197 v3s16 blockpos = getNodeBlockPos(p);
1198 MapBlock * block = getBlockNoCreate(blockpos);
1199 assert(block != NULL);
1200 modified_blocks.insert(blockpos, block);
1203 If the removed node was under sunlight, propagate the
1204 sunlight down from it and then light all neighbors
1205 of the propagated blocks.
1207 if(node_under_sunlight)
1209 s16 ybottom = propagateSunlight(p, modified_blocks);
1210 /*m_dout<<DTIME<<"Node was under sunlight. "
1211 "Propagating sunlight";
1212 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1214 for(; y >= ybottom; y--)
1216 v3s16 p2(p.X, y, p.Z);
1217 /*m_dout<<DTIME<<"lighting neighbors of node ("
1218 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1220 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1225 // Set the lighting of this node to 0
1226 // TODO: Is this needed? Lighting is cleared up there already.
1228 MapNode n = getNode(p);
1229 n.setLight(LIGHTBANK_DAY, 0);
1232 catch(InvalidPositionException &e)
1238 for(s32 i=0; i<2; i++)
1240 enum LightBank bank = banks[i];
1242 // Get the brightest neighbour node and propagate light from it
1243 v3s16 n2p = getBrightestNeighbour(bank, p);
1245 MapNode n2 = getNode(n2p);
1246 lightNeighbors(bank, n2p, modified_blocks);
1248 catch(InvalidPositionException &e)
1254 Update information about whether day and night light differ
1256 for(core::map<v3s16, MapBlock*>::Iterator
1257 i = modified_blocks.getIterator();
1258 i.atEnd() == false; i++)
1260 MapBlock *block = i.getNode()->getValue();
1261 block->updateDayNightDiff();
1265 Add neighboring liquid nodes and this node to transform queue.
1266 (it's vital for the node itself to get updated last.)
1269 v3s16(0,0,1), // back
1270 v3s16(0,1,0), // top
1271 v3s16(1,0,0), // right
1272 v3s16(0,0,-1), // front
1273 v3s16(0,-1,0), // bottom
1274 v3s16(-1,0,0), // left
1275 v3s16(0,0,0), // self
1277 for(u16 i=0; i<7; i++)
1282 v3s16 p2 = p + dirs[i];
1284 MapNode n2 = getNode(p2);
1285 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1287 m_transforming_liquid.push_back(p2);
1290 }catch(InvalidPositionException &e)
1296 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1299 event.type = MEET_ADDNODE;
1303 bool succeeded = true;
1305 core::map<v3s16, MapBlock*> modified_blocks;
1306 std::string st = std::string("");
1307 addNodeAndUpdate(p, n, modified_blocks, st);
1309 // Copy modified_blocks to event
1310 for(core::map<v3s16, MapBlock*>::Iterator
1311 i = modified_blocks.getIterator();
1312 i.atEnd()==false; i++)
1314 event.modified_blocks.insert(i.getNode()->getKey(), false);
1317 catch(InvalidPositionException &e){
1321 dispatchEvent(&event);
1326 bool Map::removeNodeWithEvent(v3s16 p)
1329 event.type = MEET_REMOVENODE;
1332 bool succeeded = true;
1334 core::map<v3s16, MapBlock*> modified_blocks;
1335 removeNodeAndUpdate(p, modified_blocks);
1337 // Copy modified_blocks to event
1338 for(core::map<v3s16, MapBlock*>::Iterator
1339 i = modified_blocks.getIterator();
1340 i.atEnd()==false; i++)
1342 event.modified_blocks.insert(i.getNode()->getKey(), false);
1345 catch(InvalidPositionException &e){
1349 dispatchEvent(&event);
1354 bool Map::dayNightDiffed(v3s16 blockpos)
1357 v3s16 p = blockpos + v3s16(0,0,0);
1358 MapBlock *b = getBlockNoCreate(p);
1359 if(b->dayNightDiffed())
1362 catch(InvalidPositionException &e){}
1365 v3s16 p = blockpos + v3s16(-1,0,0);
1366 MapBlock *b = getBlockNoCreate(p);
1367 if(b->dayNightDiffed())
1370 catch(InvalidPositionException &e){}
1372 v3s16 p = blockpos + v3s16(0,-1,0);
1373 MapBlock *b = getBlockNoCreate(p);
1374 if(b->dayNightDiffed())
1377 catch(InvalidPositionException &e){}
1379 v3s16 p = blockpos + v3s16(0,0,-1);
1380 MapBlock *b = getBlockNoCreate(p);
1381 if(b->dayNightDiffed())
1384 catch(InvalidPositionException &e){}
1387 v3s16 p = blockpos + v3s16(1,0,0);
1388 MapBlock *b = getBlockNoCreate(p);
1389 if(b->dayNightDiffed())
1392 catch(InvalidPositionException &e){}
1394 v3s16 p = blockpos + v3s16(0,1,0);
1395 MapBlock *b = getBlockNoCreate(p);
1396 if(b->dayNightDiffed())
1399 catch(InvalidPositionException &e){}
1401 v3s16 p = blockpos + v3s16(0,0,1);
1402 MapBlock *b = getBlockNoCreate(p);
1403 if(b->dayNightDiffed())
1406 catch(InvalidPositionException &e){}
1412 Updates usage timers
1414 void Map::timerUpdate(float dtime, float unload_timeout,
1415 core::list<v3s16> *unloaded_blocks)
1417 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1419 core::list<v2s16> sector_deletion_queue;
1420 u32 deleted_blocks_count = 0;
1421 u32 saved_blocks_count = 0;
1423 core::map<v2s16, MapSector*>::Iterator si;
1426 si = m_sectors.getIterator();
1427 for(; si.atEnd() == false; si++)
1429 MapSector *sector = si.getNode()->getValue();
1431 bool all_blocks_deleted = true;
1433 core::list<MapBlock*> blocks;
1434 sector->getBlocks(blocks);
1436 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1437 i != blocks.end(); i++)
1439 MapBlock *block = (*i);
1441 block->incrementUsageTimer(dtime);
1443 if(block->getUsageTimer() > unload_timeout)
1445 v3s16 p = block->getPos();
1448 if(block->getModified() != MOD_STATE_CLEAN
1449 && save_before_unloading)
1452 saved_blocks_count++;
1455 // Delete from memory
1456 sector->deleteBlock(block);
1459 unloaded_blocks->push_back(p);
1461 deleted_blocks_count++;
1465 all_blocks_deleted = false;
1469 if(all_blocks_deleted)
1471 sector_deletion_queue.push_back(si.getNode()->getKey());
1476 // Finally delete the empty sectors
1477 deleteSectors(sector_deletion_queue);
1479 if(deleted_blocks_count != 0)
1481 PrintInfo(infostream); // ServerMap/ClientMap:
1482 infostream<<"Unloaded "<<deleted_blocks_count
1483 <<" blocks from memory";
1484 if(save_before_unloading)
1485 infostream<<", of which "<<saved_blocks_count<<" were written";
1486 infostream<<"."<<std::endl;
1490 void Map::deleteSectors(core::list<v2s16> &list)
1492 core::list<v2s16>::Iterator j;
1493 for(j=list.begin(); j!=list.end(); j++)
1495 MapSector *sector = m_sectors[*j];
1496 // If sector is in sector cache, remove it from there
1497 if(m_sector_cache == sector)
1498 m_sector_cache = NULL;
1499 // Remove from map and delete
1500 m_sectors.remove(*j);
1506 void Map::unloadUnusedData(float timeout,
1507 core::list<v3s16> *deleted_blocks)
1509 core::list<v2s16> sector_deletion_queue;
1510 u32 deleted_blocks_count = 0;
1511 u32 saved_blocks_count = 0;
1513 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1514 for(; si.atEnd() == false; si++)
1516 MapSector *sector = si.getNode()->getValue();
1518 bool all_blocks_deleted = true;
1520 core::list<MapBlock*> blocks;
1521 sector->getBlocks(blocks);
1522 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1523 i != blocks.end(); i++)
1525 MapBlock *block = (*i);
1527 if(block->getUsageTimer() > timeout)
1530 if(block->getModified() != MOD_STATE_CLEAN)
1533 saved_blocks_count++;
1535 // Delete from memory
1536 sector->deleteBlock(block);
1537 deleted_blocks_count++;
1541 all_blocks_deleted = false;
1545 if(all_blocks_deleted)
1547 sector_deletion_queue.push_back(si.getNode()->getKey());
1551 deleteSectors(sector_deletion_queue);
1553 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1554 <<", of which "<<saved_blocks_count<<" were wr."
1557 //return sector_deletion_queue.getSize();
1558 //return deleted_blocks_count;
1562 void Map::PrintInfo(std::ostream &out)
1567 #define WATER_DROP_BOOST 4
1571 NEIGHBOR_SAME_LEVEL,
1574 struct NodeNeighbor {
1580 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1582 DSTACK(__FUNCTION_NAME);
1583 //TimeTaker timer("transformLiquids()");
1586 u32 initial_size = m_transforming_liquid.size();
1588 /*if(initial_size != 0)
1589 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1591 // list of nodes that due to viscosity have not reached their max level height
1592 UniqueQueue<v3s16> must_reflow;
1594 // List of MapBlocks that will require a lighting update (due to lava)
1595 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1597 while(m_transforming_liquid.size() != 0)
1599 // This should be done here so that it is done when continue is used
1600 if(loopcount >= initial_size * 3)
1605 Get a queued transforming liquid node
1607 v3s16 p0 = m_transforming_liquid.pop_front();
1609 MapNode n0 = getNodeNoEx(p0);
1612 Collect information about current node
1614 s8 liquid_level = -1;
1615 u8 liquid_kind = CONTENT_IGNORE;
1616 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1617 switch (liquid_type) {
1619 liquid_level = LIQUID_LEVEL_SOURCE;
1620 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1622 case LIQUID_FLOWING:
1623 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1624 liquid_kind = n0.getContent();
1627 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1628 // continue with the next node.
1629 if (n0.getContent() != CONTENT_AIR)
1631 liquid_kind = CONTENT_AIR;
1636 Collect information about the environment
1638 const v3s16 *dirs = g_6dirs;
1639 NodeNeighbor sources[6]; // surrounding sources
1640 int num_sources = 0;
1641 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1643 NodeNeighbor airs[6]; // surrounding air
1645 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1646 int num_neutrals = 0;
1647 bool flowing_down = false;
1648 for (u16 i = 0; i < 6; i++) {
1649 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1652 nt = NEIGHBOR_UPPER;
1655 nt = NEIGHBOR_LOWER;
1658 v3s16 npos = p0 + dirs[i];
1659 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1660 switch (content_features(nb.n.getContent()).liquid_type) {
1662 if (nb.n.getContent() == CONTENT_AIR) {
1663 airs[num_airs++] = nb;
1664 // if the current node is a water source the neighbor
1665 // should be enqueded for transformation regardless of whether the
1666 // current node changes or not.
1667 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1668 m_transforming_liquid.push_back(npos);
1669 // if the current node happens to be a flowing node, it will start to flow down here.
1670 if (nb.t == NEIGHBOR_LOWER) {
1671 flowing_down = true;
1674 neutrals[num_neutrals++] = nb;
1678 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1679 if (liquid_kind == CONTENT_AIR)
1680 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1681 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1682 neutrals[num_neutrals++] = nb;
1684 sources[num_sources++] = nb;
1687 case LIQUID_FLOWING:
1688 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1689 if (liquid_kind == CONTENT_AIR)
1690 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1691 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1692 neutrals[num_neutrals++] = nb;
1694 flows[num_flows++] = nb;
1695 if (nb.t == NEIGHBOR_LOWER)
1696 flowing_down = true;
1703 decide on the type (and possibly level) of the current node
1705 content_t new_node_content;
1706 s8 new_node_level = -1;
1707 s8 max_node_level = -1;
1708 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1709 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1710 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1711 // it's perfectly safe to use liquid_kind here to determine the new node content.
1712 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1713 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1714 // liquid_kind is set properly, see above
1715 new_node_content = liquid_kind;
1716 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1718 // no surrounding sources, so get the maximum level that can flow into this node
1719 for (u16 i = 0; i < num_flows; i++) {
1720 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1721 switch (flows[i].t) {
1722 case NEIGHBOR_UPPER:
1723 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1724 max_node_level = LIQUID_LEVEL_MAX;
1725 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1726 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1727 } else if (nb_liquid_level > max_node_level)
1728 max_node_level = nb_liquid_level;
1730 case NEIGHBOR_LOWER:
1732 case NEIGHBOR_SAME_LEVEL:
1733 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1734 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1735 max_node_level = nb_liquid_level - 1;
1741 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1742 if (viscosity > 1 && max_node_level != liquid_level) {
1743 // amount to gain, limited by viscosity
1744 // must be at least 1 in absolute value
1745 s8 level_inc = max_node_level - liquid_level;
1746 if (level_inc < -viscosity || level_inc > viscosity)
1747 new_node_level = liquid_level + level_inc/viscosity;
1748 else if (level_inc < 0)
1749 new_node_level = liquid_level - 1;
1750 else if (level_inc > 0)
1751 new_node_level = liquid_level + 1;
1752 if (new_node_level != max_node_level)
1753 must_reflow.push_back(p0);
1755 new_node_level = max_node_level;
1757 if (new_node_level >= 0)
1758 new_node_content = liquid_kind;
1760 new_node_content = CONTENT_AIR;
1765 check if anything has changed. if not, just continue with the next node.
1767 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1768 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1769 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1775 update the current node
1777 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1778 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1779 // set level to last 3 bits, flowing down bit to 4th bit
1780 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1782 // set the liquid level and flow bit to 0
1783 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1785 n0.setContent(new_node_content);
1787 v3s16 blockpos = getNodeBlockPos(p0);
1788 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1790 modified_blocks.insert(blockpos, block);
1791 // If node emits light, MapBlock requires lighting update
1792 if(content_features(n0).light_source != 0)
1793 lighting_modified_blocks[block->getPos()] = block;
1797 enqueue neighbors for update if neccessary
1799 switch (content_features(n0.getContent()).liquid_type) {
1801 case LIQUID_FLOWING:
1802 // make sure source flows into all neighboring nodes
1803 for (u16 i = 0; i < num_flows; i++)
1804 if (flows[i].t != NEIGHBOR_UPPER)
1805 m_transforming_liquid.push_back(flows[i].p);
1806 for (u16 i = 0; i < num_airs; i++)
1807 if (airs[i].t != NEIGHBOR_UPPER)
1808 m_transforming_liquid.push_back(airs[i].p);
1811 // this flow has turned to air; neighboring flows might need to do the same
1812 for (u16 i = 0; i < num_flows; i++)
1813 m_transforming_liquid.push_back(flows[i].p);
1817 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1818 while (must_reflow.size() > 0)
1819 m_transforming_liquid.push_back(must_reflow.pop_front());
1820 updateLighting(lighting_modified_blocks, modified_blocks);
1823 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1825 v3s16 blockpos = getNodeBlockPos(p);
1826 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1827 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1829 infostream<<"Map::getNodeMetadata(): Need to emerge "
1830 <<PP(blockpos)<<std::endl;
1831 block = emergeBlock(blockpos, false);
1835 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1839 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1843 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1845 v3s16 blockpos = getNodeBlockPos(p);
1846 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1847 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1849 infostream<<"Map::setNodeMetadata(): Need to emerge "
1850 <<PP(blockpos)<<std::endl;
1851 block = emergeBlock(blockpos, false);
1855 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1859 block->m_node_metadata.set(p_rel, meta);
1862 void Map::removeNodeMetadata(v3s16 p)
1864 v3s16 blockpos = getNodeBlockPos(p);
1865 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1866 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1869 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1873 block->m_node_metadata.remove(p_rel);
1876 void Map::nodeMetadataStep(float dtime,
1877 core::map<v3s16, MapBlock*> &changed_blocks)
1881 Currently there is no way to ensure that all the necessary
1882 blocks are loaded when this is run. (They might get unloaded)
1883 NOTE: ^- Actually, that might not be so. In a quick test it
1884 reloaded a block with a furnace when I walked back to it from
1887 core::map<v2s16, MapSector*>::Iterator si;
1888 si = m_sectors.getIterator();
1889 for(; si.atEnd() == false; si++)
1891 MapSector *sector = si.getNode()->getValue();
1892 core::list< MapBlock * > sectorblocks;
1893 sector->getBlocks(sectorblocks);
1894 core::list< MapBlock * >::Iterator i;
1895 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1897 MapBlock *block = *i;
1898 bool changed = block->m_node_metadata.step(dtime);
1900 changed_blocks[block->getPos()] = block;
1909 ServerMap::ServerMap(std::string savedir):
1912 m_map_metadata_changed(true),
1914 m_database_read(NULL),
1915 m_database_write(NULL)
1917 infostream<<__FUNCTION_NAME<<std::endl;
1919 //m_chunksize = 8; // Takes a few seconds
1921 if (g_settings->get("fixed_map_seed").empty())
1923 m_seed = (((u64)(myrand()%0xffff)<<0)
1924 + ((u64)(myrand()%0xffff)<<16)
1925 + ((u64)(myrand()%0xffff)<<32)
1926 + ((u64)(myrand()%0xffff)<<48));
1930 m_seed = g_settings->getU64("fixed_map_seed");
1934 Experimental and debug stuff
1941 Try to load map; if not found, create a new one.
1944 m_savedir = savedir;
1945 m_map_saving_enabled = false;
1949 // If directory exists, check contents and load if possible
1950 if(fs::PathExists(m_savedir))
1952 // If directory is empty, it is safe to save into it.
1953 if(fs::GetDirListing(m_savedir).size() == 0)
1955 infostream<<"Server: Empty save directory is valid."
1957 m_map_saving_enabled = true;
1962 // Load map metadata (seed, chunksize)
1965 catch(FileNotGoodException &e){
1966 infostream<<"WARNING: Could not load map metadata"
1967 //<<" Disabling chunk-based generator."
1973 // Load chunk metadata
1976 catch(FileNotGoodException &e){
1977 infostream<<"WARNING: Could not load chunk metadata."
1978 <<" Disabling chunk-based generator."
1983 /*infostream<<"Server: Successfully loaded chunk "
1984 "metadata and sector (0,0) from "<<savedir<<
1985 ", assuming valid save directory."
1988 infostream<<"Server: Successfully loaded map "
1989 <<"and chunk metadata from "<<savedir
1990 <<", assuming valid save directory."
1993 m_map_saving_enabled = true;
1994 // Map loaded, not creating new one
1998 // If directory doesn't exist, it is safe to save to it
2000 m_map_saving_enabled = true;
2003 catch(std::exception &e)
2005 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2006 <<", exception: "<<e.what()<<std::endl;
2007 infostream<<"Please remove the map or fix it."<<std::endl;
2008 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2011 infostream<<"Initializing new map."<<std::endl;
2013 // Create zero sector
2014 emergeSector(v2s16(0,0));
2016 // Initially write whole map
2020 ServerMap::~ServerMap()
2022 infostream<<__FUNCTION_NAME<<std::endl;
2026 if(m_map_saving_enabled)
2028 // Save only changed parts
2030 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2034 infostream<<"Server: map not saved"<<std::endl;
2037 catch(std::exception &e)
2039 infostream<<"Server: Failed to save map to "<<m_savedir
2040 <<", exception: "<<e.what()<<std::endl;
2044 Close database if it was opened
2047 sqlite3_finalize(m_database_read);
2048 if(m_database_write)
2049 sqlite3_finalize(m_database_write);
2051 sqlite3_close(m_database);
2057 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2058 for(; i.atEnd() == false; i++)
2060 MapChunk *chunk = i.getNode()->getValue();
2066 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2068 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2069 if(enable_mapgen_debug_info)
2070 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2071 <<blockpos.Z<<")"<<std::endl;
2073 // Do nothing if not inside limits (+-1 because of neighbors)
2074 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2075 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2081 data->no_op = false;
2082 data->seed = m_seed;
2083 data->blockpos = blockpos;
2086 Create the whole area of this and the neighboring blocks
2089 //TimeTaker timer("initBlockMake() create area");
2091 for(s16 x=-1; x<=1; x++)
2092 for(s16 z=-1; z<=1; z++)
2094 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2095 // Sector metadata is loaded from disk if not already loaded.
2096 ServerMapSector *sector = createSector(sectorpos);
2099 for(s16 y=-1; y<=1; y++)
2101 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2102 //MapBlock *block = createBlock(p);
2103 // 1) get from memory, 2) load from disk
2104 MapBlock *block = emergeBlock(p, false);
2105 // 3) create a blank one
2108 block = createBlock(p);
2111 Block gets sunlight if this is true.
2113 Refer to the map generator heuristics.
2115 bool ug = mapgen::block_is_underground(data->seed, p);
2116 block->setIsUnderground(ug);
2119 // Lighting will not be valid after make_chunk is called
2120 block->setLightingExpired(true);
2121 // Lighting will be calculated
2122 //block->setLightingExpired(false);
2128 Now we have a big empty area.
2130 Make a ManualMapVoxelManipulator that contains this and the
2134 // The area that contains this block and it's neighbors
2135 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2136 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2138 data->vmanip = new ManualMapVoxelManipulator(this);
2139 //data->vmanip->setMap(this);
2143 //TimeTaker timer("initBlockMake() initialEmerge");
2144 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2147 // Data is ready now.
2150 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2151 core::map<v3s16, MapBlock*> &changed_blocks)
2153 v3s16 blockpos = data->blockpos;
2154 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2155 <<blockpos.Z<<")"<<std::endl;*/
2159 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2163 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2165 /*infostream<<"Resulting vmanip:"<<std::endl;
2166 data->vmanip.print(infostream);*/
2169 Blit generated stuff to map
2170 NOTE: blitBackAll adds nearly everything to changed_blocks
2174 //TimeTaker timer("finishBlockMake() blitBackAll");
2175 data->vmanip->blitBackAll(&changed_blocks);
2178 if(enable_mapgen_debug_info)
2179 infostream<<"finishBlockMake: changed_blocks.size()="
2180 <<changed_blocks.size()<<std::endl;
2183 Copy transforming liquid information
2185 while(data->transforming_liquid.size() > 0)
2187 v3s16 p = data->transforming_liquid.pop_front();
2188 m_transforming_liquid.push_back(p);
2194 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2198 Set is_underground flag for lighting with sunlight.
2200 Refer to map generator heuristics.
2202 NOTE: This is done in initChunkMake
2204 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2208 Add sunlight to central block.
2209 This makes in-dark-spawning monsters to not flood the whole thing.
2210 Do not spread the light, though.
2212 /*core::map<v3s16, bool> light_sources;
2213 bool black_air_left = false;
2214 block->propagateSunlight(light_sources, true, &black_air_left);*/
2217 NOTE: Lighting and object adding shouldn't really be here, but
2218 lighting is a bit tricky to move properly to makeBlock.
2219 TODO: Do this the right way anyway, that is, move it to makeBlock.
2220 - There needs to be some way for makeBlock to report back if
2221 the lighting update is going further down because of the
2222 new block blocking light
2227 NOTE: This takes ~60ms, TODO: Investigate why
2230 TimeTaker t("finishBlockMake lighting update");
2232 core::map<v3s16, MapBlock*> lighting_update_blocks;
2235 lighting_update_blocks.insert(block->getPos(), block);
2240 v3s16 p = block->getPos()+v3s16(x,1,z);
2241 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2245 // All modified blocks
2246 // NOTE: Should this be done? If this is not done, then the lighting
2247 // of the others will be updated in a different place, one by one, i
2248 // think... or they might not? Well, at least they are left marked as
2249 // "lighting expired"; it seems that is not handled at all anywhere,
2250 // so enabling this will slow it down A LOT because otherwise it
2251 // would not do this at all. This causes the black trees.
2252 for(core::map<v3s16, MapBlock*>::Iterator
2253 i = changed_blocks.getIterator();
2254 i.atEnd() == false; i++)
2256 lighting_update_blocks.insert(i.getNode()->getKey(),
2257 i.getNode()->getValue());
2259 /*// Also force-add all the upmost blocks for proper sunlight
2260 for(s16 x=-1; x<=1; x++)
2261 for(s16 z=-1; z<=1; z++)
2263 v3s16 p = block->getPos()+v3s16(x,1,z);
2264 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2267 updateLighting(lighting_update_blocks, changed_blocks);
2270 Set lighting to non-expired state in all of them.
2271 This is cheating, but it is not fast enough if all of them
2272 would actually be updated.
2274 for(s16 x=-1; x<=1; x++)
2275 for(s16 y=-1; y<=1; y++)
2276 for(s16 z=-1; z<=1; z++)
2278 v3s16 p = block->getPos()+v3s16(x,y,z);
2279 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2282 if(enable_mapgen_debug_info == false)
2283 t.stop(true); // Hide output
2287 Add random objects to block
2289 mapgen::add_random_objects(block);
2292 Go through changed blocks
2294 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2295 i.atEnd() == false; i++)
2297 MapBlock *block = i.getNode()->getValue();
2300 Update day/night difference cache of the MapBlocks
2302 block->updateDayNightDiff();
2304 Set block as modified
2306 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2310 Set central block as generated
2312 block->setGenerated(true);
2315 Save changed parts of map
2316 NOTE: Will be saved later.
2320 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2321 <<blockpos.Z<<")"<<std::endl;*/
2323 if(enable_mapgen_debug_info)
2326 Analyze resulting blocks
2328 for(s16 x=-1; x<=1; x++)
2329 for(s16 y=-1; y<=1; y++)
2330 for(s16 z=-1; z<=1; z++)
2332 v3s16 p = block->getPos()+v3s16(x,y,z);
2333 MapBlock *block = getBlockNoCreateNoEx(p);
2335 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2336 infostream<<"Generated "<<spos<<": "
2337 <<analyze_block(block)<<std::endl;
2345 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2347 DSTACKF("%s: p2d=(%d,%d)",
2352 Check if it exists already in memory
2354 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2359 Try to load it from disk (with blocks)
2361 //if(loadSectorFull(p2d) == true)
2364 Try to load metadata from disk
2367 if(loadSectorMeta(p2d) == true)
2369 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2372 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2373 throw InvalidPositionException("");
2379 Do not create over-limit
2381 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2382 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2383 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2384 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2385 throw InvalidPositionException("createSector(): pos. over limit");
2388 Generate blank sector
2391 sector = new ServerMapSector(this, p2d);
2393 // Sector position on map in nodes
2394 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2399 m_sectors.insert(p2d, sector);
2405 This is a quick-hand function for calling makeBlock().
2407 MapBlock * ServerMap::generateBlock(
2409 core::map<v3s16, MapBlock*> &modified_blocks
2412 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2414 /*infostream<<"generateBlock(): "
2415 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2418 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2420 TimeTaker timer("generateBlock");
2422 //MapBlock *block = original_dummy;
2424 v2s16 p2d(p.X, p.Z);
2425 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2428 Do not generate over-limit
2430 if(blockpos_over_limit(p))
2432 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2433 throw InvalidPositionException("generateBlock(): pos. over limit");
2437 Create block make data
2439 mapgen::BlockMakeData data;
2440 initBlockMake(&data, p);
2446 TimeTaker t("mapgen::make_block()");
2447 mapgen::make_block(&data);
2449 if(enable_mapgen_debug_info == false)
2450 t.stop(true); // Hide output
2454 Blit data back on map, update lighting, add mobs and whatever this does
2456 finishBlockMake(&data, modified_blocks);
2461 MapBlock *block = getBlockNoCreateNoEx(p);
2469 bool erroneus_content = false;
2470 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2471 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2472 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2475 MapNode n = block->getNode(p);
2476 if(n.getContent() == CONTENT_IGNORE)
2478 infostream<<"CONTENT_IGNORE at "
2479 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2481 erroneus_content = true;
2485 if(erroneus_content)
2494 Generate a completely empty block
2498 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2499 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2501 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2505 n.setContent(CONTENT_AIR);
2507 n.setContent(CONTENT_STONE);
2508 block->setNode(v3s16(x0,y0,z0), n);
2514 if(enable_mapgen_debug_info == false)
2515 timer.stop(true); // Hide output
2520 MapBlock * ServerMap::createBlock(v3s16 p)
2522 DSTACKF("%s: p=(%d,%d,%d)",
2523 __FUNCTION_NAME, p.X, p.Y, p.Z);
2526 Do not create over-limit
2528 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2529 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2530 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2531 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2532 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2533 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2534 throw InvalidPositionException("createBlock(): pos. over limit");
2536 v2s16 p2d(p.X, p.Z);
2539 This will create or load a sector if not found in memory.
2540 If block exists on disk, it will be loaded.
2542 NOTE: On old save formats, this will be slow, as it generates
2543 lighting on blocks for them.
2545 ServerMapSector *sector;
2547 sector = (ServerMapSector*)createSector(p2d);
2548 assert(sector->getId() == MAPSECTOR_SERVER);
2550 catch(InvalidPositionException &e)
2552 infostream<<"createBlock: createSector() failed"<<std::endl;
2556 NOTE: This should not be done, or at least the exception
2557 should not be passed on as std::exception, because it
2558 won't be catched at all.
2560 /*catch(std::exception &e)
2562 infostream<<"createBlock: createSector() failed: "
2563 <<e.what()<<std::endl;
2568 Try to get a block from the sector
2571 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2574 if(block->isDummy())
2579 block = sector->createBlankBlock(block_y);
2583 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2585 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2587 p.X, p.Y, p.Z, allow_generate);
2590 MapBlock *block = getBlockNoCreateNoEx(p);
2591 if(block && block->isDummy() == false)
2596 MapBlock *block = loadBlock(p);
2603 core::map<v3s16, MapBlock*> modified_blocks;
2604 MapBlock *block = generateBlock(p, modified_blocks);
2608 event.type = MEET_OTHER;
2611 // Copy modified_blocks to event
2612 for(core::map<v3s16, MapBlock*>::Iterator
2613 i = modified_blocks.getIterator();
2614 i.atEnd()==false; i++)
2616 event.modified_blocks.insert(i.getNode()->getKey(), false);
2620 dispatchEvent(&event);
2629 s16 ServerMap::findGroundLevel(v2s16 p2d)
2633 Uh, just do something random...
2635 // Find existing map from top to down
2638 v3s16 p(p2d.X, max, p2d.Y);
2639 for(; p.Y>min; p.Y--)
2641 MapNode n = getNodeNoEx(p);
2642 if(n.getContent() != CONTENT_IGNORE)
2647 // If this node is not air, go to plan b
2648 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2650 // Search existing walkable and return it
2651 for(; p.Y>min; p.Y--)
2653 MapNode n = getNodeNoEx(p);
2654 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2663 Determine from map generator noise functions
2666 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2669 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2670 //return (s16)level;
2673 void ServerMap::createDatabase() {
2676 e = sqlite3_exec(m_database,
2677 "CREATE TABLE IF NOT EXISTS `blocks` ("
2678 "`pos` INT NOT NULL PRIMARY KEY,"
2681 , NULL, NULL, NULL);
2682 if(e == SQLITE_ABORT)
2683 throw FileNotGoodException("Could not create database structure");
2685 infostream<<"Server: Database structure was created";
2688 void ServerMap::verifyDatabase() {
2693 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2694 bool needs_create = false;
2698 Open the database connection
2701 createDirs(m_savedir);
2703 if(!fs::PathExists(dbp))
2704 needs_create = true;
2706 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2707 if(d != SQLITE_OK) {
2708 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2709 throw FileNotGoodException("Cannot open database file");
2715 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2716 if(d != SQLITE_OK) {
2717 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2718 throw FileNotGoodException("Cannot prepare read statement");
2721 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2722 if(d != SQLITE_OK) {
2723 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2724 throw FileNotGoodException("Cannot prepare write statement");
2727 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2728 if(d != SQLITE_OK) {
2729 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2730 throw FileNotGoodException("Cannot prepare read statement");
2733 infostream<<"Server: Database opened"<<std::endl;
2737 bool ServerMap::loadFromFolders() {
2738 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2743 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2744 return (sqlite3_int64)pos.Z*16777216 +
2745 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2748 void ServerMap::createDirs(std::string path)
2750 if(fs::CreateAllDirs(path) == false)
2752 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2753 <<"\""<<path<<"\""<<std::endl;
2754 throw BaseException("ServerMap failed to create directory");
2758 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2764 snprintf(cc, 9, "%.4x%.4x",
2765 (unsigned int)pos.X&0xffff,
2766 (unsigned int)pos.Y&0xffff);
2768 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2770 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2771 (unsigned int)pos.X&0xfff,
2772 (unsigned int)pos.Y&0xfff);
2774 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2780 v2s16 ServerMap::getSectorPos(std::string dirname)
2784 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2785 assert(spos != std::string::npos);
2786 if(dirname.size() - spos == 8)
2789 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2791 else if(dirname.size() - spos == 3)
2794 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2795 // Sign-extend the 12 bit values up to 16 bits...
2796 if(x&0x800) x|=0xF000;
2797 if(y&0x800) y|=0xF000;
2804 v2s16 pos((s16)x, (s16)y);
2808 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2810 v2s16 p2d = getSectorPos(sectordir);
2812 if(blockfile.size() != 4){
2813 throw InvalidFilenameException("Invalid block filename");
2816 int r = sscanf(blockfile.c_str(), "%4x", &y);
2818 throw InvalidFilenameException("Invalid block filename");
2819 return v3s16(p2d.X, y, p2d.Y);
2822 std::string ServerMap::getBlockFilename(v3s16 p)
2825 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2829 void ServerMap::save(bool only_changed)
2831 DSTACK(__FUNCTION_NAME);
2832 if(m_map_saving_enabled == false)
2834 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2838 if(only_changed == false)
2839 infostream<<"ServerMap: Saving whole map, this can take time."
2842 if(only_changed == false || m_map_metadata_changed)
2847 u32 sector_meta_count = 0;
2848 u32 block_count = 0;
2849 u32 block_count_all = 0; // Number of blocks in memory
2852 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2853 for(; i.atEnd() == false; i++)
2855 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2856 assert(sector->getId() == MAPSECTOR_SERVER);
2858 if(sector->differs_from_disk || only_changed == false)
2860 saveSectorMeta(sector);
2861 sector_meta_count++;
2863 core::list<MapBlock*> blocks;
2864 sector->getBlocks(blocks);
2865 core::list<MapBlock*>::Iterator j;
2867 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2868 for(j=blocks.begin(); j!=blocks.end(); j++)
2870 MapBlock *block = *j;
2874 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2875 || only_changed == false)
2880 /*infostream<<"ServerMap: Written block ("
2881 <<block->getPos().X<<","
2882 <<block->getPos().Y<<","
2883 <<block->getPos().Z<<")"
2886 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
2892 Only print if something happened or saved whole map
2894 if(only_changed == false || sector_meta_count != 0
2895 || block_count != 0)
2897 infostream<<"ServerMap: Written: "
2898 <<sector_meta_count<<" sector metadata files, "
2899 <<block_count<<" block files"
2900 <<", "<<block_count_all<<" blocks in memory."
2905 static s32 unsignedToSigned(s32 i, s32 max_positive)
2907 if(i < max_positive)
2910 return i - 2*max_positive;
2913 // modulo of a negative number does not work consistently in C
2914 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2918 return mod - ((-i) % mod);
2921 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2923 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2925 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2927 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2928 return v3s16(x,y,z);
2931 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2933 if(loadFromFolders()){
2934 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2935 <<"all blocks that are stored in flat files"<<std::endl;
2941 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2943 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2944 v3s16 p = getIntegerAsBlock(block_i);
2945 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2951 void ServerMap::saveMapMeta()
2953 DSTACK(__FUNCTION_NAME);
2955 infostream<<"ServerMap::saveMapMeta(): "
2959 createDirs(m_savedir);
2961 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2962 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2963 if(os.good() == false)
2965 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2966 <<"could not open"<<fullpath<<std::endl;
2967 throw FileNotGoodException("Cannot open chunk metadata");
2971 params.setU64("seed", m_seed);
2973 params.writeLines(os);
2975 os<<"[end_of_params]\n";
2977 m_map_metadata_changed = false;
2980 void ServerMap::loadMapMeta()
2982 DSTACK(__FUNCTION_NAME);
2984 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2987 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2988 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2989 if(is.good() == false)
2991 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2992 <<"could not open"<<fullpath<<std::endl;
2993 throw FileNotGoodException("Cannot open map metadata");
3001 throw SerializationError
3002 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3004 std::getline(is, line);
3005 std::string trimmedline = trim(line);
3006 if(trimmedline == "[end_of_params]")
3008 params.parseConfigLine(line);
3011 m_seed = params.getU64("seed");
3013 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3016 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3018 DSTACK(__FUNCTION_NAME);
3019 // Format used for writing
3020 u8 version = SER_FMT_VER_HIGHEST;
3022 v2s16 pos = sector->getPos();
3023 std::string dir = getSectorDir(pos);
3026 std::string fullpath = dir + DIR_DELIM + "meta";
3027 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3028 if(o.good() == false)
3029 throw FileNotGoodException("Cannot open sector metafile");
3031 sector->serialize(o, version);
3033 sector->differs_from_disk = false;
3036 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3038 DSTACK(__FUNCTION_NAME);
3040 v2s16 p2d = getSectorPos(sectordir);
3042 ServerMapSector *sector = NULL;
3044 std::string fullpath = sectordir + DIR_DELIM + "meta";
3045 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3046 if(is.good() == false)
3048 // If the directory exists anyway, it probably is in some old
3049 // format. Just go ahead and create the sector.
3050 if(fs::PathExists(sectordir))
3052 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3053 <<fullpath<<" doesn't exist but directory does."
3054 <<" Continuing with a sector with no metadata."
3056 sector = new ServerMapSector(this, p2d);
3057 m_sectors.insert(p2d, sector);
3061 throw FileNotGoodException("Cannot open sector metafile");
3066 sector = ServerMapSector::deSerialize
3067 (is, this, p2d, m_sectors);
3069 saveSectorMeta(sector);
3072 sector->differs_from_disk = false;
3077 bool ServerMap::loadSectorMeta(v2s16 p2d)
3079 DSTACK(__FUNCTION_NAME);
3081 MapSector *sector = NULL;
3083 // The directory layout we're going to load from.
3084 // 1 - original sectors/xxxxzzzz/
3085 // 2 - new sectors2/xxx/zzz/
3086 // If we load from anything but the latest structure, we will
3087 // immediately save to the new one, and remove the old.
3089 std::string sectordir1 = getSectorDir(p2d, 1);
3090 std::string sectordir;
3091 if(fs::PathExists(sectordir1))
3093 sectordir = sectordir1;
3098 sectordir = getSectorDir(p2d, 2);
3102 sector = loadSectorMeta(sectordir, loadlayout != 2);
3104 catch(InvalidFilenameException &e)
3108 catch(FileNotGoodException &e)
3112 catch(std::exception &e)
3121 bool ServerMap::loadSectorFull(v2s16 p2d)
3123 DSTACK(__FUNCTION_NAME);
3125 MapSector *sector = NULL;
3127 // The directory layout we're going to load from.
3128 // 1 - original sectors/xxxxzzzz/
3129 // 2 - new sectors2/xxx/zzz/
3130 // If we load from anything but the latest structure, we will
3131 // immediately save to the new one, and remove the old.
3133 std::string sectordir1 = getSectorDir(p2d, 1);
3134 std::string sectordir;
3135 if(fs::PathExists(sectordir1))
3137 sectordir = sectordir1;
3142 sectordir = getSectorDir(p2d, 2);
3146 sector = loadSectorMeta(sectordir, loadlayout != 2);
3148 catch(InvalidFilenameException &e)
3152 catch(FileNotGoodException &e)
3156 catch(std::exception &e)
3164 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3166 std::vector<fs::DirListNode>::iterator i2;
3167 for(i2=list2.begin(); i2!=list2.end(); i2++)
3173 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3175 catch(InvalidFilenameException &e)
3177 // This catches unknown crap in directory
3183 infostream<<"Sector converted to new layout - deleting "<<
3184 sectordir1<<std::endl;
3185 fs::RecursiveDelete(sectordir1);
3192 void ServerMap::beginSave() {
3194 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3195 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3198 void ServerMap::endSave() {
3200 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3201 infostream<<"WARNING: endSave() failed, map might not have saved.";
3204 void ServerMap::saveBlock(MapBlock *block)
3206 DSTACK(__FUNCTION_NAME);
3208 Dummy blocks are not written
3210 if(block->isDummy())
3212 /*v3s16 p = block->getPos();
3213 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3214 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3218 // Format used for writing
3219 u8 version = SER_FMT_VER_HIGHEST;
3221 v3s16 p3d = block->getPos();
3225 v2s16 p2d(p3d.X, p3d.Z);
3226 std::string sectordir = getSectorDir(p2d);
3228 createDirs(sectordir);
3230 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3231 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3232 if(o.good() == false)
3233 throw FileNotGoodException("Cannot open block data");
3236 [0] u8 serialization version
3242 std::ostringstream o(std::ios_base::binary);
3244 o.write((char*)&version, 1);
3247 block->serialize(o, version);
3249 // Write extra data stored on disk
3250 block->serializeDiskExtra(o, version);
3252 // Write block to database
3254 std::string tmp = o.str();
3255 const char *bytes = tmp.c_str();
3257 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3258 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3259 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3260 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3261 int written = sqlite3_step(m_database_write);
3262 if(written != SQLITE_DONE)
3263 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3264 <<sqlite3_errmsg(m_database)<<std::endl;
3265 // Make ready for later reuse
3266 sqlite3_reset(m_database_write);
3268 // We just wrote it to the disk so clear modified flag
3269 block->resetModified();
3272 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3274 DSTACK(__FUNCTION_NAME);
3276 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3279 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3280 if(is.good() == false)
3281 throw FileNotGoodException("Cannot open block file");
3283 v3s16 p3d = getBlockPos(sectordir, blockfile);
3284 v2s16 p2d(p3d.X, p3d.Z);
3286 assert(sector->getPos() == p2d);
3288 u8 version = SER_FMT_VER_INVALID;
3289 is.read((char*)&version, 1);
3292 throw SerializationError("ServerMap::loadBlock(): Failed"
3293 " to read MapBlock version");
3295 /*u32 block_size = MapBlock::serializedLength(version);
3296 SharedBuffer<u8> data(block_size);
3297 is.read((char*)*data, block_size);*/
3299 // This will always return a sector because we're the server
3300 //MapSector *sector = emergeSector(p2d);
3302 MapBlock *block = NULL;
3303 bool created_new = false;
3304 block = sector->getBlockNoCreateNoEx(p3d.Y);
3307 block = sector->createBlankBlockNoInsert(p3d.Y);
3312 block->deSerialize(is, version);
3314 // Read extra data stored on disk
3315 block->deSerializeDiskExtra(is, version);
3317 // If it's a new block, insert it to the map
3319 sector->insertBlock(block);
3322 Save blocks loaded in old format in new format
3325 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3329 // Should be in database now, so delete the old file
3330 fs::RecursiveDelete(fullpath);
3333 // We just loaded it from the disk, so it's up-to-date.
3334 block->resetModified();
3337 catch(SerializationError &e)
3339 infostream<<"WARNING: Invalid block data on disk "
3340 <<"fullpath="<<fullpath
3341 <<" (SerializationError). "
3342 <<"what()="<<e.what()
3344 //" Ignoring. A new one will be generated.
3347 // TODO: Backup file; name is in fullpath.
3351 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3353 DSTACK(__FUNCTION_NAME);
3356 std::istringstream is(*blob, std::ios_base::binary);
3358 u8 version = SER_FMT_VER_INVALID;
3359 is.read((char*)&version, 1);
3362 throw SerializationError("ServerMap::loadBlock(): Failed"
3363 " to read MapBlock version");
3365 /*u32 block_size = MapBlock::serializedLength(version);
3366 SharedBuffer<u8> data(block_size);
3367 is.read((char*)*data, block_size);*/
3369 // This will always return a sector because we're the server
3370 //MapSector *sector = emergeSector(p2d);
3372 MapBlock *block = NULL;
3373 bool created_new = false;
3374 block = sector->getBlockNoCreateNoEx(p3d.Y);
3377 block = sector->createBlankBlockNoInsert(p3d.Y);
3382 block->deSerialize(is, version);
3384 // Read extra data stored on disk
3385 block->deSerializeDiskExtra(is, version);
3387 // If it's a new block, insert it to the map
3389 sector->insertBlock(block);
3392 Save blocks loaded in old format in new format
3395 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3400 // We just loaded it from, so it's up-to-date.
3401 block->resetModified();
3404 catch(SerializationError &e)
3406 infostream<<"WARNING: Invalid block data in database "
3407 <<" (SerializationError). "
3408 <<"what()="<<e.what()
3410 //" Ignoring. A new one will be generated.
3413 // TODO: Copy to a backup database.
3417 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3419 DSTACK(__FUNCTION_NAME);
3421 v2s16 p2d(blockpos.X, blockpos.Z);
3423 if(!loadFromFolders()) {
3426 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3427 infostream<<"WARNING: Could not bind block position for load: "
3428 <<sqlite3_errmsg(m_database)<<std::endl;
3429 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3431 Make sure sector is loaded
3433 MapSector *sector = createSector(p2d);
3438 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3439 size_t len = sqlite3_column_bytes(m_database_read, 0);
3441 std::string datastr(data, len);
3443 loadBlock(&datastr, blockpos, sector, false);
3445 sqlite3_step(m_database_read);
3446 // We should never get more than 1 row, so ok to reset
3447 sqlite3_reset(m_database_read);
3449 return getBlockNoCreateNoEx(blockpos);
3451 sqlite3_reset(m_database_read);
3453 // Not found in database, try the files
3456 // The directory layout we're going to load from.
3457 // 1 - original sectors/xxxxzzzz/
3458 // 2 - new sectors2/xxx/zzz/
3459 // If we load from anything but the latest structure, we will
3460 // immediately save to the new one, and remove the old.
3462 std::string sectordir1 = getSectorDir(p2d, 1);
3463 std::string sectordir;
3464 if(fs::PathExists(sectordir1))
3466 sectordir = sectordir1;
3471 sectordir = getSectorDir(p2d, 2);
3475 Make sure sector is loaded
3477 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3481 sector = loadSectorMeta(sectordir, loadlayout != 2);
3483 catch(InvalidFilenameException &e)
3487 catch(FileNotGoodException &e)
3491 catch(std::exception &e)
3498 Make sure file exists
3501 std::string blockfilename = getBlockFilename(blockpos);
3502 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3506 Load block and save it to the database
3508 loadBlock(sectordir, blockfilename, sector, true);
3509 return getBlockNoCreateNoEx(blockpos);
3512 void ServerMap::PrintInfo(std::ostream &out)
3523 ClientMap::ClientMap(
3525 MapDrawControl &control,
3526 scene::ISceneNode* parent,
3527 scene::ISceneManager* mgr,
3531 scene::ISceneNode(parent, mgr, id),
3534 m_camera_position(0,0,0),
3535 m_camera_direction(0,0,1),
3538 m_camera_mutex.Init();
3539 assert(m_camera_mutex.IsInitialized());
3541 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3542 BS*1000000,BS*1000000,BS*1000000);
3545 ClientMap::~ClientMap()
3547 /*JMutexAutoLock lock(mesh_mutex);
3556 MapSector * ClientMap::emergeSector(v2s16 p2d)
3558 DSTACK(__FUNCTION_NAME);
3559 // Check that it doesn't exist already
3561 return getSectorNoGenerate(p2d);
3563 catch(InvalidPositionException &e)
3568 ClientMapSector *sector = new ClientMapSector(this, p2d);
3571 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3572 m_sectors.insert(p2d, sector);
3579 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3581 DSTACK(__FUNCTION_NAME);
3582 ClientMapSector *sector = NULL;
3584 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3586 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3590 sector = (ClientMapSector*)n->getValue();
3591 assert(sector->getId() == MAPSECTOR_CLIENT);
3595 sector = new ClientMapSector(this, p2d);
3597 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3598 m_sectors.insert(p2d, sector);
3602 sector->deSerialize(is);
3606 void ClientMap::OnRegisterSceneNode()
3610 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3611 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3614 ISceneNode::OnRegisterSceneNode();
3617 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3618 float start_off, float end_off, u32 needed_count)
3620 float d0 = (float)BS * p0.getDistanceFrom(p1);
3622 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3624 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3626 for(float s=start_off; s<d0+end_off; s+=step){
3627 v3f pf = p0f + uf * s;
3628 v3s16 p = floatToInt(pf, BS);
3629 MapNode n = map->getNodeNoEx(p);
3630 bool is_transparent = false;
3631 ContentFeatures &f = content_features(n);
3632 if(f.solidness == 0)
3633 is_transparent = (f.visual_solidness != 2);
3635 is_transparent = (f.solidness != 2);
3636 if(!is_transparent){
3638 if(count >= needed_count)
3646 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3648 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3649 DSTACK(__FUNCTION_NAME);
3651 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3654 if(pass == scene::ESNRP_SOLID)
3655 prefix = "CM: solid: ";
3657 prefix = "CM: transparent: ";
3660 This is called two times per frame, reset on the non-transparent one
3662 if(pass == scene::ESNRP_SOLID)
3664 m_last_drawn_sectors.clear();
3668 Get time for measuring timeout.
3670 Measuring time is very useful for long delays when the
3671 machine is swapping a lot.
3673 int time1 = time(0);
3675 //u32 daynight_ratio = m_client->getDayNightRatio();
3677 m_camera_mutex.Lock();
3678 v3f camera_position = m_camera_position;
3679 v3f camera_direction = m_camera_direction;
3680 f32 camera_fov = m_camera_fov;
3681 m_camera_mutex.Unlock();
3684 Get all blocks and draw all visible ones
3687 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3689 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3691 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3692 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3694 // Take a fair amount as we will be dropping more out later
3695 // Umm... these additions are a bit strange but they are needed.
3697 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3698 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3699 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3701 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3702 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3703 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3705 u32 vertex_count = 0;
3706 u32 meshbuffer_count = 0;
3708 // For limiting number of mesh updates per frame
3709 u32 mesh_update_count = 0;
3711 // Number of blocks in rendering range
3712 u32 blocks_in_range = 0;
3713 // Number of blocks occlusion culled
3714 u32 blocks_occlusion_culled = 0;
3715 // Number of blocks in rendering range but don't have a mesh
3716 u32 blocks_in_range_without_mesh = 0;
3717 // Blocks that had mesh that would have been drawn according to
3718 // rendering range (if max blocks limit didn't kick in)
3719 u32 blocks_would_have_drawn = 0;
3720 // Blocks that were drawn and had a mesh
3721 u32 blocks_drawn = 0;
3722 // Blocks which had a corresponding meshbuffer for this pass
3723 u32 blocks_had_pass_meshbuf = 0;
3724 // Blocks from which stuff was actually drawn
3725 u32 blocks_without_stuff = 0;
3728 Collect a set of blocks for drawing
3731 core::map<v3s16, MapBlock*> drawset;
3734 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3736 for(core::map<v2s16, MapSector*>::Iterator
3737 si = m_sectors.getIterator();
3738 si.atEnd() == false; si++)
3740 MapSector *sector = si.getNode()->getValue();
3741 v2s16 sp = sector->getPos();
3743 if(m_control.range_all == false)
3745 if(sp.X < p_blocks_min.X
3746 || sp.X > p_blocks_max.X
3747 || sp.Y < p_blocks_min.Z
3748 || sp.Y > p_blocks_max.Z)
3752 core::list< MapBlock * > sectorblocks;
3753 sector->getBlocks(sectorblocks);
3756 Loop through blocks in sector
3759 u32 sector_blocks_drawn = 0;
3761 core::list< MapBlock * >::Iterator i;
3762 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3764 MapBlock *block = *i;
3767 Compare block position to camera position, skip
3768 if not seen on display
3771 float range = 100000 * BS;
3772 if(m_control.range_all == false)
3773 range = m_control.wanted_range * BS;
3776 if(isBlockInSight(block->getPos(), camera_position,
3777 camera_direction, camera_fov,
3778 range, &d) == false)
3783 // This is ugly (spherical distance limit?)
3784 /*if(m_control.range_all == false &&
3785 d - 0.5*BS*MAP_BLOCKSIZE > range)
3792 Update expired mesh (used for day/night change)
3794 It doesn't work exactly like it should now with the
3795 tasked mesh update but whatever.
3798 bool mesh_expired = false;
3801 JMutexAutoLock lock(block->mesh_mutex);
3803 mesh_expired = block->getMeshExpired();
3805 // Mesh has not been expired and there is no mesh:
3806 // block has no content
3807 if(block->mesh == NULL && mesh_expired == false){
3808 blocks_in_range_without_mesh++;
3813 f32 faraway = BS*50;
3814 //f32 faraway = m_control.wanted_range * BS;
3817 This has to be done with the mesh_mutex unlocked
3819 // Pretty random but this should work somewhat nicely
3820 if(mesh_expired && (
3821 (mesh_update_count < 3
3822 && (d < faraway || mesh_update_count < 2)
3825 (m_control.range_all && mesh_update_count < 20)
3828 /*if(mesh_expired && mesh_update_count < 6
3829 && (d < faraway || mesh_update_count < 3))*/
3831 mesh_update_count++;
3833 // Mesh has been expired: generate new mesh
3834 //block->updateMesh(daynight_ratio);
3835 m_client->addUpdateMeshTask(block->getPos());
3837 mesh_expired = false;
3845 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3846 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3848 float stepfac = 1.1;
3849 float startoff = BS*1;
3850 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3851 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3852 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3853 u32 needed_count = 1;
3855 isOccluded(this, spn, cpn + v3s16(0,0,0),
3856 step, stepfac, startoff, endoff, needed_count) &&
3857 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3858 step, stepfac, startoff, endoff, needed_count) &&
3859 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3860 step, stepfac, startoff, endoff, needed_count) &&
3861 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3862 step, stepfac, startoff, endoff, needed_count) &&
3863 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3864 step, stepfac, startoff, endoff, needed_count) &&
3865 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3866 step, stepfac, startoff, endoff, needed_count) &&
3867 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3868 step, stepfac, startoff, endoff, needed_count) &&
3869 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3870 step, stepfac, startoff, endoff, needed_count) &&
3871 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3872 step, stepfac, startoff, endoff, needed_count)
3875 blocks_occlusion_culled++;
3879 // This block is in range. Reset usage timer.
3880 block->resetUsageTimer();
3883 Ignore if mesh doesn't exist
3886 JMutexAutoLock lock(block->mesh_mutex);
3888 scene::SMesh *mesh = block->mesh;
3891 blocks_in_range_without_mesh++;
3896 // Limit block count in case of a sudden increase
3897 blocks_would_have_drawn++;
3898 if(blocks_drawn >= m_control.wanted_max_blocks
3899 && m_control.range_all == false
3900 && d > m_control.wanted_min_range * BS)
3904 drawset[block->getPos()] = block;
3906 sector_blocks_drawn++;
3909 } // foreach sectorblocks
3911 if(sector_blocks_drawn != 0)
3912 m_last_drawn_sectors[sp] = true;
3917 Draw the selected MapBlocks
3921 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3923 int timecheck_counter = 0;
3924 for(core::map<v3s16, MapBlock*>::Iterator
3925 i = drawset.getIterator();
3926 i.atEnd() == false; i++)
3929 timecheck_counter++;
3930 if(timecheck_counter > 50)
3932 timecheck_counter = 0;
3933 int time2 = time(0);
3934 if(time2 > time1 + 4)
3936 infostream<<"ClientMap::renderMap(): "
3937 "Rendering takes ages, returning."
3944 MapBlock *block = i.getNode()->getValue();
3947 Draw the faces of the block
3950 JMutexAutoLock lock(block->mesh_mutex);
3952 scene::SMesh *mesh = block->mesh;
3955 u32 c = mesh->getMeshBufferCount();
3956 bool stuff_actually_drawn = false;
3957 for(u32 i=0; i<c; i++)
3959 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3960 const video::SMaterial& material = buf->getMaterial();
3961 video::IMaterialRenderer* rnd =
3962 driver->getMaterialRenderer(material.MaterialType);
3963 bool transparent = (rnd && rnd->isTransparent());
3964 // Render transparent on transparent pass and likewise.
3965 if(transparent == is_transparent_pass)
3967 if(buf->getVertexCount() == 0)
3968 errorstream<<"Block ["<<analyze_block(block)
3969 <<"] contains an empty meshbuf"<<std::endl;
3971 This *shouldn't* hurt too much because Irrlicht
3972 doesn't change opengl textures if the old
3973 material has the same texture.
3975 driver->setMaterial(buf->getMaterial());
3976 driver->drawMeshBuffer(buf);
3977 vertex_count += buf->getVertexCount();
3979 stuff_actually_drawn = true;
3982 if(stuff_actually_drawn)
3983 blocks_had_pass_meshbuf++;
3985 blocks_without_stuff++;
3990 // Log only on solid pass because values are the same
3991 if(pass == scene::ESNRP_SOLID){
3992 g_profiler->avg("CM: blocks in range", blocks_in_range);
3993 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3994 if(blocks_in_range != 0)
3995 g_profiler->avg("CM: blocks in range without mesh (frac)",
3996 (float)blocks_in_range_without_mesh/blocks_in_range);
3997 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4000 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4001 if(blocks_had_pass_meshbuf != 0)
4002 g_profiler->avg(prefix+"meshbuffers per block",
4003 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4004 if(blocks_drawn != 0)
4005 g_profiler->avg(prefix+"empty blocks (frac)",
4006 (float)blocks_without_stuff / blocks_drawn);
4008 m_control.blocks_drawn = blocks_drawn;
4009 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4011 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4012 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4015 void ClientMap::renderPostFx()
4017 // Sadly ISceneManager has no "post effects" render pass, in that case we
4018 // could just register for that and handle it in renderMap().
4020 m_camera_mutex.Lock();
4021 v3f camera_position = m_camera_position;
4022 m_camera_mutex.Unlock();
4024 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4026 // - If the player is in a solid node, make everything black.
4027 // - If the player is in liquid, draw a semi-transparent overlay.
4028 ContentFeatures& features = content_features(n);
4029 video::SColor post_effect_color = features.post_effect_color;
4030 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4032 post_effect_color = video::SColor(255, 0, 0, 0);
4034 if (post_effect_color.getAlpha() != 0)
4036 // Draw a full-screen rectangle
4037 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4038 v2u32 ss = driver->getScreenSize();
4039 core::rect<s32> rect(0,0, ss.X, ss.Y);
4040 driver->draw2DRectangle(post_effect_color, rect);
4044 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4045 core::map<v3s16, MapBlock*> *affected_blocks)
4047 bool changed = false;
4049 Add it to all blocks touching it
4052 v3s16(0,0,0), // this
4053 v3s16(0,0,1), // back
4054 v3s16(0,1,0), // top
4055 v3s16(1,0,0), // right
4056 v3s16(0,0,-1), // front
4057 v3s16(0,-1,0), // bottom
4058 v3s16(-1,0,0), // left
4060 for(u16 i=0; i<7; i++)
4062 v3s16 p2 = p + dirs[i];
4063 // Block position of neighbor (or requested) node
4064 v3s16 blockpos = getNodeBlockPos(p2);
4065 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4066 if(blockref == NULL)
4068 // Relative position of requested node
4069 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4070 if(blockref->setTempMod(relpos, mod))
4075 if(changed && affected_blocks!=NULL)
4077 for(u16 i=0; i<7; i++)
4079 v3s16 p2 = p + dirs[i];
4080 // Block position of neighbor (or requested) node
4081 v3s16 blockpos = getNodeBlockPos(p2);
4082 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4083 if(blockref == NULL)
4085 affected_blocks->insert(blockpos, blockref);
4091 bool ClientMap::clearTempMod(v3s16 p,
4092 core::map<v3s16, MapBlock*> *affected_blocks)
4094 bool changed = false;
4096 v3s16(0,0,0), // this
4097 v3s16(0,0,1), // back
4098 v3s16(0,1,0), // top
4099 v3s16(1,0,0), // right
4100 v3s16(0,0,-1), // front
4101 v3s16(0,-1,0), // bottom
4102 v3s16(-1,0,0), // left
4104 for(u16 i=0; i<7; i++)
4106 v3s16 p2 = p + dirs[i];
4107 // Block position of neighbor (or requested) node
4108 v3s16 blockpos = getNodeBlockPos(p2);
4109 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4110 if(blockref == NULL)
4112 // Relative position of requested node
4113 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4114 if(blockref->clearTempMod(relpos))
4119 if(changed && affected_blocks!=NULL)
4121 for(u16 i=0; i<7; i++)
4123 v3s16 p2 = p + dirs[i];
4124 // Block position of neighbor (or requested) node
4125 v3s16 blockpos = getNodeBlockPos(p2);
4126 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4127 if(blockref == NULL)
4129 affected_blocks->insert(blockpos, blockref);
4135 void ClientMap::expireMeshes(bool only_daynight_diffed)
4137 TimeTaker timer("expireMeshes()");
4139 core::map<v2s16, MapSector*>::Iterator si;
4140 si = m_sectors.getIterator();
4141 for(; si.atEnd() == false; si++)
4143 MapSector *sector = si.getNode()->getValue();
4145 core::list< MapBlock * > sectorblocks;
4146 sector->getBlocks(sectorblocks);
4148 core::list< MapBlock * >::Iterator i;
4149 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4151 MapBlock *block = *i;
4153 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4159 JMutexAutoLock lock(block->mesh_mutex);
4160 if(block->mesh != NULL)
4162 /*block->mesh->drop();
4163 block->mesh = NULL;*/
4164 block->setMeshExpired(true);
4171 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4173 assert(mapType() == MAPTYPE_CLIENT);
4176 v3s16 p = blockpos + v3s16(0,0,0);
4177 MapBlock *b = getBlockNoCreate(p);
4178 b->updateMesh(daynight_ratio);
4179 //b->setMeshExpired(true);
4181 catch(InvalidPositionException &e){}
4184 v3s16 p = blockpos + v3s16(-1,0,0);
4185 MapBlock *b = getBlockNoCreate(p);
4186 b->updateMesh(daynight_ratio);
4187 //b->setMeshExpired(true);
4189 catch(InvalidPositionException &e){}
4191 v3s16 p = blockpos + v3s16(0,-1,0);
4192 MapBlock *b = getBlockNoCreate(p);
4193 b->updateMesh(daynight_ratio);
4194 //b->setMeshExpired(true);
4196 catch(InvalidPositionException &e){}
4198 v3s16 p = blockpos + v3s16(0,0,-1);
4199 MapBlock *b = getBlockNoCreate(p);
4200 b->updateMesh(daynight_ratio);
4201 //b->setMeshExpired(true);
4203 catch(InvalidPositionException &e){}
4208 Update mesh of block in which the node is, and if the node is at the
4209 leading edge, update the appropriate leading blocks too.
4211 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4219 v3s16 blockposes[4];
4220 for(u32 i=0; i<4; i++)
4222 v3s16 np = nodepos + dirs[i];
4223 blockposes[i] = getNodeBlockPos(np);
4224 // Don't update mesh of block if it has been done already
4225 bool already_updated = false;
4226 for(u32 j=0; j<i; j++)
4228 if(blockposes[j] == blockposes[i])
4230 already_updated = true;
4237 MapBlock *b = getBlockNoCreate(blockposes[i]);
4238 b->updateMesh(daynight_ratio);
4243 void ClientMap::PrintInfo(std::ostream &out)
4254 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4259 MapVoxelManipulator::~MapVoxelManipulator()
4261 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4265 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4267 TimeTaker timer1("emerge", &emerge_time);
4269 // Units of these are MapBlocks
4270 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4271 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4273 VoxelArea block_area_nodes
4274 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4276 addArea(block_area_nodes);
4278 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4279 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4280 for(s32 x=p_min.X; x<=p_max.X; x++)
4283 core::map<v3s16, bool>::Node *n;
4284 n = m_loaded_blocks.find(p);
4288 bool block_data_inexistent = false;
4291 TimeTaker timer1("emerge load", &emerge_load_time);
4293 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4294 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4296 a.print(infostream);
4297 infostream<<std::endl;*/
4299 MapBlock *block = m_map->getBlockNoCreate(p);
4300 if(block->isDummy())
4301 block_data_inexistent = true;
4303 block->copyTo(*this);
4305 catch(InvalidPositionException &e)
4307 block_data_inexistent = true;
4310 if(block_data_inexistent)
4312 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4313 // Fill with VOXELFLAG_INEXISTENT
4314 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4315 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4317 s32 i = m_area.index(a.MinEdge.X,y,z);
4318 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4322 m_loaded_blocks.insert(p, !block_data_inexistent);
4325 //infostream<<"emerge done"<<std::endl;
4329 SUGG: Add an option to only update eg. water and air nodes.
4330 This will make it interfere less with important stuff if
4333 void MapVoxelManipulator::blitBack
4334 (core::map<v3s16, MapBlock*> & modified_blocks)
4336 if(m_area.getExtent() == v3s16(0,0,0))
4339 //TimeTaker timer1("blitBack");
4341 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4342 <<m_loaded_blocks.size()<<std::endl;*/
4345 Initialize block cache
4347 v3s16 blockpos_last;
4348 MapBlock *block = NULL;
4349 bool block_checked_in_modified = false;
4351 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4352 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4353 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4357 u8 f = m_flags[m_area.index(p)];
4358 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4361 MapNode &n = m_data[m_area.index(p)];
4363 v3s16 blockpos = getNodeBlockPos(p);
4368 if(block == NULL || blockpos != blockpos_last){
4369 block = m_map->getBlockNoCreate(blockpos);
4370 blockpos_last = blockpos;
4371 block_checked_in_modified = false;
4374 // Calculate relative position in block
4375 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4377 // Don't continue if nothing has changed here
4378 if(block->getNode(relpos) == n)
4381 //m_map->setNode(m_area.MinEdge + p, n);
4382 block->setNode(relpos, n);
4385 Make sure block is in modified_blocks
4387 if(block_checked_in_modified == false)
4389 modified_blocks[blockpos] = block;
4390 block_checked_in_modified = true;
4393 catch(InvalidPositionException &e)
4399 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4400 MapVoxelManipulator(map),
4401 m_create_area(false)
4405 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4409 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4411 // Just create the area so that it can be pointed to
4412 VoxelManipulator::emerge(a, caller_id);
4415 void ManualMapVoxelManipulator::initialEmerge(
4416 v3s16 blockpos_min, v3s16 blockpos_max)
4418 TimeTaker timer1("initialEmerge", &emerge_time);
4420 // Units of these are MapBlocks
4421 v3s16 p_min = blockpos_min;
4422 v3s16 p_max = blockpos_max;
4424 VoxelArea block_area_nodes
4425 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4427 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4430 infostream<<"initialEmerge: area: ";
4431 block_area_nodes.print(infostream);
4432 infostream<<" ("<<size_MB<<"MB)";
4433 infostream<<std::endl;
4436 addArea(block_area_nodes);
4438 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4439 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4440 for(s32 x=p_min.X; x<=p_max.X; x++)
4443 core::map<v3s16, bool>::Node *n;
4444 n = m_loaded_blocks.find(p);
4448 bool block_data_inexistent = false;
4451 TimeTaker timer1("emerge load", &emerge_load_time);
4453 MapBlock *block = m_map->getBlockNoCreate(p);
4454 if(block->isDummy())
4455 block_data_inexistent = true;
4457 block->copyTo(*this);
4459 catch(InvalidPositionException &e)
4461 block_data_inexistent = true;
4464 if(block_data_inexistent)
4467 Mark area inexistent
4469 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4470 // Fill with VOXELFLAG_INEXISTENT
4471 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4472 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4474 s32 i = m_area.index(a.MinEdge.X,y,z);
4475 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4479 m_loaded_blocks.insert(p, !block_data_inexistent);
4483 void ManualMapVoxelManipulator::blitBackAll(
4484 core::map<v3s16, MapBlock*> * modified_blocks)
4486 if(m_area.getExtent() == v3s16(0,0,0))
4490 Copy data of all blocks
4492 for(core::map<v3s16, bool>::Iterator
4493 i = m_loaded_blocks.getIterator();
4494 i.atEnd() == false; i++)
4496 v3s16 p = i.getNode()->getKey();
4497 bool existed = i.getNode()->getValue();
4498 if(existed == false)
4500 // The Great Bug was found using this
4501 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4502 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4506 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4509 infostream<<"WARNING: "<<__FUNCTION_NAME
4510 <<": got NULL block "
4511 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4516 block->copyFrom(*this);
4519 modified_blocks->insert(p, block);