3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
30 #include "nodemetadata.h"
31 #include "content_mapnode.h"
33 #include <IMaterialRenderer.h>
39 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
42 SQLite format specification:
43 - Initially only replaces sectors/ and sectors2/
45 If map.sqlite does not exist in the save dir
46 or the block was not found in the database
47 the map will try to load from sectors folder.
48 In either case, map.sqlite will be created
49 and all future saves will save there.
51 Structure of map.sqlite:
62 Map::Map(std::ostream &dout):
66 /*m_sector_mutex.Init();
67 assert(m_sector_mutex.IsInitialized());*/
75 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
76 for(; i.atEnd() == false; i++)
78 MapSector *sector = i.getNode()->getValue();
83 void Map::addEventReceiver(MapEventReceiver *event_receiver)
85 m_event_receivers.insert(event_receiver, false);
88 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
90 if(m_event_receivers.find(event_receiver) == NULL)
92 m_event_receivers.remove(event_receiver);
95 void Map::dispatchEvent(MapEditEvent *event)
97 for(core::map<MapEventReceiver*, bool>::Iterator
98 i = m_event_receivers.getIterator();
99 i.atEnd()==false; i++)
101 MapEventReceiver* event_receiver = i.getNode()->getKey();
102 event_receiver->onMapEditEvent(event);
106 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
108 if(m_sector_cache != NULL && p == m_sector_cache_p){
109 MapSector * sector = m_sector_cache;
113 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
118 MapSector *sector = n->getValue();
120 // Cache the last result
121 m_sector_cache_p = p;
122 m_sector_cache = sector;
127 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
129 return getSectorNoGenerateNoExNoLock(p);
132 MapSector * Map::getSectorNoGenerate(v2s16 p)
134 MapSector *sector = getSectorNoGenerateNoEx(p);
136 throw InvalidPositionException();
141 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
143 v2s16 p2d(p3d.X, p3d.Z);
144 MapSector * sector = getSectorNoGenerateNoEx(p2d);
147 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
151 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
153 MapBlock *block = getBlockNoCreateNoEx(p3d);
155 throw InvalidPositionException();
159 bool Map::isNodeUnderground(v3s16 p)
161 v3s16 blockpos = getNodeBlockPos(p);
163 MapBlock * block = getBlockNoCreate(blockpos);
164 return block->getIsUnderground();
166 catch(InvalidPositionException &e)
172 bool Map::isValidPosition(v3s16 p)
174 v3s16 blockpos = getNodeBlockPos(p);
175 MapBlock *block = getBlockNoCreate(blockpos);
176 return (block != NULL);
179 // Returns a CONTENT_IGNORE node if not found
180 MapNode Map::getNodeNoEx(v3s16 p)
182 v3s16 blockpos = getNodeBlockPos(p);
183 MapBlock *block = getBlockNoCreateNoEx(blockpos);
185 return MapNode(CONTENT_IGNORE);
186 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
187 return block->getNodeNoCheck(relpos);
190 // throws InvalidPositionException if not found
191 MapNode Map::getNode(v3s16 p)
193 v3s16 blockpos = getNodeBlockPos(p);
194 MapBlock *block = getBlockNoCreateNoEx(blockpos);
196 throw InvalidPositionException();
197 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
198 return block->getNodeNoCheck(relpos);
201 // throws InvalidPositionException if not found
202 void Map::setNode(v3s16 p, MapNode & n)
204 v3s16 blockpos = getNodeBlockPos(p);
205 MapBlock *block = getBlockNoCreate(blockpos);
206 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
207 block->setNodeNoCheck(relpos, n);
212 Goes recursively through the neighbours of the node.
214 Alters only transparent nodes.
216 If the lighting of the neighbour is lower than the lighting of
217 the node was (before changing it to 0 at the step before), the
218 lighting of the neighbour is set to 0 and then the same stuff
219 repeats for the neighbour.
221 The ending nodes of the routine are stored in light_sources.
222 This is useful when a light is removed. In such case, this
223 routine can be called for the light node and then again for
224 light_sources to re-light the area without the removed light.
226 values of from_nodes are lighting values.
228 void Map::unspreadLight(enum LightBank bank,
229 core::map<v3s16, u8> & from_nodes,
230 core::map<v3s16, bool> & light_sources,
231 core::map<v3s16, MapBlock*> & modified_blocks)
234 v3s16(0,0,1), // back
236 v3s16(1,0,0), // right
237 v3s16(0,0,-1), // front
238 v3s16(0,-1,0), // bottom
239 v3s16(-1,0,0), // left
242 if(from_nodes.size() == 0)
245 u32 blockchangecount = 0;
247 core::map<v3s16, u8> unlighted_nodes;
248 core::map<v3s16, u8>::Iterator j;
249 j = from_nodes.getIterator();
252 Initialize block cache
255 MapBlock *block = NULL;
256 // Cache this a bit, too
257 bool block_checked_in_modified = false;
259 for(; j.atEnd() == false; j++)
261 v3s16 pos = j.getNode()->getKey();
262 v3s16 blockpos = getNodeBlockPos(pos);
264 // Only fetch a new block if the block position has changed
266 if(block == NULL || blockpos != blockpos_last){
267 block = getBlockNoCreate(blockpos);
268 blockpos_last = blockpos;
270 block_checked_in_modified = false;
274 catch(InvalidPositionException &e)
282 // Calculate relative position in block
283 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
285 // Get node straight from the block
286 MapNode n = block->getNode(relpos);
288 u8 oldlight = j.getNode()->getValue();
290 // Loop through 6 neighbors
291 for(u16 i=0; i<6; i++)
293 // Get the position of the neighbor node
294 v3s16 n2pos = pos + dirs[i];
296 // Get the block where the node is located
297 v3s16 blockpos = getNodeBlockPos(n2pos);
301 // Only fetch a new block if the block position has changed
303 if(block == NULL || blockpos != blockpos_last){
304 block = getBlockNoCreate(blockpos);
305 blockpos_last = blockpos;
307 block_checked_in_modified = false;
311 catch(InvalidPositionException &e)
316 // Calculate relative position in block
317 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
318 // Get node straight from the block
319 MapNode n2 = block->getNode(relpos);
321 bool changed = false;
323 //TODO: Optimize output by optimizing light_sources?
326 If the neighbor is dimmer than what was specified
327 as oldlight (the light of the previous node)
329 if(n2.getLight(bank) < oldlight)
332 And the neighbor is transparent and it has some light
334 if(n2.light_propagates() && n2.getLight(bank) != 0)
337 Set light to 0 and add to queue
340 u8 current_light = n2.getLight(bank);
341 n2.setLight(bank, 0);
342 block->setNode(relpos, n2);
344 unlighted_nodes.insert(n2pos, current_light);
348 Remove from light_sources if it is there
349 NOTE: This doesn't happen nearly at all
351 /*if(light_sources.find(n2pos))
353 infostream<<"Removed from light_sources"<<std::endl;
354 light_sources.remove(n2pos);
359 if(light_sources.find(n2pos) != NULL)
360 light_sources.remove(n2pos);*/
363 light_sources.insert(n2pos, true);
366 // Add to modified_blocks
367 if(changed == true && block_checked_in_modified == false)
369 // If the block is not found in modified_blocks, add.
370 if(modified_blocks.find(blockpos) == NULL)
372 modified_blocks.insert(blockpos, block);
374 block_checked_in_modified = true;
377 catch(InvalidPositionException &e)
384 /*infostream<<"unspreadLight(): Changed block "
385 <<blockchangecount<<" times"
386 <<" for "<<from_nodes.size()<<" nodes"
389 if(unlighted_nodes.size() > 0)
390 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
394 A single-node wrapper of the above
396 void Map::unLightNeighbors(enum LightBank bank,
397 v3s16 pos, u8 lightwas,
398 core::map<v3s16, bool> & light_sources,
399 core::map<v3s16, MapBlock*> & modified_blocks)
401 core::map<v3s16, u8> from_nodes;
402 from_nodes.insert(pos, lightwas);
404 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
408 Lights neighbors of from_nodes, collects all them and then
411 void Map::spreadLight(enum LightBank bank,
412 core::map<v3s16, bool> & from_nodes,
413 core::map<v3s16, MapBlock*> & modified_blocks)
415 const v3s16 dirs[6] = {
416 v3s16(0,0,1), // back
418 v3s16(1,0,0), // right
419 v3s16(0,0,-1), // front
420 v3s16(0,-1,0), // bottom
421 v3s16(-1,0,0), // left
424 if(from_nodes.size() == 0)
427 u32 blockchangecount = 0;
429 core::map<v3s16, bool> lighted_nodes;
430 core::map<v3s16, bool>::Iterator j;
431 j = from_nodes.getIterator();
434 Initialize block cache
437 MapBlock *block = NULL;
438 // Cache this a bit, too
439 bool block_checked_in_modified = false;
441 for(; j.atEnd() == false; j++)
442 //for(; j != from_nodes.end(); j++)
444 v3s16 pos = j.getNode()->getKey();
446 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
447 v3s16 blockpos = getNodeBlockPos(pos);
449 // Only fetch a new block if the block position has changed
451 if(block == NULL || blockpos != blockpos_last){
452 block = getBlockNoCreate(blockpos);
453 blockpos_last = blockpos;
455 block_checked_in_modified = false;
459 catch(InvalidPositionException &e)
467 // Calculate relative position in block
468 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
470 // Get node straight from the block
471 MapNode n = block->getNode(relpos);
473 u8 oldlight = n.getLight(bank);
474 u8 newlight = diminish_light(oldlight);
476 // Loop through 6 neighbors
477 for(u16 i=0; i<6; i++){
478 // Get the position of the neighbor node
479 v3s16 n2pos = pos + dirs[i];
481 // Get the block where the node is located
482 v3s16 blockpos = getNodeBlockPos(n2pos);
486 // Only fetch a new block if the block position has changed
488 if(block == NULL || blockpos != blockpos_last){
489 block = getBlockNoCreate(blockpos);
490 blockpos_last = blockpos;
492 block_checked_in_modified = false;
496 catch(InvalidPositionException &e)
501 // Calculate relative position in block
502 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
503 // Get node straight from the block
504 MapNode n2 = block->getNode(relpos);
506 bool changed = false;
508 If the neighbor is brighter than the current node,
509 add to list (it will light up this node on its turn)
511 if(n2.getLight(bank) > undiminish_light(oldlight))
513 lighted_nodes.insert(n2pos, true);
514 //lighted_nodes.push_back(n2pos);
518 If the neighbor is dimmer than how much light this node
519 would spread on it, add to list
521 if(n2.getLight(bank) < newlight)
523 if(n2.light_propagates())
525 n2.setLight(bank, newlight);
526 block->setNode(relpos, n2);
527 lighted_nodes.insert(n2pos, true);
528 //lighted_nodes.push_back(n2pos);
533 // Add to modified_blocks
534 if(changed == true && block_checked_in_modified == false)
536 // If the block is not found in modified_blocks, add.
537 if(modified_blocks.find(blockpos) == NULL)
539 modified_blocks.insert(blockpos, block);
541 block_checked_in_modified = true;
544 catch(InvalidPositionException &e)
551 /*infostream<<"spreadLight(): Changed block "
552 <<blockchangecount<<" times"
553 <<" for "<<from_nodes.size()<<" nodes"
556 if(lighted_nodes.size() > 0)
557 spreadLight(bank, lighted_nodes, modified_blocks);
561 A single-node source variation of the above.
563 void Map::lightNeighbors(enum LightBank bank,
565 core::map<v3s16, MapBlock*> & modified_blocks)
567 core::map<v3s16, bool> from_nodes;
568 from_nodes.insert(pos, true);
569 spreadLight(bank, from_nodes, modified_blocks);
572 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
575 v3s16(0,0,1), // back
577 v3s16(1,0,0), // right
578 v3s16(0,0,-1), // front
579 v3s16(0,-1,0), // bottom
580 v3s16(-1,0,0), // left
583 u8 brightest_light = 0;
584 v3s16 brightest_pos(0,0,0);
585 bool found_something = false;
587 // Loop through 6 neighbors
588 for(u16 i=0; i<6; i++){
589 // Get the position of the neighbor node
590 v3s16 n2pos = p + dirs[i];
595 catch(InvalidPositionException &e)
599 if(n2.getLight(bank) > brightest_light || found_something == false){
600 brightest_light = n2.getLight(bank);
601 brightest_pos = n2pos;
602 found_something = true;
606 if(found_something == false)
607 throw InvalidPositionException();
609 return brightest_pos;
613 Propagates sunlight down from a node.
614 Starting point gets sunlight.
616 Returns the lowest y value of where the sunlight went.
618 Mud is turned into grass in where the sunlight stops.
620 s16 Map::propagateSunlight(v3s16 start,
621 core::map<v3s16, MapBlock*> & modified_blocks)
626 v3s16 pos(start.X, y, start.Z);
628 v3s16 blockpos = getNodeBlockPos(pos);
631 block = getBlockNoCreate(blockpos);
633 catch(InvalidPositionException &e)
638 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
639 MapNode n = block->getNode(relpos);
641 if(n.sunlight_propagates())
643 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
644 block->setNode(relpos, n);
646 modified_blocks.insert(blockpos, block);
650 /*// Turn mud into grass
651 if(n.getContent() == CONTENT_MUD)
653 n.setContent(CONTENT_GRASS);
654 block->setNode(relpos, n);
655 modified_blocks.insert(blockpos, block);
658 // Sunlight goes no further
665 void Map::updateLighting(enum LightBank bank,
666 core::map<v3s16, MapBlock*> & a_blocks,
667 core::map<v3s16, MapBlock*> & modified_blocks)
669 /*m_dout<<DTIME<<"Map::updateLighting(): "
670 <<a_blocks.size()<<" blocks."<<std::endl;*/
672 //TimeTaker timer("updateLighting");
676 //u32 count_was = modified_blocks.size();
678 core::map<v3s16, MapBlock*> blocks_to_update;
680 core::map<v3s16, bool> light_sources;
682 core::map<v3s16, u8> unlight_from;
684 core::map<v3s16, MapBlock*>::Iterator i;
685 i = a_blocks.getIterator();
686 for(; i.atEnd() == false; i++)
688 MapBlock *block = i.getNode()->getValue();
692 // Don't bother with dummy blocks.
696 v3s16 pos = block->getPos();
697 modified_blocks.insert(pos, block);
699 blocks_to_update.insert(pos, block);
702 Clear all light from block
704 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
705 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
706 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
711 MapNode n = block->getNode(v3s16(x,y,z));
712 u8 oldlight = n.getLight(bank);
714 block->setNode(v3s16(x,y,z), n);
716 // Collect borders for unlighting
717 if(x==0 || x == MAP_BLOCKSIZE-1
718 || y==0 || y == MAP_BLOCKSIZE-1
719 || z==0 || z == MAP_BLOCKSIZE-1)
721 v3s16 p_map = p + v3s16(
724 MAP_BLOCKSIZE*pos.Z);
725 unlight_from.insert(p_map, oldlight);
728 catch(InvalidPositionException &e)
731 This would happen when dealing with a
735 infostream<<"updateLighting(): InvalidPositionException"
740 if(bank == LIGHTBANK_DAY)
742 bool bottom_valid = block->propagateSunlight(light_sources);
744 // If bottom is valid, we're done.
748 else if(bank == LIGHTBANK_NIGHT)
750 // For night lighting, sunlight is not propagated
755 // Invalid lighting bank
759 /*infostream<<"Bottom for sunlight-propagated block ("
760 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
763 // Bottom sunlight is not valid; get the block and loop to it
767 block = getBlockNoCreate(pos);
769 catch(InvalidPositionException &e)
778 Enable this to disable proper lighting for speeding up map
779 generation for testing or whatever
782 //if(g_settings->get(""))
784 core::map<v3s16, MapBlock*>::Iterator i;
785 i = blocks_to_update.getIterator();
786 for(; i.atEnd() == false; i++)
788 MapBlock *block = i.getNode()->getValue();
789 v3s16 p = block->getPos();
790 block->setLightingExpired(false);
798 TimeTaker timer("unspreadLight");
799 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
804 u32 diff = modified_blocks.size() - count_was;
805 count_was = modified_blocks.size();
806 infostream<<"unspreadLight modified "<<diff<<std::endl;
810 TimeTaker timer("spreadLight");
811 spreadLight(bank, light_sources, modified_blocks);
816 u32 diff = modified_blocks.size() - count_was;
817 count_was = modified_blocks.size();
818 infostream<<"spreadLight modified "<<diff<<std::endl;
823 //MapVoxelManipulator vmanip(this);
825 // Make a manual voxel manipulator and load all the blocks
826 // that touch the requested blocks
827 ManualMapVoxelManipulator vmanip(this);
828 core::map<v3s16, MapBlock*>::Iterator i;
829 i = blocks_to_update.getIterator();
830 for(; i.atEnd() == false; i++)
832 MapBlock *block = i.getNode()->getValue();
833 v3s16 p = block->getPos();
835 // Add all surrounding blocks
836 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
839 Add all surrounding blocks that have up-to-date lighting
840 NOTE: This doesn't quite do the job (not everything
841 appropriate is lighted)
843 /*for(s16 z=-1; z<=1; z++)
844 for(s16 y=-1; y<=1; y++)
845 for(s16 x=-1; x<=1; x++)
848 MapBlock *block = getBlockNoCreateNoEx(p);
853 if(block->getLightingExpired())
855 vmanip.initialEmerge(p, p);
858 // Lighting of block will be updated completely
859 block->setLightingExpired(false);
863 //TimeTaker timer("unSpreadLight");
864 vmanip.unspreadLight(bank, unlight_from, light_sources);
867 //TimeTaker timer("spreadLight");
868 vmanip.spreadLight(bank, light_sources);
871 //TimeTaker timer("blitBack");
872 vmanip.blitBack(modified_blocks);
874 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
878 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
881 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
882 core::map<v3s16, MapBlock*> & modified_blocks)
884 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
885 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
888 Update information about whether day and night light differ
890 for(core::map<v3s16, MapBlock*>::Iterator
891 i = modified_blocks.getIterator();
892 i.atEnd() == false; i++)
894 MapBlock *block = i.getNode()->getValue();
895 block->updateDayNightDiff();
901 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
902 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
905 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
906 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
909 From this node to nodes underneath:
910 If lighting is sunlight (1.0), unlight neighbours and
915 v3s16 toppos = p + v3s16(0,1,0);
916 v3s16 bottompos = p + v3s16(0,-1,0);
918 bool node_under_sunlight = true;
919 core::map<v3s16, bool> light_sources;
922 If there is a node at top and it doesn't have sunlight,
923 there has not been any sunlight going down.
925 Otherwise there probably is.
928 MapNode topnode = getNode(toppos);
930 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
931 node_under_sunlight = false;
933 catch(InvalidPositionException &e)
939 If the new node is solid and there is grass below, change it to mud
941 if(content_features(n).walkable == true)
944 MapNode bottomnode = getNode(bottompos);
946 if(bottomnode.getContent() == CONTENT_GRASS
947 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
949 bottomnode.setContent(CONTENT_MUD);
950 setNode(bottompos, bottomnode);
953 catch(InvalidPositionException &e)
961 If the new node is mud and it is under sunlight, change it
964 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
966 n.setContent(CONTENT_GRASS);
971 Remove all light that has come out of this node
974 enum LightBank banks[] =
979 for(s32 i=0; i<2; i++)
981 enum LightBank bank = banks[i];
983 u8 lightwas = getNode(p).getLight(bank);
985 // Add the block of the added node to modified_blocks
986 v3s16 blockpos = getNodeBlockPos(p);
987 MapBlock * block = getBlockNoCreate(blockpos);
988 assert(block != NULL);
989 modified_blocks.insert(blockpos, block);
991 assert(isValidPosition(p));
993 // Unlight neighbours of node.
994 // This means setting light of all consequent dimmer nodes
996 // This also collects the nodes at the border which will spread
997 // light again into this.
998 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1000 n.setLight(bank, 0);
1004 If node lets sunlight through and is under sunlight, it has
1007 if(node_under_sunlight && content_features(n).sunlight_propagates)
1009 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1013 Set the node on the map
1022 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1025 NodeMetadata *meta = meta_proto->clone();
1026 meta->setOwner(player_name);
1027 setNodeMetadata(p, meta);
1031 If node is under sunlight and doesn't let sunlight through,
1032 take all sunlighted nodes under it and clear light from them
1033 and from where the light has been spread.
1034 TODO: This could be optimized by mass-unlighting instead
1037 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1041 //m_dout<<DTIME<<"y="<<y<<std::endl;
1042 v3s16 n2pos(p.X, y, p.Z);
1046 n2 = getNode(n2pos);
1048 catch(InvalidPositionException &e)
1053 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1055 unLightNeighbors(LIGHTBANK_DAY,
1056 n2pos, n2.getLight(LIGHTBANK_DAY),
1057 light_sources, modified_blocks);
1058 n2.setLight(LIGHTBANK_DAY, 0);
1066 for(s32 i=0; i<2; i++)
1068 enum LightBank bank = banks[i];
1071 Spread light from all nodes that might be capable of doing so
1073 spreadLight(bank, light_sources, modified_blocks);
1077 Update information about whether day and night light differ
1079 for(core::map<v3s16, MapBlock*>::Iterator
1080 i = modified_blocks.getIterator();
1081 i.atEnd() == false; i++)
1083 MapBlock *block = i.getNode()->getValue();
1084 block->updateDayNightDiff();
1088 Add neighboring liquid nodes and the node itself if it is
1089 liquid (=water node was added) to transform queue.
1092 v3s16(0,0,0), // self
1093 v3s16(0,0,1), // back
1094 v3s16(0,1,0), // top
1095 v3s16(1,0,0), // right
1096 v3s16(0,0,-1), // front
1097 v3s16(0,-1,0), // bottom
1098 v3s16(-1,0,0), // left
1100 for(u16 i=0; i<7; i++)
1105 v3s16 p2 = p + dirs[i];
1107 MapNode n2 = getNode(p2);
1108 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1110 m_transforming_liquid.push_back(p2);
1113 }catch(InvalidPositionException &e)
1121 void Map::removeNodeAndUpdate(v3s16 p,
1122 core::map<v3s16, MapBlock*> &modified_blocks)
1124 /*PrintInfo(m_dout);
1125 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1126 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1128 bool node_under_sunlight = true;
1130 v3s16 toppos = p + v3s16(0,1,0);
1132 // Node will be replaced with this
1133 content_t replace_material = CONTENT_AIR;
1136 If there is a node at top and it doesn't have sunlight,
1137 there will be no sunlight going down.
1140 MapNode topnode = getNode(toppos);
1142 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1143 node_under_sunlight = false;
1145 catch(InvalidPositionException &e)
1149 core::map<v3s16, bool> light_sources;
1151 enum LightBank banks[] =
1156 for(s32 i=0; i<2; i++)
1158 enum LightBank bank = banks[i];
1161 Unlight neighbors (in case the node is a light source)
1163 unLightNeighbors(bank, p,
1164 getNode(p).getLight(bank),
1165 light_sources, modified_blocks);
1169 Remove node metadata
1172 removeNodeMetadata(p);
1176 This also clears the lighting.
1180 n.setContent(replace_material);
1183 for(s32 i=0; i<2; i++)
1185 enum LightBank bank = banks[i];
1188 Recalculate lighting
1190 spreadLight(bank, light_sources, modified_blocks);
1193 // Add the block of the removed node to modified_blocks
1194 v3s16 blockpos = getNodeBlockPos(p);
1195 MapBlock * block = getBlockNoCreate(blockpos);
1196 assert(block != NULL);
1197 modified_blocks.insert(blockpos, block);
1200 If the removed node was under sunlight, propagate the
1201 sunlight down from it and then light all neighbors
1202 of the propagated blocks.
1204 if(node_under_sunlight)
1206 s16 ybottom = propagateSunlight(p, modified_blocks);
1207 /*m_dout<<DTIME<<"Node was under sunlight. "
1208 "Propagating sunlight";
1209 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1211 for(; y >= ybottom; y--)
1213 v3s16 p2(p.X, y, p.Z);
1214 /*m_dout<<DTIME<<"lighting neighbors of node ("
1215 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1217 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1222 // Set the lighting of this node to 0
1223 // TODO: Is this needed? Lighting is cleared up there already.
1225 MapNode n = getNode(p);
1226 n.setLight(LIGHTBANK_DAY, 0);
1229 catch(InvalidPositionException &e)
1235 for(s32 i=0; i<2; i++)
1237 enum LightBank bank = banks[i];
1239 // Get the brightest neighbour node and propagate light from it
1240 v3s16 n2p = getBrightestNeighbour(bank, p);
1242 MapNode n2 = getNode(n2p);
1243 lightNeighbors(bank, n2p, modified_blocks);
1245 catch(InvalidPositionException &e)
1251 Update information about whether day and night light differ
1253 for(core::map<v3s16, MapBlock*>::Iterator
1254 i = modified_blocks.getIterator();
1255 i.atEnd() == false; i++)
1257 MapBlock *block = i.getNode()->getValue();
1258 block->updateDayNightDiff();
1262 Add neighboring liquid nodes and this node to transform queue.
1263 (it's vital for the node itself to get updated last.)
1266 v3s16(0,0,1), // back
1267 v3s16(0,1,0), // top
1268 v3s16(1,0,0), // right
1269 v3s16(0,0,-1), // front
1270 v3s16(0,-1,0), // bottom
1271 v3s16(-1,0,0), // left
1272 v3s16(0,0,0), // self
1274 for(u16 i=0; i<7; i++)
1279 v3s16 p2 = p + dirs[i];
1281 MapNode n2 = getNode(p2);
1282 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1284 m_transforming_liquid.push_back(p2);
1287 }catch(InvalidPositionException &e)
1293 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1296 event.type = MEET_ADDNODE;
1300 bool succeeded = true;
1302 core::map<v3s16, MapBlock*> modified_blocks;
1303 std::string st = std::string("");
1304 addNodeAndUpdate(p, n, modified_blocks, st);
1306 // Copy modified_blocks to event
1307 for(core::map<v3s16, MapBlock*>::Iterator
1308 i = modified_blocks.getIterator();
1309 i.atEnd()==false; i++)
1311 event.modified_blocks.insert(i.getNode()->getKey(), false);
1314 catch(InvalidPositionException &e){
1318 dispatchEvent(&event);
1323 bool Map::removeNodeWithEvent(v3s16 p)
1326 event.type = MEET_REMOVENODE;
1329 bool succeeded = true;
1331 core::map<v3s16, MapBlock*> modified_blocks;
1332 removeNodeAndUpdate(p, modified_blocks);
1334 // Copy modified_blocks to event
1335 for(core::map<v3s16, MapBlock*>::Iterator
1336 i = modified_blocks.getIterator();
1337 i.atEnd()==false; i++)
1339 event.modified_blocks.insert(i.getNode()->getKey(), false);
1342 catch(InvalidPositionException &e){
1346 dispatchEvent(&event);
1351 bool Map::dayNightDiffed(v3s16 blockpos)
1354 v3s16 p = blockpos + v3s16(0,0,0);
1355 MapBlock *b = getBlockNoCreate(p);
1356 if(b->dayNightDiffed())
1359 catch(InvalidPositionException &e){}
1362 v3s16 p = blockpos + v3s16(-1,0,0);
1363 MapBlock *b = getBlockNoCreate(p);
1364 if(b->dayNightDiffed())
1367 catch(InvalidPositionException &e){}
1369 v3s16 p = blockpos + v3s16(0,-1,0);
1370 MapBlock *b = getBlockNoCreate(p);
1371 if(b->dayNightDiffed())
1374 catch(InvalidPositionException &e){}
1376 v3s16 p = blockpos + v3s16(0,0,-1);
1377 MapBlock *b = getBlockNoCreate(p);
1378 if(b->dayNightDiffed())
1381 catch(InvalidPositionException &e){}
1384 v3s16 p = blockpos + v3s16(1,0,0);
1385 MapBlock *b = getBlockNoCreate(p);
1386 if(b->dayNightDiffed())
1389 catch(InvalidPositionException &e){}
1391 v3s16 p = blockpos + v3s16(0,1,0);
1392 MapBlock *b = getBlockNoCreate(p);
1393 if(b->dayNightDiffed())
1396 catch(InvalidPositionException &e){}
1398 v3s16 p = blockpos + v3s16(0,0,1);
1399 MapBlock *b = getBlockNoCreate(p);
1400 if(b->dayNightDiffed())
1403 catch(InvalidPositionException &e){}
1409 Updates usage timers
1411 void Map::timerUpdate(float dtime, float unload_timeout,
1412 core::list<v3s16> *unloaded_blocks)
1414 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1416 core::list<v2s16> sector_deletion_queue;
1417 u32 deleted_blocks_count = 0;
1418 u32 saved_blocks_count = 0;
1420 core::map<v2s16, MapSector*>::Iterator si;
1423 si = m_sectors.getIterator();
1424 for(; si.atEnd() == false; si++)
1426 MapSector *sector = si.getNode()->getValue();
1428 bool all_blocks_deleted = true;
1430 core::list<MapBlock*> blocks;
1431 sector->getBlocks(blocks);
1433 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1434 i != blocks.end(); i++)
1436 MapBlock *block = (*i);
1438 block->incrementUsageTimer(dtime);
1440 if(block->getUsageTimer() > unload_timeout)
1442 v3s16 p = block->getPos();
1445 if(block->getModified() != MOD_STATE_CLEAN
1446 && save_before_unloading)
1449 saved_blocks_count++;
1452 // Delete from memory
1453 sector->deleteBlock(block);
1456 unloaded_blocks->push_back(p);
1458 deleted_blocks_count++;
1462 all_blocks_deleted = false;
1466 if(all_blocks_deleted)
1468 sector_deletion_queue.push_back(si.getNode()->getKey());
1473 // Finally delete the empty sectors
1474 deleteSectors(sector_deletion_queue);
1476 if(deleted_blocks_count != 0)
1478 PrintInfo(infostream); // ServerMap/ClientMap:
1479 infostream<<"Unloaded "<<deleted_blocks_count
1480 <<" blocks from memory";
1481 if(save_before_unloading)
1482 infostream<<", of which "<<saved_blocks_count<<" were written";
1483 infostream<<"."<<std::endl;
1487 void Map::deleteSectors(core::list<v2s16> &list)
1489 core::list<v2s16>::Iterator j;
1490 for(j=list.begin(); j!=list.end(); j++)
1492 MapSector *sector = m_sectors[*j];
1493 // If sector is in sector cache, remove it from there
1494 if(m_sector_cache == sector)
1495 m_sector_cache = NULL;
1496 // Remove from map and delete
1497 m_sectors.remove(*j);
1503 void Map::unloadUnusedData(float timeout,
1504 core::list<v3s16> *deleted_blocks)
1506 core::list<v2s16> sector_deletion_queue;
1507 u32 deleted_blocks_count = 0;
1508 u32 saved_blocks_count = 0;
1510 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1511 for(; si.atEnd() == false; si++)
1513 MapSector *sector = si.getNode()->getValue();
1515 bool all_blocks_deleted = true;
1517 core::list<MapBlock*> blocks;
1518 sector->getBlocks(blocks);
1519 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1520 i != blocks.end(); i++)
1522 MapBlock *block = (*i);
1524 if(block->getUsageTimer() > timeout)
1527 if(block->getModified() != MOD_STATE_CLEAN)
1530 saved_blocks_count++;
1532 // Delete from memory
1533 sector->deleteBlock(block);
1534 deleted_blocks_count++;
1538 all_blocks_deleted = false;
1542 if(all_blocks_deleted)
1544 sector_deletion_queue.push_back(si.getNode()->getKey());
1548 deleteSectors(sector_deletion_queue);
1550 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1551 <<", of which "<<saved_blocks_count<<" were wr."
1554 //return sector_deletion_queue.getSize();
1555 //return deleted_blocks_count;
1559 void Map::PrintInfo(std::ostream &out)
1564 #define WATER_DROP_BOOST 4
1568 NEIGHBOR_SAME_LEVEL,
1571 struct NodeNeighbor {
1577 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1579 DSTACK(__FUNCTION_NAME);
1580 //TimeTaker timer("transformLiquids()");
1583 u32 initial_size = m_transforming_liquid.size();
1585 /*if(initial_size != 0)
1586 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1588 // list of nodes that due to viscosity have not reached their max level height
1589 UniqueQueue<v3s16> must_reflow;
1591 // List of MapBlocks that will require a lighting update (due to lava)
1592 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1594 while(m_transforming_liquid.size() != 0)
1596 // This should be done here so that it is done when continue is used
1597 if(loopcount >= initial_size * 3)
1602 Get a queued transforming liquid node
1604 v3s16 p0 = m_transforming_liquid.pop_front();
1606 MapNode n0 = getNodeNoEx(p0);
1609 Collect information about current node
1611 s8 liquid_level = -1;
1612 u8 liquid_kind = CONTENT_IGNORE;
1613 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1614 switch (liquid_type) {
1616 liquid_level = LIQUID_LEVEL_SOURCE;
1617 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1619 case LIQUID_FLOWING:
1620 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1621 liquid_kind = n0.getContent();
1624 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1625 // continue with the next node.
1626 if (n0.getContent() != CONTENT_AIR)
1628 liquid_kind = CONTENT_AIR;
1633 Collect information about the environment
1635 const v3s16 *dirs = g_6dirs;
1636 NodeNeighbor sources[6]; // surrounding sources
1637 int num_sources = 0;
1638 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1640 NodeNeighbor airs[6]; // surrounding air
1642 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1643 int num_neutrals = 0;
1644 bool flowing_down = false;
1645 for (u16 i = 0; i < 6; i++) {
1646 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1649 nt = NEIGHBOR_UPPER;
1652 nt = NEIGHBOR_LOWER;
1655 v3s16 npos = p0 + dirs[i];
1656 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1657 switch (content_features(nb.n.getContent()).liquid_type) {
1659 if (nb.n.getContent() == CONTENT_AIR) {
1660 airs[num_airs++] = nb;
1661 // if the current node is a water source the neighbor
1662 // should be enqueded for transformation regardless of whether the
1663 // current node changes or not.
1664 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1665 m_transforming_liquid.push_back(npos);
1666 // if the current node happens to be a flowing node, it will start to flow down here.
1667 if (nb.t == NEIGHBOR_LOWER) {
1668 flowing_down = true;
1671 neutrals[num_neutrals++] = nb;
1675 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1676 if (liquid_kind == CONTENT_AIR)
1677 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1678 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1679 neutrals[num_neutrals++] = nb;
1681 sources[num_sources++] = nb;
1684 case LIQUID_FLOWING:
1685 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1686 if (liquid_kind == CONTENT_AIR)
1687 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1688 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1689 neutrals[num_neutrals++] = nb;
1691 flows[num_flows++] = nb;
1692 if (nb.t == NEIGHBOR_LOWER)
1693 flowing_down = true;
1700 decide on the type (and possibly level) of the current node
1702 content_t new_node_content;
1703 s8 new_node_level = -1;
1704 s8 max_node_level = -1;
1705 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1706 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1707 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1708 // it's perfectly safe to use liquid_kind here to determine the new node content.
1709 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1710 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1711 // liquid_kind is set properly, see above
1712 new_node_content = liquid_kind;
1713 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1715 // no surrounding sources, so get the maximum level that can flow into this node
1716 for (u16 i = 0; i < num_flows; i++) {
1717 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1718 switch (flows[i].t) {
1719 case NEIGHBOR_UPPER:
1720 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1721 max_node_level = LIQUID_LEVEL_MAX;
1722 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1723 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1724 } else if (nb_liquid_level > max_node_level)
1725 max_node_level = nb_liquid_level;
1727 case NEIGHBOR_LOWER:
1729 case NEIGHBOR_SAME_LEVEL:
1730 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1731 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1732 max_node_level = nb_liquid_level - 1;
1738 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1739 if (viscosity > 1 && max_node_level != liquid_level) {
1740 // amount to gain, limited by viscosity
1741 // must be at least 1 in absolute value
1742 s8 level_inc = max_node_level - liquid_level;
1743 if (level_inc < -viscosity || level_inc > viscosity)
1744 new_node_level = liquid_level + level_inc/viscosity;
1745 else if (level_inc < 0)
1746 new_node_level = liquid_level - 1;
1747 else if (level_inc > 0)
1748 new_node_level = liquid_level + 1;
1749 if (new_node_level != max_node_level)
1750 must_reflow.push_back(p0);
1752 new_node_level = max_node_level;
1754 if (new_node_level >= 0)
1755 new_node_content = liquid_kind;
1757 new_node_content = CONTENT_AIR;
1762 check if anything has changed. if not, just continue with the next node.
1764 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1765 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1766 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1772 update the current node
1774 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1775 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1776 // set level to last 3 bits, flowing down bit to 4th bit
1777 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1779 // set the liquid level and flow bit to 0
1780 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1782 n0.setContent(new_node_content);
1784 v3s16 blockpos = getNodeBlockPos(p0);
1785 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1787 modified_blocks.insert(blockpos, block);
1788 // If node emits light, MapBlock requires lighting update
1789 if(content_features(n0).light_source != 0)
1790 lighting_modified_blocks[block->getPos()] = block;
1794 enqueue neighbors for update if neccessary
1796 switch (content_features(n0.getContent()).liquid_type) {
1798 case LIQUID_FLOWING:
1799 // make sure source flows into all neighboring nodes
1800 for (u16 i = 0; i < num_flows; i++)
1801 if (flows[i].t != NEIGHBOR_UPPER)
1802 m_transforming_liquid.push_back(flows[i].p);
1803 for (u16 i = 0; i < num_airs; i++)
1804 if (airs[i].t != NEIGHBOR_UPPER)
1805 m_transforming_liquid.push_back(airs[i].p);
1808 // this flow has turned to air; neighboring flows might need to do the same
1809 for (u16 i = 0; i < num_flows; i++)
1810 m_transforming_liquid.push_back(flows[i].p);
1814 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1815 while (must_reflow.size() > 0)
1816 m_transforming_liquid.push_back(must_reflow.pop_front());
1817 updateLighting(lighting_modified_blocks, modified_blocks);
1820 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1822 v3s16 blockpos = getNodeBlockPos(p);
1823 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1824 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1826 infostream<<"Map::getNodeMetadata(): Need to emerge "
1827 <<PP(blockpos)<<std::endl;
1828 block = emergeBlock(blockpos, false);
1832 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1836 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1840 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1842 v3s16 blockpos = getNodeBlockPos(p);
1843 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1844 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1846 infostream<<"Map::setNodeMetadata(): Need to emerge "
1847 <<PP(blockpos)<<std::endl;
1848 block = emergeBlock(blockpos, false);
1852 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1856 block->m_node_metadata.set(p_rel, meta);
1859 void Map::removeNodeMetadata(v3s16 p)
1861 v3s16 blockpos = getNodeBlockPos(p);
1862 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1863 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1866 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1870 block->m_node_metadata.remove(p_rel);
1873 void Map::nodeMetadataStep(float dtime,
1874 core::map<v3s16, MapBlock*> &changed_blocks)
1878 Currently there is no way to ensure that all the necessary
1879 blocks are loaded when this is run. (They might get unloaded)
1880 NOTE: ^- Actually, that might not be so. In a quick test it
1881 reloaded a block with a furnace when I walked back to it from
1884 core::map<v2s16, MapSector*>::Iterator si;
1885 si = m_sectors.getIterator();
1886 for(; si.atEnd() == false; si++)
1888 MapSector *sector = si.getNode()->getValue();
1889 core::list< MapBlock * > sectorblocks;
1890 sector->getBlocks(sectorblocks);
1891 core::list< MapBlock * >::Iterator i;
1892 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1894 MapBlock *block = *i;
1895 bool changed = block->m_node_metadata.step(dtime);
1897 changed_blocks[block->getPos()] = block;
1906 ServerMap::ServerMap(std::string savedir):
1909 m_map_metadata_changed(true),
1911 m_database_read(NULL),
1912 m_database_write(NULL)
1914 infostream<<__FUNCTION_NAME<<std::endl;
1916 //m_chunksize = 8; // Takes a few seconds
1918 if (g_settings->get("fixed_map_seed").empty())
1920 m_seed = (((u64)(myrand()%0xffff)<<0)
1921 + ((u64)(myrand()%0xffff)<<16)
1922 + ((u64)(myrand()%0xffff)<<32)
1923 + ((u64)(myrand()%0xffff)<<48));
1927 m_seed = g_settings->getU64("fixed_map_seed");
1931 Experimental and debug stuff
1938 Try to load map; if not found, create a new one.
1941 m_savedir = savedir;
1942 m_map_saving_enabled = false;
1946 // If directory exists, check contents and load if possible
1947 if(fs::PathExists(m_savedir))
1949 // If directory is empty, it is safe to save into it.
1950 if(fs::GetDirListing(m_savedir).size() == 0)
1952 infostream<<"Server: Empty save directory is valid."
1954 m_map_saving_enabled = true;
1959 // Load map metadata (seed, chunksize)
1962 catch(FileNotGoodException &e){
1963 infostream<<"WARNING: Could not load map metadata"
1964 //<<" Disabling chunk-based generator."
1970 // Load chunk metadata
1973 catch(FileNotGoodException &e){
1974 infostream<<"WARNING: Could not load chunk metadata."
1975 <<" Disabling chunk-based generator."
1980 /*infostream<<"Server: Successfully loaded chunk "
1981 "metadata and sector (0,0) from "<<savedir<<
1982 ", assuming valid save directory."
1985 infostream<<"Server: Successfully loaded map "
1986 <<"and chunk metadata from "<<savedir
1987 <<", assuming valid save directory."
1990 m_map_saving_enabled = true;
1991 // Map loaded, not creating new one
1995 // If directory doesn't exist, it is safe to save to it
1997 m_map_saving_enabled = true;
2000 catch(std::exception &e)
2002 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2003 <<", exception: "<<e.what()<<std::endl;
2004 infostream<<"Please remove the map or fix it."<<std::endl;
2005 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2008 infostream<<"Initializing new map."<<std::endl;
2010 // Create zero sector
2011 emergeSector(v2s16(0,0));
2013 // Initially write whole map
2017 ServerMap::~ServerMap()
2019 infostream<<__FUNCTION_NAME<<std::endl;
2023 if(m_map_saving_enabled)
2025 // Save only changed parts
2027 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2031 infostream<<"Server: map not saved"<<std::endl;
2034 catch(std::exception &e)
2036 infostream<<"Server: Failed to save map to "<<m_savedir
2037 <<", exception: "<<e.what()<<std::endl;
2041 Close database if it was opened
2044 sqlite3_finalize(m_database_read);
2045 if(m_database_write)
2046 sqlite3_finalize(m_database_write);
2048 sqlite3_close(m_database);
2054 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2055 for(; i.atEnd() == false; i++)
2057 MapChunk *chunk = i.getNode()->getValue();
2063 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2065 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2066 if(enable_mapgen_debug_info)
2067 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2068 <<blockpos.Z<<")"<<std::endl;
2070 // Do nothing if not inside limits (+-1 because of neighbors)
2071 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2072 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2078 data->no_op = false;
2079 data->seed = m_seed;
2080 data->blockpos = blockpos;
2083 Create the whole area of this and the neighboring blocks
2086 //TimeTaker timer("initBlockMake() create area");
2088 for(s16 x=-1; x<=1; x++)
2089 for(s16 z=-1; z<=1; z++)
2091 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2092 // Sector metadata is loaded from disk if not already loaded.
2093 ServerMapSector *sector = createSector(sectorpos);
2096 for(s16 y=-1; y<=1; y++)
2098 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2099 //MapBlock *block = createBlock(p);
2100 // 1) get from memory, 2) load from disk
2101 MapBlock *block = emergeBlock(p, false);
2102 // 3) create a blank one
2105 block = createBlock(p);
2108 Block gets sunlight if this is true.
2110 Refer to the map generator heuristics.
2112 bool ug = mapgen::block_is_underground(data->seed, p);
2113 block->setIsUnderground(ug);
2116 // Lighting will not be valid after make_chunk is called
2117 block->setLightingExpired(true);
2118 // Lighting will be calculated
2119 //block->setLightingExpired(false);
2125 Now we have a big empty area.
2127 Make a ManualMapVoxelManipulator that contains this and the
2131 // The area that contains this block and it's neighbors
2132 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2133 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2135 data->vmanip = new ManualMapVoxelManipulator(this);
2136 //data->vmanip->setMap(this);
2140 //TimeTaker timer("initBlockMake() initialEmerge");
2141 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2144 // Data is ready now.
2147 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2148 core::map<v3s16, MapBlock*> &changed_blocks)
2150 v3s16 blockpos = data->blockpos;
2151 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2152 <<blockpos.Z<<")"<<std::endl;*/
2156 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2160 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2162 /*infostream<<"Resulting vmanip:"<<std::endl;
2163 data->vmanip.print(infostream);*/
2166 Blit generated stuff to map
2167 NOTE: blitBackAll adds nearly everything to changed_blocks
2171 //TimeTaker timer("finishBlockMake() blitBackAll");
2172 data->vmanip->blitBackAll(&changed_blocks);
2175 if(enable_mapgen_debug_info)
2176 infostream<<"finishBlockMake: changed_blocks.size()="
2177 <<changed_blocks.size()<<std::endl;
2180 Copy transforming liquid information
2182 while(data->transforming_liquid.size() > 0)
2184 v3s16 p = data->transforming_liquid.pop_front();
2185 m_transforming_liquid.push_back(p);
2191 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2195 Set is_underground flag for lighting with sunlight.
2197 Refer to map generator heuristics.
2199 NOTE: This is done in initChunkMake
2201 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2205 Add sunlight to central block.
2206 This makes in-dark-spawning monsters to not flood the whole thing.
2207 Do not spread the light, though.
2209 /*core::map<v3s16, bool> light_sources;
2210 bool black_air_left = false;
2211 block->propagateSunlight(light_sources, true, &black_air_left);*/
2214 NOTE: Lighting and object adding shouldn't really be here, but
2215 lighting is a bit tricky to move properly to makeBlock.
2216 TODO: Do this the right way anyway, that is, move it to makeBlock.
2217 - There needs to be some way for makeBlock to report back if
2218 the lighting update is going further down because of the
2219 new block blocking light
2224 NOTE: This takes ~60ms, TODO: Investigate why
2227 TimeTaker t("finishBlockMake lighting update");
2229 core::map<v3s16, MapBlock*> lighting_update_blocks;
2232 lighting_update_blocks.insert(block->getPos(), block);
2237 v3s16 p = block->getPos()+v3s16(x,1,z);
2238 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2242 // All modified blocks
2243 // NOTE: Should this be done? If this is not done, then the lighting
2244 // of the others will be updated in a different place, one by one, i
2245 // think... or they might not? Well, at least they are left marked as
2246 // "lighting expired"; it seems that is not handled at all anywhere,
2247 // so enabling this will slow it down A LOT because otherwise it
2248 // would not do this at all. This causes the black trees.
2249 for(core::map<v3s16, MapBlock*>::Iterator
2250 i = changed_blocks.getIterator();
2251 i.atEnd() == false; i++)
2253 lighting_update_blocks.insert(i.getNode()->getKey(),
2254 i.getNode()->getValue());
2256 /*// Also force-add all the upmost blocks for proper sunlight
2257 for(s16 x=-1; x<=1; x++)
2258 for(s16 z=-1; z<=1; z++)
2260 v3s16 p = block->getPos()+v3s16(x,1,z);
2261 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2264 updateLighting(lighting_update_blocks, changed_blocks);
2267 Set lighting to non-expired state in all of them.
2268 This is cheating, but it is not fast enough if all of them
2269 would actually be updated.
2271 for(s16 x=-1; x<=1; x++)
2272 for(s16 y=-1; y<=1; y++)
2273 for(s16 z=-1; z<=1; z++)
2275 v3s16 p = block->getPos()+v3s16(x,y,z);
2276 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2279 if(enable_mapgen_debug_info == false)
2280 t.stop(true); // Hide output
2284 Add random objects to block
2286 mapgen::add_random_objects(block);
2289 Go through changed blocks
2291 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2292 i.atEnd() == false; i++)
2294 MapBlock *block = i.getNode()->getValue();
2297 Update day/night difference cache of the MapBlocks
2299 block->updateDayNightDiff();
2301 Set block as modified
2303 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2307 Set central block as generated
2309 block->setGenerated(true);
2312 Save changed parts of map
2313 NOTE: Will be saved later.
2317 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2318 <<blockpos.Z<<")"<<std::endl;*/
2320 if(enable_mapgen_debug_info)
2323 Analyze resulting blocks
2325 for(s16 x=-1; x<=1; x++)
2326 for(s16 y=-1; y<=1; y++)
2327 for(s16 z=-1; z<=1; z++)
2329 v3s16 p = block->getPos()+v3s16(x,y,z);
2330 MapBlock *block = getBlockNoCreateNoEx(p);
2332 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2333 infostream<<"Generated "<<spos<<": "
2334 <<analyze_block(block)<<std::endl;
2342 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2344 DSTACKF("%s: p2d=(%d,%d)",
2349 Check if it exists already in memory
2351 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2356 Try to load it from disk (with blocks)
2358 //if(loadSectorFull(p2d) == true)
2361 Try to load metadata from disk
2364 if(loadSectorMeta(p2d) == true)
2366 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2369 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2370 throw InvalidPositionException("");
2376 Do not create over-limit
2378 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2379 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2380 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2381 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2382 throw InvalidPositionException("createSector(): pos. over limit");
2385 Generate blank sector
2388 sector = new ServerMapSector(this, p2d);
2390 // Sector position on map in nodes
2391 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2396 m_sectors.insert(p2d, sector);
2402 This is a quick-hand function for calling makeBlock().
2404 MapBlock * ServerMap::generateBlock(
2406 core::map<v3s16, MapBlock*> &modified_blocks
2409 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2411 /*infostream<<"generateBlock(): "
2412 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2415 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2417 TimeTaker timer("generateBlock");
2419 //MapBlock *block = original_dummy;
2421 v2s16 p2d(p.X, p.Z);
2422 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2425 Do not generate over-limit
2427 if(blockpos_over_limit(p))
2429 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2430 throw InvalidPositionException("generateBlock(): pos. over limit");
2434 Create block make data
2436 mapgen::BlockMakeData data;
2437 initBlockMake(&data, p);
2443 TimeTaker t("mapgen::make_block()");
2444 mapgen::make_block(&data);
2446 if(enable_mapgen_debug_info == false)
2447 t.stop(true); // Hide output
2451 Blit data back on map, update lighting, add mobs and whatever this does
2453 finishBlockMake(&data, modified_blocks);
2458 MapBlock *block = getBlockNoCreateNoEx(p);
2466 bool erroneus_content = false;
2467 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2468 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2469 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2472 MapNode n = block->getNode(p);
2473 if(n.getContent() == CONTENT_IGNORE)
2475 infostream<<"CONTENT_IGNORE at "
2476 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2478 erroneus_content = true;
2482 if(erroneus_content)
2491 Generate a completely empty block
2495 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2496 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2498 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2502 n.setContent(CONTENT_AIR);
2504 n.setContent(CONTENT_STONE);
2505 block->setNode(v3s16(x0,y0,z0), n);
2511 if(enable_mapgen_debug_info == false)
2512 timer.stop(true); // Hide output
2517 MapBlock * ServerMap::createBlock(v3s16 p)
2519 DSTACKF("%s: p=(%d,%d,%d)",
2520 __FUNCTION_NAME, p.X, p.Y, p.Z);
2523 Do not create over-limit
2525 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2526 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2527 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2528 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2529 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2530 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2531 throw InvalidPositionException("createBlock(): pos. over limit");
2533 v2s16 p2d(p.X, p.Z);
2536 This will create or load a sector if not found in memory.
2537 If block exists on disk, it will be loaded.
2539 NOTE: On old save formats, this will be slow, as it generates
2540 lighting on blocks for them.
2542 ServerMapSector *sector;
2544 sector = (ServerMapSector*)createSector(p2d);
2545 assert(sector->getId() == MAPSECTOR_SERVER);
2547 catch(InvalidPositionException &e)
2549 infostream<<"createBlock: createSector() failed"<<std::endl;
2553 NOTE: This should not be done, or at least the exception
2554 should not be passed on as std::exception, because it
2555 won't be catched at all.
2557 /*catch(std::exception &e)
2559 infostream<<"createBlock: createSector() failed: "
2560 <<e.what()<<std::endl;
2565 Try to get a block from the sector
2568 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2571 if(block->isDummy())
2576 block = sector->createBlankBlock(block_y);
2580 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2582 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2584 p.X, p.Y, p.Z, allow_generate);
2587 MapBlock *block = getBlockNoCreateNoEx(p);
2588 if(block && block->isDummy() == false)
2593 MapBlock *block = loadBlock(p);
2600 core::map<v3s16, MapBlock*> modified_blocks;
2601 MapBlock *block = generateBlock(p, modified_blocks);
2605 event.type = MEET_OTHER;
2608 // Copy modified_blocks to event
2609 for(core::map<v3s16, MapBlock*>::Iterator
2610 i = modified_blocks.getIterator();
2611 i.atEnd()==false; i++)
2613 event.modified_blocks.insert(i.getNode()->getKey(), false);
2617 dispatchEvent(&event);
2628 Do not generate over-limit
2630 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2631 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2632 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2633 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2634 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2635 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2636 throw InvalidPositionException("emergeBlock(): pos. over limit");
2638 v2s16 p2d(p.X, p.Z);
2641 This will create or load a sector if not found in memory.
2642 If block exists on disk, it will be loaded.
2644 ServerMapSector *sector;
2646 sector = createSector(p2d);
2647 //sector = emergeSector(p2d, changed_blocks);
2649 catch(InvalidPositionException &e)
2651 infostream<<"emergeBlock: createSector() failed: "
2652 <<e.what()<<std::endl;
2653 infostream<<"Path to failed sector: "<<getSectorDir(p2d)
2655 <<"You could try to delete it."<<std::endl;
2658 catch(VersionMismatchException &e)
2660 infostream<<"emergeBlock: createSector() failed: "
2661 <<e.what()<<std::endl;
2662 infostream<<"Path to failed sector: "<<getSectorDir(p2d)
2664 <<"You could try to delete it."<<std::endl;
2669 Try to get a block from the sector
2672 bool does_not_exist = false;
2673 bool lighting_expired = false;
2674 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2676 // If not found, try loading from disk
2679 block = loadBlock(p);
2685 does_not_exist = true;
2687 else if(block->isDummy() == true)
2689 does_not_exist = true;
2691 else if(block->getLightingExpired())
2693 lighting_expired = true;
2698 //infostream<<"emergeBlock(): Returning already valid block"<<std::endl;
2703 If block was not found on disk and not going to generate a
2704 new one, make sure there is a dummy block in place.
2706 if(only_from_disk && (does_not_exist || lighting_expired))
2708 //infostream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2712 // Create dummy block
2713 block = new MapBlock(this, p, true);
2715 // Add block to sector
2716 sector->insertBlock(block);
2722 //infostream<<"Not found on disk, generating."<<std::endl;
2724 //TimeTaker("emergeBlock() generate");
2726 //infostream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2729 If the block doesn't exist, generate the block.
2733 block = generateBlock(p, block, sector, changed_blocks,
2734 lighting_invalidated_blocks);
2737 if(lighting_expired)
2739 lighting_invalidated_blocks.insert(p, block);
2744 Initially update sunlight
2747 core::map<v3s16, bool> light_sources;
2748 bool black_air_left = false;
2749 bool bottom_invalid =
2750 block->propagateSunlight(light_sources, true,
2753 // If sunlight didn't reach everywhere and part of block is
2754 // above ground, lighting has to be properly updated
2755 //if(black_air_left && some_part_underground)
2758 lighting_invalidated_blocks[block->getPos()] = block;
2763 lighting_invalidated_blocks[block->getPos()] = block;
2772 s16 ServerMap::findGroundLevel(v2s16 p2d)
2776 Uh, just do something random...
2778 // Find existing map from top to down
2781 v3s16 p(p2d.X, max, p2d.Y);
2782 for(; p.Y>min; p.Y--)
2784 MapNode n = getNodeNoEx(p);
2785 if(n.getContent() != CONTENT_IGNORE)
2790 // If this node is not air, go to plan b
2791 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2793 // Search existing walkable and return it
2794 for(; p.Y>min; p.Y--)
2796 MapNode n = getNodeNoEx(p);
2797 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2806 Determine from map generator noise functions
2809 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2812 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2813 //return (s16)level;
2816 void ServerMap::createDatabase() {
2819 e = sqlite3_exec(m_database,
2820 "CREATE TABLE IF NOT EXISTS `blocks` ("
2821 "`pos` INT NOT NULL PRIMARY KEY,"
2824 , NULL, NULL, NULL);
2825 if(e == SQLITE_ABORT)
2826 throw FileNotGoodException("Could not create database structure");
2828 infostream<<"Server: Database structure was created";
2831 void ServerMap::verifyDatabase() {
2836 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2837 bool needs_create = false;
2841 Open the database connection
2844 createDirs(m_savedir);
2846 if(!fs::PathExists(dbp))
2847 needs_create = true;
2849 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2850 if(d != SQLITE_OK) {
2851 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2852 throw FileNotGoodException("Cannot open database file");
2858 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2859 if(d != SQLITE_OK) {
2860 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2861 throw FileNotGoodException("Cannot prepare read statement");
2864 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2865 if(d != SQLITE_OK) {
2866 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2867 throw FileNotGoodException("Cannot prepare write statement");
2870 infostream<<"Server: Database opened"<<std::endl;
2874 bool ServerMap::loadFromFolders() {
2875 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2880 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2881 return (sqlite3_int64)pos.Z*16777216 +
2882 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2885 void ServerMap::createDirs(std::string path)
2887 if(fs::CreateAllDirs(path) == false)
2889 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2890 <<"\""<<path<<"\""<<std::endl;
2891 throw BaseException("ServerMap failed to create directory");
2895 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2901 snprintf(cc, 9, "%.4x%.4x",
2902 (unsigned int)pos.X&0xffff,
2903 (unsigned int)pos.Y&0xffff);
2905 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2907 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2908 (unsigned int)pos.X&0xfff,
2909 (unsigned int)pos.Y&0xfff);
2911 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2917 v2s16 ServerMap::getSectorPos(std::string dirname)
2921 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2922 assert(spos != std::string::npos);
2923 if(dirname.size() - spos == 8)
2926 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2928 else if(dirname.size() - spos == 3)
2931 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2932 // Sign-extend the 12 bit values up to 16 bits...
2933 if(x&0x800) x|=0xF000;
2934 if(y&0x800) y|=0xF000;
2941 v2s16 pos((s16)x, (s16)y);
2945 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2947 v2s16 p2d = getSectorPos(sectordir);
2949 if(blockfile.size() != 4){
2950 throw InvalidFilenameException("Invalid block filename");
2953 int r = sscanf(blockfile.c_str(), "%4x", &y);
2955 throw InvalidFilenameException("Invalid block filename");
2956 return v3s16(p2d.X, y, p2d.Y);
2959 std::string ServerMap::getBlockFilename(v3s16 p)
2962 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2966 void ServerMap::save(bool only_changed)
2968 DSTACK(__FUNCTION_NAME);
2969 if(m_map_saving_enabled == false)
2971 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2975 if(only_changed == false)
2976 infostream<<"ServerMap: Saving whole map, this can take time."
2979 if(only_changed == false || m_map_metadata_changed)
2984 u32 sector_meta_count = 0;
2985 u32 block_count = 0;
2986 u32 block_count_all = 0; // Number of blocks in memory
2989 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2990 for(; i.atEnd() == false; i++)
2992 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2993 assert(sector->getId() == MAPSECTOR_SERVER);
2995 if(sector->differs_from_disk || only_changed == false)
2997 saveSectorMeta(sector);
2998 sector_meta_count++;
3000 core::list<MapBlock*> blocks;
3001 sector->getBlocks(blocks);
3002 core::list<MapBlock*>::Iterator j;
3004 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
3005 for(j=blocks.begin(); j!=blocks.end(); j++)
3007 MapBlock *block = *j;
3011 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
3012 || only_changed == false)
3017 /*infostream<<"ServerMap: Written block ("
3018 <<block->getPos().X<<","
3019 <<block->getPos().Y<<","
3020 <<block->getPos().Z<<")"
3023 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3029 Only print if something happened or saved whole map
3031 if(only_changed == false || sector_meta_count != 0
3032 || block_count != 0)
3034 infostream<<"ServerMap: Written: "
3035 <<sector_meta_count<<" sector metadata files, "
3036 <<block_count<<" block files"
3037 <<", "<<block_count_all<<" blocks in memory."
3042 void ServerMap::saveMapMeta()
3044 DSTACK(__FUNCTION_NAME);
3046 infostream<<"ServerMap::saveMapMeta(): "
3050 createDirs(m_savedir);
3052 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3053 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3054 if(os.good() == false)
3056 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3057 <<"could not open"<<fullpath<<std::endl;
3058 throw FileNotGoodException("Cannot open chunk metadata");
3062 params.setU64("seed", m_seed);
3064 params.writeLines(os);
3066 os<<"[end_of_params]\n";
3068 m_map_metadata_changed = false;
3071 void ServerMap::loadMapMeta()
3073 DSTACK(__FUNCTION_NAME);
3075 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3078 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3079 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3080 if(is.good() == false)
3082 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3083 <<"could not open"<<fullpath<<std::endl;
3084 throw FileNotGoodException("Cannot open map metadata");
3092 throw SerializationError
3093 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3095 std::getline(is, line);
3096 std::string trimmedline = trim(line);
3097 if(trimmedline == "[end_of_params]")
3099 params.parseConfigLine(line);
3102 m_seed = params.getU64("seed");
3104 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3107 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3109 DSTACK(__FUNCTION_NAME);
3110 // Format used for writing
3111 u8 version = SER_FMT_VER_HIGHEST;
3113 v2s16 pos = sector->getPos();
3114 std::string dir = getSectorDir(pos);
3117 std::string fullpath = dir + DIR_DELIM + "meta";
3118 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3119 if(o.good() == false)
3120 throw FileNotGoodException("Cannot open sector metafile");
3122 sector->serialize(o, version);
3124 sector->differs_from_disk = false;
3127 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3129 DSTACK(__FUNCTION_NAME);
3131 v2s16 p2d = getSectorPos(sectordir);
3133 ServerMapSector *sector = NULL;
3135 std::string fullpath = sectordir + DIR_DELIM + "meta";
3136 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3137 if(is.good() == false)
3139 // If the directory exists anyway, it probably is in some old
3140 // format. Just go ahead and create the sector.
3141 if(fs::PathExists(sectordir))
3143 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3144 <<fullpath<<" doesn't exist but directory does."
3145 <<" Continuing with a sector with no metadata."
3147 sector = new ServerMapSector(this, p2d);
3148 m_sectors.insert(p2d, sector);
3152 throw FileNotGoodException("Cannot open sector metafile");
3157 sector = ServerMapSector::deSerialize
3158 (is, this, p2d, m_sectors);
3160 saveSectorMeta(sector);
3163 sector->differs_from_disk = false;
3168 bool ServerMap::loadSectorMeta(v2s16 p2d)
3170 DSTACK(__FUNCTION_NAME);
3172 MapSector *sector = NULL;
3174 // The directory layout we're going to load from.
3175 // 1 - original sectors/xxxxzzzz/
3176 // 2 - new sectors2/xxx/zzz/
3177 // If we load from anything but the latest structure, we will
3178 // immediately save to the new one, and remove the old.
3180 std::string sectordir1 = getSectorDir(p2d, 1);
3181 std::string sectordir;
3182 if(fs::PathExists(sectordir1))
3184 sectordir = sectordir1;
3189 sectordir = getSectorDir(p2d, 2);
3193 sector = loadSectorMeta(sectordir, loadlayout != 2);
3195 catch(InvalidFilenameException &e)
3199 catch(FileNotGoodException &e)
3203 catch(std::exception &e)
3212 bool ServerMap::loadSectorFull(v2s16 p2d)
3214 DSTACK(__FUNCTION_NAME);
3216 MapSector *sector = NULL;
3218 // The directory layout we're going to load from.
3219 // 1 - original sectors/xxxxzzzz/
3220 // 2 - new sectors2/xxx/zzz/
3221 // If we load from anything but the latest structure, we will
3222 // immediately save to the new one, and remove the old.
3224 std::string sectordir1 = getSectorDir(p2d, 1);
3225 std::string sectordir;
3226 if(fs::PathExists(sectordir1))
3228 sectordir = sectordir1;
3233 sectordir = getSectorDir(p2d, 2);
3237 sector = loadSectorMeta(sectordir, loadlayout != 2);
3239 catch(InvalidFilenameException &e)
3243 catch(FileNotGoodException &e)
3247 catch(std::exception &e)
3255 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3257 std::vector<fs::DirListNode>::iterator i2;
3258 for(i2=list2.begin(); i2!=list2.end(); i2++)
3264 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3266 catch(InvalidFilenameException &e)
3268 // This catches unknown crap in directory
3274 infostream<<"Sector converted to new layout - deleting "<<
3275 sectordir1<<std::endl;
3276 fs::RecursiveDelete(sectordir1);
3283 void ServerMap::beginSave() {
3285 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3286 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3289 void ServerMap::endSave() {
3291 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3292 infostream<<"WARNING: endSave() failed, map might not have saved.";
3295 void ServerMap::saveBlock(MapBlock *block)
3297 DSTACK(__FUNCTION_NAME);
3299 Dummy blocks are not written
3301 if(block->isDummy())
3303 /*v3s16 p = block->getPos();
3304 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3305 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3309 // Format used for writing
3310 u8 version = SER_FMT_VER_HIGHEST;
3312 v3s16 p3d = block->getPos();
3316 v2s16 p2d(p3d.X, p3d.Z);
3317 std::string sectordir = getSectorDir(p2d);
3319 createDirs(sectordir);
3321 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3322 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3323 if(o.good() == false)
3324 throw FileNotGoodException("Cannot open block data");
3327 [0] u8 serialization version
3333 std::ostringstream o(std::ios_base::binary);
3335 o.write((char*)&version, 1);
3338 block->serialize(o, version);
3340 // Write extra data stored on disk
3341 block->serializeDiskExtra(o, version);
3343 // Write block to database
3345 std::string tmp = o.str();
3346 const char *bytes = tmp.c_str();
3348 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3349 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3350 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3351 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3352 int written = sqlite3_step(m_database_write);
3353 if(written != SQLITE_DONE)
3354 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3355 <<sqlite3_errmsg(m_database)<<std::endl;
3356 // Make ready for later reuse
3357 sqlite3_reset(m_database_write);
3359 // We just wrote it to the disk so clear modified flag
3360 block->resetModified();
3363 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3365 DSTACK(__FUNCTION_NAME);
3367 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3370 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3371 if(is.good() == false)
3372 throw FileNotGoodException("Cannot open block file");
3374 v3s16 p3d = getBlockPos(sectordir, blockfile);
3375 v2s16 p2d(p3d.X, p3d.Z);
3377 assert(sector->getPos() == p2d);
3379 u8 version = SER_FMT_VER_INVALID;
3380 is.read((char*)&version, 1);
3383 throw SerializationError("ServerMap::loadBlock(): Failed"
3384 " to read MapBlock version");
3386 /*u32 block_size = MapBlock::serializedLength(version);
3387 SharedBuffer<u8> data(block_size);
3388 is.read((char*)*data, block_size);*/
3390 // This will always return a sector because we're the server
3391 //MapSector *sector = emergeSector(p2d);
3393 MapBlock *block = NULL;
3394 bool created_new = false;
3395 block = sector->getBlockNoCreateNoEx(p3d.Y);
3398 block = sector->createBlankBlockNoInsert(p3d.Y);
3403 block->deSerialize(is, version);
3405 // Read extra data stored on disk
3406 block->deSerializeDiskExtra(is, version);
3408 // If it's a new block, insert it to the map
3410 sector->insertBlock(block);
3413 Save blocks loaded in old format in new format
3416 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3420 // Should be in database now, so delete the old file
3421 fs::RecursiveDelete(fullpath);
3424 // We just loaded it from the disk, so it's up-to-date.
3425 block->resetModified();
3428 catch(SerializationError &e)
3430 infostream<<"WARNING: Invalid block data on disk "
3431 <<"fullpath="<<fullpath
3432 <<" (SerializationError). "
3433 <<"what()="<<e.what()
3435 //" Ignoring. A new one will be generated.
3438 // TODO: Backup file; name is in fullpath.
3442 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3444 DSTACK(__FUNCTION_NAME);
3447 std::istringstream is(*blob, std::ios_base::binary);
3449 u8 version = SER_FMT_VER_INVALID;
3450 is.read((char*)&version, 1);
3453 throw SerializationError("ServerMap::loadBlock(): Failed"
3454 " to read MapBlock version");
3456 /*u32 block_size = MapBlock::serializedLength(version);
3457 SharedBuffer<u8> data(block_size);
3458 is.read((char*)*data, block_size);*/
3460 // This will always return a sector because we're the server
3461 //MapSector *sector = emergeSector(p2d);
3463 MapBlock *block = NULL;
3464 bool created_new = false;
3465 block = sector->getBlockNoCreateNoEx(p3d.Y);
3468 block = sector->createBlankBlockNoInsert(p3d.Y);
3473 block->deSerialize(is, version);
3475 // Read extra data stored on disk
3476 block->deSerializeDiskExtra(is, version);
3478 // If it's a new block, insert it to the map
3480 sector->insertBlock(block);
3483 Save blocks loaded in old format in new format
3486 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3491 // We just loaded it from, so it's up-to-date.
3492 block->resetModified();
3495 catch(SerializationError &e)
3497 infostream<<"WARNING: Invalid block data in database "
3498 <<" (SerializationError). "
3499 <<"what()="<<e.what()
3501 //" Ignoring. A new one will be generated.
3504 // TODO: Copy to a backup database.
3508 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3510 DSTACK(__FUNCTION_NAME);
3512 v2s16 p2d(blockpos.X, blockpos.Z);
3514 if(!loadFromFolders()) {
3517 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3518 infostream<<"WARNING: Could not bind block position for load: "
3519 <<sqlite3_errmsg(m_database)<<std::endl;
3520 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3522 Make sure sector is loaded
3524 MapSector *sector = createSector(p2d);
3529 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3530 size_t len = sqlite3_column_bytes(m_database_read, 0);
3532 std::string datastr(data, len);
3534 loadBlock(&datastr, blockpos, sector, false);
3536 sqlite3_step(m_database_read);
3537 // We should never get more than 1 row, so ok to reset
3538 sqlite3_reset(m_database_read);
3540 return getBlockNoCreateNoEx(blockpos);
3542 sqlite3_reset(m_database_read);
3544 // Not found in database, try the files
3547 // The directory layout we're going to load from.
3548 // 1 - original sectors/xxxxzzzz/
3549 // 2 - new sectors2/xxx/zzz/
3550 // If we load from anything but the latest structure, we will
3551 // immediately save to the new one, and remove the old.
3553 std::string sectordir1 = getSectorDir(p2d, 1);
3554 std::string sectordir;
3555 if(fs::PathExists(sectordir1))
3557 sectordir = sectordir1;
3562 sectordir = getSectorDir(p2d, 2);
3566 Make sure sector is loaded
3568 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3572 sector = loadSectorMeta(sectordir, loadlayout != 2);
3574 catch(InvalidFilenameException &e)
3578 catch(FileNotGoodException &e)
3582 catch(std::exception &e)
3589 Make sure file exists
3592 std::string blockfilename = getBlockFilename(blockpos);
3593 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3597 Load block and save it to the database
3599 loadBlock(sectordir, blockfilename, sector, true);
3600 return getBlockNoCreateNoEx(blockpos);
3603 void ServerMap::PrintInfo(std::ostream &out)
3614 ClientMap::ClientMap(
3616 MapDrawControl &control,
3617 scene::ISceneNode* parent,
3618 scene::ISceneManager* mgr,
3622 scene::ISceneNode(parent, mgr, id),
3625 m_camera_position(0,0,0),
3626 m_camera_direction(0,0,1),
3629 m_camera_mutex.Init();
3630 assert(m_camera_mutex.IsInitialized());
3632 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3633 BS*1000000,BS*1000000,BS*1000000);
3636 ClientMap::~ClientMap()
3638 /*JMutexAutoLock lock(mesh_mutex);
3647 MapSector * ClientMap::emergeSector(v2s16 p2d)
3649 DSTACK(__FUNCTION_NAME);
3650 // Check that it doesn't exist already
3652 return getSectorNoGenerate(p2d);
3654 catch(InvalidPositionException &e)
3659 ClientMapSector *sector = new ClientMapSector(this, p2d);
3662 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3663 m_sectors.insert(p2d, sector);
3670 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3672 DSTACK(__FUNCTION_NAME);
3673 ClientMapSector *sector = NULL;
3675 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3677 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3681 sector = (ClientMapSector*)n->getValue();
3682 assert(sector->getId() == MAPSECTOR_CLIENT);
3686 sector = new ClientMapSector(this, p2d);
3688 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3689 m_sectors.insert(p2d, sector);
3693 sector->deSerialize(is);
3697 void ClientMap::OnRegisterSceneNode()
3701 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3702 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3705 ISceneNode::OnRegisterSceneNode();
3708 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3710 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3711 DSTACK(__FUNCTION_NAME);
3713 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3716 This is called two times per frame, reset on the non-transparent one
3718 if(pass == scene::ESNRP_SOLID)
3720 m_last_drawn_sectors.clear();
3724 Get time for measuring timeout.
3726 Measuring time is very useful for long delays when the
3727 machine is swapping a lot.
3729 int time1 = time(0);
3731 //u32 daynight_ratio = m_client->getDayNightRatio();
3733 m_camera_mutex.Lock();
3734 v3f camera_position = m_camera_position;
3735 v3f camera_direction = m_camera_direction;
3736 f32 camera_fov = m_camera_fov;
3737 m_camera_mutex.Unlock();
3740 Get all blocks and draw all visible ones
3743 v3s16 cam_pos_nodes(
3744 camera_position.X / BS,
3745 camera_position.Y / BS,
3746 camera_position.Z / BS);
3748 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3750 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3751 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3753 // Take a fair amount as we will be dropping more out later
3755 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3756 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3757 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3759 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3760 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3761 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3763 u32 vertex_count = 0;
3764 u32 meshbuffer_count = 0;
3766 // For limiting number of mesh updates per frame
3767 u32 mesh_update_count = 0;
3769 u32 blocks_would_have_drawn = 0;
3770 u32 blocks_drawn = 0;
3772 int timecheck_counter = 0;
3773 core::map<v2s16, MapSector*>::Iterator si;
3774 si = m_sectors.getIterator();
3775 for(; si.atEnd() == false; si++)
3778 timecheck_counter++;
3779 if(timecheck_counter > 50)
3781 timecheck_counter = 0;
3782 int time2 = time(0);
3783 if(time2 > time1 + 4)
3785 infostream<<"ClientMap::renderMap(): "
3786 "Rendering takes ages, returning."
3793 MapSector *sector = si.getNode()->getValue();
3794 v2s16 sp = sector->getPos();
3796 if(m_control.range_all == false)
3798 if(sp.X < p_blocks_min.X
3799 || sp.X > p_blocks_max.X
3800 || sp.Y < p_blocks_min.Z
3801 || sp.Y > p_blocks_max.Z)
3805 core::list< MapBlock * > sectorblocks;
3806 sector->getBlocks(sectorblocks);
3812 u32 sector_blocks_drawn = 0;
3814 core::list< MapBlock * >::Iterator i;
3815 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3817 MapBlock *block = *i;
3820 Compare block position to camera position, skip
3821 if not seen on display
3824 float range = 100000 * BS;
3825 if(m_control.range_all == false)
3826 range = m_control.wanted_range * BS;
3829 if(isBlockInSight(block->getPos(), camera_position,
3830 camera_direction, camera_fov,
3831 range, &d) == false)
3836 // Okay, this block will be drawn. Reset usage timer.
3837 block->resetUsageTimer();
3839 // This is ugly (spherical distance limit?)
3840 /*if(m_control.range_all == false &&
3841 d - 0.5*BS*MAP_BLOCKSIZE > range)
3846 Update expired mesh (used for day/night change)
3848 It doesn't work exactly like it should now with the
3849 tasked mesh update but whatever.
3852 bool mesh_expired = false;
3855 JMutexAutoLock lock(block->mesh_mutex);
3857 mesh_expired = block->getMeshExpired();
3859 // Mesh has not been expired and there is no mesh:
3860 // block has no content
3861 if(block->mesh == NULL && mesh_expired == false)
3865 f32 faraway = BS*50;
3866 //f32 faraway = m_control.wanted_range * BS;
3869 This has to be done with the mesh_mutex unlocked
3871 // Pretty random but this should work somewhat nicely
3872 if(mesh_expired && (
3873 (mesh_update_count < 3
3874 && (d < faraway || mesh_update_count < 2)
3877 (m_control.range_all && mesh_update_count < 20)
3880 /*if(mesh_expired && mesh_update_count < 6
3881 && (d < faraway || mesh_update_count < 3))*/
3883 mesh_update_count++;
3885 // Mesh has been expired: generate new mesh
3886 //block->updateMesh(daynight_ratio);
3887 m_client->addUpdateMeshTask(block->getPos());
3889 mesh_expired = false;
3894 Draw the faces of the block
3897 JMutexAutoLock lock(block->mesh_mutex);
3899 scene::SMesh *mesh = block->mesh;
3904 blocks_would_have_drawn++;
3905 if(blocks_drawn >= m_control.wanted_max_blocks
3906 && m_control.range_all == false
3907 && d > m_control.wanted_min_range * BS)
3911 sector_blocks_drawn++;
3913 u32 c = mesh->getMeshBufferCount();
3914 meshbuffer_count += c;
3916 for(u32 i=0; i<c; i++)
3918 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3919 const video::SMaterial& material = buf->getMaterial();
3920 video::IMaterialRenderer* rnd =
3921 driver->getMaterialRenderer(material.MaterialType);
3922 bool transparent = (rnd && rnd->isTransparent());
3923 // Render transparent on transparent pass and likewise.
3924 if(transparent == is_transparent_pass)
3927 This *shouldn't* hurt too much because Irrlicht
3928 doesn't change opengl textures if the old
3929 material is set again.
3931 driver->setMaterial(buf->getMaterial());
3932 driver->drawMeshBuffer(buf);
3933 vertex_count += buf->getVertexCount();
3937 } // foreach sectorblocks
3939 if(sector_blocks_drawn != 0)
3941 m_last_drawn_sectors[sp] = true;
3945 if(pass == scene::ESNRP_SOLID){
3946 g_profiler->avg("CM: blocks drawn on solid pass", blocks_drawn);
3947 g_profiler->avg("CM: vertices drawn on solid pass", vertex_count);
3948 if(blocks_drawn != 0)
3949 g_profiler->avg("CM: solid meshbuffers per block",
3950 (float)meshbuffer_count / (float)blocks_drawn);
3952 g_profiler->avg("CM: blocks drawn on transparent pass", blocks_drawn);
3953 g_profiler->avg("CM: vertices drawn on transparent pass", vertex_count);
3956 m_control.blocks_drawn = blocks_drawn;
3957 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3959 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3960 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3963 void ClientMap::renderPostFx()
3965 // Sadly ISceneManager has no "post effects" render pass, in that case we
3966 // could just register for that and handle it in renderMap().
3968 m_camera_mutex.Lock();
3969 v3f camera_position = m_camera_position;
3970 m_camera_mutex.Unlock();
3972 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
3974 // - If the player is in a solid node, make everything black.
3975 // - If the player is in liquid, draw a semi-transparent overlay.
3976 ContentFeatures& features = content_features(n);
3977 video::SColor post_effect_color = features.post_effect_color;
3978 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
3980 post_effect_color = video::SColor(255, 0, 0, 0);
3982 if (post_effect_color.getAlpha() != 0)
3984 // Draw a full-screen rectangle
3985 video::IVideoDriver* driver = SceneManager->getVideoDriver();
3986 v2u32 ss = driver->getScreenSize();
3987 core::rect<s32> rect(0,0, ss.X, ss.Y);
3988 driver->draw2DRectangle(post_effect_color, rect);
3992 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3993 core::map<v3s16, MapBlock*> *affected_blocks)
3995 bool changed = false;
3997 Add it to all blocks touching it
4000 v3s16(0,0,0), // this
4001 v3s16(0,0,1), // back
4002 v3s16(0,1,0), // top
4003 v3s16(1,0,0), // right
4004 v3s16(0,0,-1), // front
4005 v3s16(0,-1,0), // bottom
4006 v3s16(-1,0,0), // left
4008 for(u16 i=0; i<7; i++)
4010 v3s16 p2 = p + dirs[i];
4011 // Block position of neighbor (or requested) node
4012 v3s16 blockpos = getNodeBlockPos(p2);
4013 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4014 if(blockref == NULL)
4016 // Relative position of requested node
4017 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4018 if(blockref->setTempMod(relpos, mod))
4023 if(changed && affected_blocks!=NULL)
4025 for(u16 i=0; i<7; i++)
4027 v3s16 p2 = p + dirs[i];
4028 // Block position of neighbor (or requested) node
4029 v3s16 blockpos = getNodeBlockPos(p2);
4030 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4031 if(blockref == NULL)
4033 affected_blocks->insert(blockpos, blockref);
4039 bool ClientMap::clearTempMod(v3s16 p,
4040 core::map<v3s16, MapBlock*> *affected_blocks)
4042 bool changed = false;
4044 v3s16(0,0,0), // this
4045 v3s16(0,0,1), // back
4046 v3s16(0,1,0), // top
4047 v3s16(1,0,0), // right
4048 v3s16(0,0,-1), // front
4049 v3s16(0,-1,0), // bottom
4050 v3s16(-1,0,0), // left
4052 for(u16 i=0; i<7; i++)
4054 v3s16 p2 = p + dirs[i];
4055 // Block position of neighbor (or requested) node
4056 v3s16 blockpos = getNodeBlockPos(p2);
4057 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4058 if(blockref == NULL)
4060 // Relative position of requested node
4061 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4062 if(blockref->clearTempMod(relpos))
4067 if(changed && affected_blocks!=NULL)
4069 for(u16 i=0; i<7; i++)
4071 v3s16 p2 = p + dirs[i];
4072 // Block position of neighbor (or requested) node
4073 v3s16 blockpos = getNodeBlockPos(p2);
4074 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4075 if(blockref == NULL)
4077 affected_blocks->insert(blockpos, blockref);
4083 void ClientMap::expireMeshes(bool only_daynight_diffed)
4085 TimeTaker timer("expireMeshes()");
4087 core::map<v2s16, MapSector*>::Iterator si;
4088 si = m_sectors.getIterator();
4089 for(; si.atEnd() == false; si++)
4091 MapSector *sector = si.getNode()->getValue();
4093 core::list< MapBlock * > sectorblocks;
4094 sector->getBlocks(sectorblocks);
4096 core::list< MapBlock * >::Iterator i;
4097 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4099 MapBlock *block = *i;
4101 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4107 JMutexAutoLock lock(block->mesh_mutex);
4108 if(block->mesh != NULL)
4110 /*block->mesh->drop();
4111 block->mesh = NULL;*/
4112 block->setMeshExpired(true);
4119 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4121 assert(mapType() == MAPTYPE_CLIENT);
4124 v3s16 p = blockpos + v3s16(0,0,0);
4125 MapBlock *b = getBlockNoCreate(p);
4126 b->updateMesh(daynight_ratio);
4127 //b->setMeshExpired(true);
4129 catch(InvalidPositionException &e){}
4132 v3s16 p = blockpos + v3s16(-1,0,0);
4133 MapBlock *b = getBlockNoCreate(p);
4134 b->updateMesh(daynight_ratio);
4135 //b->setMeshExpired(true);
4137 catch(InvalidPositionException &e){}
4139 v3s16 p = blockpos + v3s16(0,-1,0);
4140 MapBlock *b = getBlockNoCreate(p);
4141 b->updateMesh(daynight_ratio);
4142 //b->setMeshExpired(true);
4144 catch(InvalidPositionException &e){}
4146 v3s16 p = blockpos + v3s16(0,0,-1);
4147 MapBlock *b = getBlockNoCreate(p);
4148 b->updateMesh(daynight_ratio);
4149 //b->setMeshExpired(true);
4151 catch(InvalidPositionException &e){}
4156 Update mesh of block in which the node is, and if the node is at the
4157 leading edge, update the appropriate leading blocks too.
4159 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4167 v3s16 blockposes[4];
4168 for(u32 i=0; i<4; i++)
4170 v3s16 np = nodepos + dirs[i];
4171 blockposes[i] = getNodeBlockPos(np);
4172 // Don't update mesh of block if it has been done already
4173 bool already_updated = false;
4174 for(u32 j=0; j<i; j++)
4176 if(blockposes[j] == blockposes[i])
4178 already_updated = true;
4185 MapBlock *b = getBlockNoCreate(blockposes[i]);
4186 b->updateMesh(daynight_ratio);
4191 void ClientMap::PrintInfo(std::ostream &out)
4202 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4207 MapVoxelManipulator::~MapVoxelManipulator()
4209 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4213 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4215 TimeTaker timer1("emerge", &emerge_time);
4217 // Units of these are MapBlocks
4218 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4219 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4221 VoxelArea block_area_nodes
4222 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4224 addArea(block_area_nodes);
4226 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4227 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4228 for(s32 x=p_min.X; x<=p_max.X; x++)
4231 core::map<v3s16, bool>::Node *n;
4232 n = m_loaded_blocks.find(p);
4236 bool block_data_inexistent = false;
4239 TimeTaker timer1("emerge load", &emerge_load_time);
4241 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4242 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4244 a.print(infostream);
4245 infostream<<std::endl;*/
4247 MapBlock *block = m_map->getBlockNoCreate(p);
4248 if(block->isDummy())
4249 block_data_inexistent = true;
4251 block->copyTo(*this);
4253 catch(InvalidPositionException &e)
4255 block_data_inexistent = true;
4258 if(block_data_inexistent)
4260 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4261 // Fill with VOXELFLAG_INEXISTENT
4262 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4263 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4265 s32 i = m_area.index(a.MinEdge.X,y,z);
4266 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4270 m_loaded_blocks.insert(p, !block_data_inexistent);
4273 //infostream<<"emerge done"<<std::endl;
4277 SUGG: Add an option to only update eg. water and air nodes.
4278 This will make it interfere less with important stuff if
4281 void MapVoxelManipulator::blitBack
4282 (core::map<v3s16, MapBlock*> & modified_blocks)
4284 if(m_area.getExtent() == v3s16(0,0,0))
4287 //TimeTaker timer1("blitBack");
4289 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4290 <<m_loaded_blocks.size()<<std::endl;*/
4293 Initialize block cache
4295 v3s16 blockpos_last;
4296 MapBlock *block = NULL;
4297 bool block_checked_in_modified = false;
4299 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4300 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4301 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4305 u8 f = m_flags[m_area.index(p)];
4306 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4309 MapNode &n = m_data[m_area.index(p)];
4311 v3s16 blockpos = getNodeBlockPos(p);
4316 if(block == NULL || blockpos != blockpos_last){
4317 block = m_map->getBlockNoCreate(blockpos);
4318 blockpos_last = blockpos;
4319 block_checked_in_modified = false;
4322 // Calculate relative position in block
4323 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4325 // Don't continue if nothing has changed here
4326 if(block->getNode(relpos) == n)
4329 //m_map->setNode(m_area.MinEdge + p, n);
4330 block->setNode(relpos, n);
4333 Make sure block is in modified_blocks
4335 if(block_checked_in_modified == false)
4337 modified_blocks[blockpos] = block;
4338 block_checked_in_modified = true;
4341 catch(InvalidPositionException &e)
4347 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4348 MapVoxelManipulator(map),
4349 m_create_area(false)
4353 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4357 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4359 // Just create the area so that it can be pointed to
4360 VoxelManipulator::emerge(a, caller_id);
4363 void ManualMapVoxelManipulator::initialEmerge(
4364 v3s16 blockpos_min, v3s16 blockpos_max)
4366 TimeTaker timer1("initialEmerge", &emerge_time);
4368 // Units of these are MapBlocks
4369 v3s16 p_min = blockpos_min;
4370 v3s16 p_max = blockpos_max;
4372 VoxelArea block_area_nodes
4373 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4375 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4378 infostream<<"initialEmerge: area: ";
4379 block_area_nodes.print(infostream);
4380 infostream<<" ("<<size_MB<<"MB)";
4381 infostream<<std::endl;
4384 addArea(block_area_nodes);
4386 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4387 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4388 for(s32 x=p_min.X; x<=p_max.X; x++)
4391 core::map<v3s16, bool>::Node *n;
4392 n = m_loaded_blocks.find(p);
4396 bool block_data_inexistent = false;
4399 TimeTaker timer1("emerge load", &emerge_load_time);
4401 MapBlock *block = m_map->getBlockNoCreate(p);
4402 if(block->isDummy())
4403 block_data_inexistent = true;
4405 block->copyTo(*this);
4407 catch(InvalidPositionException &e)
4409 block_data_inexistent = true;
4412 if(block_data_inexistent)
4415 Mark area inexistent
4417 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4418 // Fill with VOXELFLAG_INEXISTENT
4419 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4420 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4422 s32 i = m_area.index(a.MinEdge.X,y,z);
4423 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4427 m_loaded_blocks.insert(p, !block_data_inexistent);
4431 void ManualMapVoxelManipulator::blitBackAll(
4432 core::map<v3s16, MapBlock*> * modified_blocks)
4434 if(m_area.getExtent() == v3s16(0,0,0))
4438 Copy data of all blocks
4440 for(core::map<v3s16, bool>::Iterator
4441 i = m_loaded_blocks.getIterator();
4442 i.atEnd() == false; i++)
4444 v3s16 p = i.getNode()->getKey();
4445 bool existed = i.getNode()->getValue();
4446 if(existed == false)
4448 // The Great Bug was found using this
4449 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4450 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4454 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4457 infostream<<"WARNING: "<<__FUNCTION_NAME
4458 <<": got NULL block "
4459 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4464 block->copyFrom(*this);
4467 modified_blocks->insert(p, block);