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);
2626 s16 ServerMap::findGroundLevel(v2s16 p2d)
2630 Uh, just do something random...
2632 // Find existing map from top to down
2635 v3s16 p(p2d.X, max, p2d.Y);
2636 for(; p.Y>min; p.Y--)
2638 MapNode n = getNodeNoEx(p);
2639 if(n.getContent() != CONTENT_IGNORE)
2644 // If this node is not air, go to plan b
2645 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2647 // Search existing walkable and return it
2648 for(; p.Y>min; p.Y--)
2650 MapNode n = getNodeNoEx(p);
2651 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2660 Determine from map generator noise functions
2663 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2666 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2667 //return (s16)level;
2670 void ServerMap::createDatabase() {
2673 e = sqlite3_exec(m_database,
2674 "CREATE TABLE IF NOT EXISTS `blocks` ("
2675 "`pos` INT NOT NULL PRIMARY KEY,"
2678 , NULL, NULL, NULL);
2679 if(e == SQLITE_ABORT)
2680 throw FileNotGoodException("Could not create database structure");
2682 infostream<<"Server: Database structure was created";
2685 void ServerMap::verifyDatabase() {
2690 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2691 bool needs_create = false;
2695 Open the database connection
2698 createDirs(m_savedir);
2700 if(!fs::PathExists(dbp))
2701 needs_create = true;
2703 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2704 if(d != SQLITE_OK) {
2705 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2706 throw FileNotGoodException("Cannot open database file");
2712 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2713 if(d != SQLITE_OK) {
2714 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2715 throw FileNotGoodException("Cannot prepare read statement");
2718 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2719 if(d != SQLITE_OK) {
2720 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2721 throw FileNotGoodException("Cannot prepare write statement");
2724 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2725 if(d != SQLITE_OK) {
2726 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2727 throw FileNotGoodException("Cannot prepare read statement");
2730 infostream<<"Server: Database opened"<<std::endl;
2734 bool ServerMap::loadFromFolders() {
2735 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2740 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2741 return (sqlite3_int64)pos.Z*16777216 +
2742 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2745 void ServerMap::createDirs(std::string path)
2747 if(fs::CreateAllDirs(path) == false)
2749 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2750 <<"\""<<path<<"\""<<std::endl;
2751 throw BaseException("ServerMap failed to create directory");
2755 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2761 snprintf(cc, 9, "%.4x%.4x",
2762 (unsigned int)pos.X&0xffff,
2763 (unsigned int)pos.Y&0xffff);
2765 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2767 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2768 (unsigned int)pos.X&0xfff,
2769 (unsigned int)pos.Y&0xfff);
2771 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2777 v2s16 ServerMap::getSectorPos(std::string dirname)
2781 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2782 assert(spos != std::string::npos);
2783 if(dirname.size() - spos == 8)
2786 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2788 else if(dirname.size() - spos == 3)
2791 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2792 // Sign-extend the 12 bit values up to 16 bits...
2793 if(x&0x800) x|=0xF000;
2794 if(y&0x800) y|=0xF000;
2801 v2s16 pos((s16)x, (s16)y);
2805 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2807 v2s16 p2d = getSectorPos(sectordir);
2809 if(blockfile.size() != 4){
2810 throw InvalidFilenameException("Invalid block filename");
2813 int r = sscanf(blockfile.c_str(), "%4x", &y);
2815 throw InvalidFilenameException("Invalid block filename");
2816 return v3s16(p2d.X, y, p2d.Y);
2819 std::string ServerMap::getBlockFilename(v3s16 p)
2822 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2826 void ServerMap::save(bool only_changed)
2828 DSTACK(__FUNCTION_NAME);
2829 if(m_map_saving_enabled == false)
2831 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2835 if(only_changed == false)
2836 infostream<<"ServerMap: Saving whole map, this can take time."
2839 if(only_changed == false || m_map_metadata_changed)
2844 u32 sector_meta_count = 0;
2845 u32 block_count = 0;
2846 u32 block_count_all = 0; // Number of blocks in memory
2849 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2850 for(; i.atEnd() == false; i++)
2852 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2853 assert(sector->getId() == MAPSECTOR_SERVER);
2855 if(sector->differs_from_disk || only_changed == false)
2857 saveSectorMeta(sector);
2858 sector_meta_count++;
2860 core::list<MapBlock*> blocks;
2861 sector->getBlocks(blocks);
2862 core::list<MapBlock*>::Iterator j;
2864 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2865 for(j=blocks.begin(); j!=blocks.end(); j++)
2867 MapBlock *block = *j;
2871 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2872 || only_changed == false)
2877 /*infostream<<"ServerMap: Written block ("
2878 <<block->getPos().X<<","
2879 <<block->getPos().Y<<","
2880 <<block->getPos().Z<<")"
2883 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
2889 Only print if something happened or saved whole map
2891 if(only_changed == false || sector_meta_count != 0
2892 || block_count != 0)
2894 infostream<<"ServerMap: Written: "
2895 <<sector_meta_count<<" sector metadata files, "
2896 <<block_count<<" block files"
2897 <<", "<<block_count_all<<" blocks in memory."
2902 static s32 unsignedToSigned(s32 i, s32 max_positive)
2904 if(i < max_positive)
2907 return i - 2*max_positive;
2910 // modulo of a negative number does not work consistently in C
2911 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2915 return mod - ((-i) % mod);
2918 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2920 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2922 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2924 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2925 return v3s16(x,y,z);
2928 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2930 if(loadFromFolders()){
2931 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2932 <<"all blocks that are stored in flat files"<<std::endl;
2938 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2940 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2941 v3s16 p = getIntegerAsBlock(block_i);
2942 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2948 void ServerMap::saveMapMeta()
2950 DSTACK(__FUNCTION_NAME);
2952 infostream<<"ServerMap::saveMapMeta(): "
2956 createDirs(m_savedir);
2958 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2959 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2960 if(os.good() == false)
2962 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2963 <<"could not open"<<fullpath<<std::endl;
2964 throw FileNotGoodException("Cannot open chunk metadata");
2968 params.setU64("seed", m_seed);
2970 params.writeLines(os);
2972 os<<"[end_of_params]\n";
2974 m_map_metadata_changed = false;
2977 void ServerMap::loadMapMeta()
2979 DSTACK(__FUNCTION_NAME);
2981 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2984 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2985 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2986 if(is.good() == false)
2988 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2989 <<"could not open"<<fullpath<<std::endl;
2990 throw FileNotGoodException("Cannot open map metadata");
2998 throw SerializationError
2999 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3001 std::getline(is, line);
3002 std::string trimmedline = trim(line);
3003 if(trimmedline == "[end_of_params]")
3005 params.parseConfigLine(line);
3008 m_seed = params.getU64("seed");
3010 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3013 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3015 DSTACK(__FUNCTION_NAME);
3016 // Format used for writing
3017 u8 version = SER_FMT_VER_HIGHEST;
3019 v2s16 pos = sector->getPos();
3020 std::string dir = getSectorDir(pos);
3023 std::string fullpath = dir + DIR_DELIM + "meta";
3024 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3025 if(o.good() == false)
3026 throw FileNotGoodException("Cannot open sector metafile");
3028 sector->serialize(o, version);
3030 sector->differs_from_disk = false;
3033 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3035 DSTACK(__FUNCTION_NAME);
3037 v2s16 p2d = getSectorPos(sectordir);
3039 ServerMapSector *sector = NULL;
3041 std::string fullpath = sectordir + DIR_DELIM + "meta";
3042 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3043 if(is.good() == false)
3045 // If the directory exists anyway, it probably is in some old
3046 // format. Just go ahead and create the sector.
3047 if(fs::PathExists(sectordir))
3049 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3050 <<fullpath<<" doesn't exist but directory does."
3051 <<" Continuing with a sector with no metadata."
3053 sector = new ServerMapSector(this, p2d);
3054 m_sectors.insert(p2d, sector);
3058 throw FileNotGoodException("Cannot open sector metafile");
3063 sector = ServerMapSector::deSerialize
3064 (is, this, p2d, m_sectors);
3066 saveSectorMeta(sector);
3069 sector->differs_from_disk = false;
3074 bool ServerMap::loadSectorMeta(v2s16 p2d)
3076 DSTACK(__FUNCTION_NAME);
3078 MapSector *sector = NULL;
3080 // The directory layout we're going to load from.
3081 // 1 - original sectors/xxxxzzzz/
3082 // 2 - new sectors2/xxx/zzz/
3083 // If we load from anything but the latest structure, we will
3084 // immediately save to the new one, and remove the old.
3086 std::string sectordir1 = getSectorDir(p2d, 1);
3087 std::string sectordir;
3088 if(fs::PathExists(sectordir1))
3090 sectordir = sectordir1;
3095 sectordir = getSectorDir(p2d, 2);
3099 sector = loadSectorMeta(sectordir, loadlayout != 2);
3101 catch(InvalidFilenameException &e)
3105 catch(FileNotGoodException &e)
3109 catch(std::exception &e)
3118 bool ServerMap::loadSectorFull(v2s16 p2d)
3120 DSTACK(__FUNCTION_NAME);
3122 MapSector *sector = NULL;
3124 // The directory layout we're going to load from.
3125 // 1 - original sectors/xxxxzzzz/
3126 // 2 - new sectors2/xxx/zzz/
3127 // If we load from anything but the latest structure, we will
3128 // immediately save to the new one, and remove the old.
3130 std::string sectordir1 = getSectorDir(p2d, 1);
3131 std::string sectordir;
3132 if(fs::PathExists(sectordir1))
3134 sectordir = sectordir1;
3139 sectordir = getSectorDir(p2d, 2);
3143 sector = loadSectorMeta(sectordir, loadlayout != 2);
3145 catch(InvalidFilenameException &e)
3149 catch(FileNotGoodException &e)
3153 catch(std::exception &e)
3161 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3163 std::vector<fs::DirListNode>::iterator i2;
3164 for(i2=list2.begin(); i2!=list2.end(); i2++)
3170 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3172 catch(InvalidFilenameException &e)
3174 // This catches unknown crap in directory
3180 infostream<<"Sector converted to new layout - deleting "<<
3181 sectordir1<<std::endl;
3182 fs::RecursiveDelete(sectordir1);
3189 void ServerMap::beginSave() {
3191 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3192 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3195 void ServerMap::endSave() {
3197 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3198 infostream<<"WARNING: endSave() failed, map might not have saved.";
3201 void ServerMap::saveBlock(MapBlock *block)
3203 DSTACK(__FUNCTION_NAME);
3205 Dummy blocks are not written
3207 if(block->isDummy())
3209 /*v3s16 p = block->getPos();
3210 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3211 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3215 // Format used for writing
3216 u8 version = SER_FMT_VER_HIGHEST;
3218 v3s16 p3d = block->getPos();
3222 v2s16 p2d(p3d.X, p3d.Z);
3223 std::string sectordir = getSectorDir(p2d);
3225 createDirs(sectordir);
3227 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3228 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3229 if(o.good() == false)
3230 throw FileNotGoodException("Cannot open block data");
3233 [0] u8 serialization version
3239 std::ostringstream o(std::ios_base::binary);
3241 o.write((char*)&version, 1);
3244 block->serialize(o, version);
3246 // Write extra data stored on disk
3247 block->serializeDiskExtra(o, version);
3249 // Write block to database
3251 std::string tmp = o.str();
3252 const char *bytes = tmp.c_str();
3254 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3255 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3256 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3257 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3258 int written = sqlite3_step(m_database_write);
3259 if(written != SQLITE_DONE)
3260 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3261 <<sqlite3_errmsg(m_database)<<std::endl;
3262 // Make ready for later reuse
3263 sqlite3_reset(m_database_write);
3265 // We just wrote it to the disk so clear modified flag
3266 block->resetModified();
3269 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3271 DSTACK(__FUNCTION_NAME);
3273 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3276 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3277 if(is.good() == false)
3278 throw FileNotGoodException("Cannot open block file");
3280 v3s16 p3d = getBlockPos(sectordir, blockfile);
3281 v2s16 p2d(p3d.X, p3d.Z);
3283 assert(sector->getPos() == p2d);
3285 u8 version = SER_FMT_VER_INVALID;
3286 is.read((char*)&version, 1);
3289 throw SerializationError("ServerMap::loadBlock(): Failed"
3290 " to read MapBlock version");
3292 /*u32 block_size = MapBlock::serializedLength(version);
3293 SharedBuffer<u8> data(block_size);
3294 is.read((char*)*data, block_size);*/
3296 // This will always return a sector because we're the server
3297 //MapSector *sector = emergeSector(p2d);
3299 MapBlock *block = NULL;
3300 bool created_new = false;
3301 block = sector->getBlockNoCreateNoEx(p3d.Y);
3304 block = sector->createBlankBlockNoInsert(p3d.Y);
3309 block->deSerialize(is, version);
3311 // Read extra data stored on disk
3312 block->deSerializeDiskExtra(is, version);
3314 // If it's a new block, insert it to the map
3316 sector->insertBlock(block);
3319 Save blocks loaded in old format in new format
3322 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3326 // Should be in database now, so delete the old file
3327 fs::RecursiveDelete(fullpath);
3330 // We just loaded it from the disk, so it's up-to-date.
3331 block->resetModified();
3334 catch(SerializationError &e)
3336 infostream<<"WARNING: Invalid block data on disk "
3337 <<"fullpath="<<fullpath
3338 <<" (SerializationError). "
3339 <<"what()="<<e.what()
3341 //" Ignoring. A new one will be generated.
3344 // TODO: Backup file; name is in fullpath.
3348 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3350 DSTACK(__FUNCTION_NAME);
3353 std::istringstream is(*blob, std::ios_base::binary);
3355 u8 version = SER_FMT_VER_INVALID;
3356 is.read((char*)&version, 1);
3359 throw SerializationError("ServerMap::loadBlock(): Failed"
3360 " to read MapBlock version");
3362 /*u32 block_size = MapBlock::serializedLength(version);
3363 SharedBuffer<u8> data(block_size);
3364 is.read((char*)*data, block_size);*/
3366 // This will always return a sector because we're the server
3367 //MapSector *sector = emergeSector(p2d);
3369 MapBlock *block = NULL;
3370 bool created_new = false;
3371 block = sector->getBlockNoCreateNoEx(p3d.Y);
3374 block = sector->createBlankBlockNoInsert(p3d.Y);
3379 block->deSerialize(is, version);
3381 // Read extra data stored on disk
3382 block->deSerializeDiskExtra(is, version);
3384 // If it's a new block, insert it to the map
3386 sector->insertBlock(block);
3389 Save blocks loaded in old format in new format
3392 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3397 // We just loaded it from, so it's up-to-date.
3398 block->resetModified();
3401 catch(SerializationError &e)
3403 infostream<<"WARNING: Invalid block data in database "
3404 <<" (SerializationError). "
3405 <<"what()="<<e.what()
3407 //" Ignoring. A new one will be generated.
3410 // TODO: Copy to a backup database.
3414 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3416 DSTACK(__FUNCTION_NAME);
3418 v2s16 p2d(blockpos.X, blockpos.Z);
3420 if(!loadFromFolders()) {
3423 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3424 infostream<<"WARNING: Could not bind block position for load: "
3425 <<sqlite3_errmsg(m_database)<<std::endl;
3426 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3428 Make sure sector is loaded
3430 MapSector *sector = createSector(p2d);
3435 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3436 size_t len = sqlite3_column_bytes(m_database_read, 0);
3438 std::string datastr(data, len);
3440 loadBlock(&datastr, blockpos, sector, false);
3442 sqlite3_step(m_database_read);
3443 // We should never get more than 1 row, so ok to reset
3444 sqlite3_reset(m_database_read);
3446 return getBlockNoCreateNoEx(blockpos);
3448 sqlite3_reset(m_database_read);
3450 // Not found in database, try the files
3453 // The directory layout we're going to load from.
3454 // 1 - original sectors/xxxxzzzz/
3455 // 2 - new sectors2/xxx/zzz/
3456 // If we load from anything but the latest structure, we will
3457 // immediately save to the new one, and remove the old.
3459 std::string sectordir1 = getSectorDir(p2d, 1);
3460 std::string sectordir;
3461 if(fs::PathExists(sectordir1))
3463 sectordir = sectordir1;
3468 sectordir = getSectorDir(p2d, 2);
3472 Make sure sector is loaded
3474 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3478 sector = loadSectorMeta(sectordir, loadlayout != 2);
3480 catch(InvalidFilenameException &e)
3484 catch(FileNotGoodException &e)
3488 catch(std::exception &e)
3495 Make sure file exists
3498 std::string blockfilename = getBlockFilename(blockpos);
3499 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3503 Load block and save it to the database
3505 loadBlock(sectordir, blockfilename, sector, true);
3506 return getBlockNoCreateNoEx(blockpos);
3509 void ServerMap::PrintInfo(std::ostream &out)
3520 ClientMap::ClientMap(
3522 MapDrawControl &control,
3523 scene::ISceneNode* parent,
3524 scene::ISceneManager* mgr,
3528 scene::ISceneNode(parent, mgr, id),
3531 m_camera_position(0,0,0),
3532 m_camera_direction(0,0,1),
3535 m_camera_mutex.Init();
3536 assert(m_camera_mutex.IsInitialized());
3538 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3539 BS*1000000,BS*1000000,BS*1000000);
3542 ClientMap::~ClientMap()
3544 /*JMutexAutoLock lock(mesh_mutex);
3553 MapSector * ClientMap::emergeSector(v2s16 p2d)
3555 DSTACK(__FUNCTION_NAME);
3556 // Check that it doesn't exist already
3558 return getSectorNoGenerate(p2d);
3560 catch(InvalidPositionException &e)
3565 ClientMapSector *sector = new ClientMapSector(this, p2d);
3568 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3569 m_sectors.insert(p2d, sector);
3576 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3578 DSTACK(__FUNCTION_NAME);
3579 ClientMapSector *sector = NULL;
3581 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3583 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3587 sector = (ClientMapSector*)n->getValue();
3588 assert(sector->getId() == MAPSECTOR_CLIENT);
3592 sector = new ClientMapSector(this, p2d);
3594 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3595 m_sectors.insert(p2d, sector);
3599 sector->deSerialize(is);
3603 void ClientMap::OnRegisterSceneNode()
3607 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3608 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3611 ISceneNode::OnRegisterSceneNode();
3614 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3616 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3617 DSTACK(__FUNCTION_NAME);
3619 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3622 This is called two times per frame, reset on the non-transparent one
3624 if(pass == scene::ESNRP_SOLID)
3626 m_last_drawn_sectors.clear();
3630 Get time for measuring timeout.
3632 Measuring time is very useful for long delays when the
3633 machine is swapping a lot.
3635 int time1 = time(0);
3637 //u32 daynight_ratio = m_client->getDayNightRatio();
3639 m_camera_mutex.Lock();
3640 v3f camera_position = m_camera_position;
3641 v3f camera_direction = m_camera_direction;
3642 f32 camera_fov = m_camera_fov;
3643 m_camera_mutex.Unlock();
3646 Get all blocks and draw all visible ones
3649 v3s16 cam_pos_nodes(
3650 camera_position.X / BS,
3651 camera_position.Y / BS,
3652 camera_position.Z / BS);
3654 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3656 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3657 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3659 // Take a fair amount as we will be dropping more out later
3660 // Umm... these additions are a bit strange but they are needed.
3662 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3663 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3664 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3666 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3667 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3668 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3670 u32 vertex_count = 0;
3671 u32 meshbuffer_count = 0;
3673 // For limiting number of mesh updates per frame
3674 u32 mesh_update_count = 0;
3676 // Number of blocks in rendering range
3677 u32 blocks_in_range = 0;
3678 // Number of blocks in rendering range but don't have a mesh
3679 u32 blocks_in_range_without_mesh = 0;
3680 // Blocks that had mesh that would have been drawn according to
3681 // rendering range (if max blocks limit didn't kick in)
3682 u32 blocks_would_have_drawn = 0;
3683 // Blocks that were drawn and had a mesh
3684 u32 blocks_drawn = 0;
3685 // Blocks which had a corresponding meshbuffer for this pass
3686 u32 blocks_had_pass_meshbuf = 0;
3687 // Blocks from which stuff was actually drawn
3688 u32 blocks_without_stuff = 0;
3690 int timecheck_counter = 0;
3691 core::map<v2s16, MapSector*>::Iterator si;
3692 si = m_sectors.getIterator();
3693 for(; si.atEnd() == false; si++)
3696 timecheck_counter++;
3697 if(timecheck_counter > 50)
3699 timecheck_counter = 0;
3700 int time2 = time(0);
3701 if(time2 > time1 + 4)
3703 infostream<<"ClientMap::renderMap(): "
3704 "Rendering takes ages, returning."
3711 MapSector *sector = si.getNode()->getValue();
3712 v2s16 sp = sector->getPos();
3714 if(m_control.range_all == false)
3716 if(sp.X < p_blocks_min.X
3717 || sp.X > p_blocks_max.X
3718 || sp.Y < p_blocks_min.Z
3719 || sp.Y > p_blocks_max.Z)
3723 core::list< MapBlock * > sectorblocks;
3724 sector->getBlocks(sectorblocks);
3730 u32 sector_blocks_drawn = 0;
3732 core::list< MapBlock * >::Iterator i;
3733 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3735 MapBlock *block = *i;
3738 Compare block position to camera position, skip
3739 if not seen on display
3742 float range = 100000 * BS;
3743 if(m_control.range_all == false)
3744 range = m_control.wanted_range * BS;
3747 if(isBlockInSight(block->getPos(), camera_position,
3748 camera_direction, camera_fov,
3749 range, &d) == false)
3754 // Okay, this block will be drawn. Reset usage timer.
3755 block->resetUsageTimer();
3757 // This is ugly (spherical distance limit?)
3758 /*if(m_control.range_all == false &&
3759 d - 0.5*BS*MAP_BLOCKSIZE > range)
3766 Update expired mesh (used for day/night change)
3768 It doesn't work exactly like it should now with the
3769 tasked mesh update but whatever.
3772 bool mesh_expired = false;
3775 JMutexAutoLock lock(block->mesh_mutex);
3777 mesh_expired = block->getMeshExpired();
3779 // Mesh has not been expired and there is no mesh:
3780 // block has no content
3781 if(block->mesh == NULL && mesh_expired == false){
3782 blocks_in_range_without_mesh++;
3787 f32 faraway = BS*50;
3788 //f32 faraway = m_control.wanted_range * BS;
3791 This has to be done with the mesh_mutex unlocked
3793 // Pretty random but this should work somewhat nicely
3794 if(mesh_expired && (
3795 (mesh_update_count < 3
3796 && (d < faraway || mesh_update_count < 2)
3799 (m_control.range_all && mesh_update_count < 20)
3802 /*if(mesh_expired && mesh_update_count < 6
3803 && (d < faraway || mesh_update_count < 3))*/
3805 mesh_update_count++;
3807 // Mesh has been expired: generate new mesh
3808 //block->updateMesh(daynight_ratio);
3809 m_client->addUpdateMeshTask(block->getPos());
3811 mesh_expired = false;
3816 Draw the faces of the block
3819 JMutexAutoLock lock(block->mesh_mutex);
3821 scene::SMesh *mesh = block->mesh;
3824 blocks_in_range_without_mesh++;
3828 blocks_would_have_drawn++;
3829 if(blocks_drawn >= m_control.wanted_max_blocks
3830 && m_control.range_all == false
3831 && d > m_control.wanted_min_range * BS)
3835 sector_blocks_drawn++;
3837 u32 c = mesh->getMeshBufferCount();
3838 bool stuff_actually_drawn = false;
3839 for(u32 i=0; i<c; i++)
3841 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3842 const video::SMaterial& material = buf->getMaterial();
3843 video::IMaterialRenderer* rnd =
3844 driver->getMaterialRenderer(material.MaterialType);
3845 bool transparent = (rnd && rnd->isTransparent());
3846 // Render transparent on transparent pass and likewise.
3847 if(transparent == is_transparent_pass)
3849 if(buf->getVertexCount() == 0)
3850 errorstream<<"Block ["<<analyze_block(block)
3851 <<"] contains an empty meshbuf"<<std::endl;
3853 This *shouldn't* hurt too much because Irrlicht
3854 doesn't change opengl textures if the old
3855 material has the same texture.
3857 driver->setMaterial(buf->getMaterial());
3858 driver->drawMeshBuffer(buf);
3859 vertex_count += buf->getVertexCount();
3861 stuff_actually_drawn = true;
3864 if(stuff_actually_drawn)
3865 blocks_had_pass_meshbuf++;
3867 blocks_without_stuff++;
3869 } // foreach sectorblocks
3871 if(sector_blocks_drawn != 0)
3873 m_last_drawn_sectors[sp] = true;
3877 std::string prefix = "CM: ";
3879 // Log only on solid pass because values are the same
3880 if(pass == scene::ESNRP_SOLID){
3881 g_profiler->avg(prefix+"blocks in range", blocks_in_range);
3882 if(blocks_in_range != 0)
3883 g_profiler->avg(prefix+"blocks in range without mesh (frac)",
3884 (float)blocks_in_range_without_mesh/blocks_in_range);
3885 g_profiler->avg(prefix+"blocks drawn", blocks_drawn);
3888 if(pass == scene::ESNRP_SOLID)
3889 prefix = "CM: solid: ";
3891 prefix = "CM: transparent: ";
3893 g_profiler->avg(prefix+"vertices drawn", vertex_count);
3894 if(blocks_had_pass_meshbuf != 0)
3895 g_profiler->avg(prefix+"meshbuffers per block",
3896 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
3897 if(blocks_drawn != 0)
3898 g_profiler->avg(prefix+"empty blocks (frac)",
3899 (float)blocks_without_stuff / blocks_drawn);
3901 m_control.blocks_drawn = blocks_drawn;
3902 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3904 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3905 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3908 void ClientMap::renderPostFx()
3910 // Sadly ISceneManager has no "post effects" render pass, in that case we
3911 // could just register for that and handle it in renderMap().
3913 m_camera_mutex.Lock();
3914 v3f camera_position = m_camera_position;
3915 m_camera_mutex.Unlock();
3917 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
3919 // - If the player is in a solid node, make everything black.
3920 // - If the player is in liquid, draw a semi-transparent overlay.
3921 ContentFeatures& features = content_features(n);
3922 video::SColor post_effect_color = features.post_effect_color;
3923 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
3925 post_effect_color = video::SColor(255, 0, 0, 0);
3927 if (post_effect_color.getAlpha() != 0)
3929 // Draw a full-screen rectangle
3930 video::IVideoDriver* driver = SceneManager->getVideoDriver();
3931 v2u32 ss = driver->getScreenSize();
3932 core::rect<s32> rect(0,0, ss.X, ss.Y);
3933 driver->draw2DRectangle(post_effect_color, rect);
3937 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3938 core::map<v3s16, MapBlock*> *affected_blocks)
3940 bool changed = false;
3942 Add it to all blocks touching it
3945 v3s16(0,0,0), // this
3946 v3s16(0,0,1), // back
3947 v3s16(0,1,0), // top
3948 v3s16(1,0,0), // right
3949 v3s16(0,0,-1), // front
3950 v3s16(0,-1,0), // bottom
3951 v3s16(-1,0,0), // left
3953 for(u16 i=0; i<7; i++)
3955 v3s16 p2 = p + dirs[i];
3956 // Block position of neighbor (or requested) node
3957 v3s16 blockpos = getNodeBlockPos(p2);
3958 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3959 if(blockref == NULL)
3961 // Relative position of requested node
3962 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3963 if(blockref->setTempMod(relpos, mod))
3968 if(changed && affected_blocks!=NULL)
3970 for(u16 i=0; i<7; i++)
3972 v3s16 p2 = p + dirs[i];
3973 // Block position of neighbor (or requested) node
3974 v3s16 blockpos = getNodeBlockPos(p2);
3975 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3976 if(blockref == NULL)
3978 affected_blocks->insert(blockpos, blockref);
3984 bool ClientMap::clearTempMod(v3s16 p,
3985 core::map<v3s16, MapBlock*> *affected_blocks)
3987 bool changed = false;
3989 v3s16(0,0,0), // this
3990 v3s16(0,0,1), // back
3991 v3s16(0,1,0), // top
3992 v3s16(1,0,0), // right
3993 v3s16(0,0,-1), // front
3994 v3s16(0,-1,0), // bottom
3995 v3s16(-1,0,0), // left
3997 for(u16 i=0; i<7; i++)
3999 v3s16 p2 = p + dirs[i];
4000 // Block position of neighbor (or requested) node
4001 v3s16 blockpos = getNodeBlockPos(p2);
4002 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4003 if(blockref == NULL)
4005 // Relative position of requested node
4006 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4007 if(blockref->clearTempMod(relpos))
4012 if(changed && affected_blocks!=NULL)
4014 for(u16 i=0; i<7; i++)
4016 v3s16 p2 = p + dirs[i];
4017 // Block position of neighbor (or requested) node
4018 v3s16 blockpos = getNodeBlockPos(p2);
4019 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4020 if(blockref == NULL)
4022 affected_blocks->insert(blockpos, blockref);
4028 void ClientMap::expireMeshes(bool only_daynight_diffed)
4030 TimeTaker timer("expireMeshes()");
4032 core::map<v2s16, MapSector*>::Iterator si;
4033 si = m_sectors.getIterator();
4034 for(; si.atEnd() == false; si++)
4036 MapSector *sector = si.getNode()->getValue();
4038 core::list< MapBlock * > sectorblocks;
4039 sector->getBlocks(sectorblocks);
4041 core::list< MapBlock * >::Iterator i;
4042 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4044 MapBlock *block = *i;
4046 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4052 JMutexAutoLock lock(block->mesh_mutex);
4053 if(block->mesh != NULL)
4055 /*block->mesh->drop();
4056 block->mesh = NULL;*/
4057 block->setMeshExpired(true);
4064 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4066 assert(mapType() == MAPTYPE_CLIENT);
4069 v3s16 p = blockpos + v3s16(0,0,0);
4070 MapBlock *b = getBlockNoCreate(p);
4071 b->updateMesh(daynight_ratio);
4072 //b->setMeshExpired(true);
4074 catch(InvalidPositionException &e){}
4077 v3s16 p = blockpos + v3s16(-1,0,0);
4078 MapBlock *b = getBlockNoCreate(p);
4079 b->updateMesh(daynight_ratio);
4080 //b->setMeshExpired(true);
4082 catch(InvalidPositionException &e){}
4084 v3s16 p = blockpos + v3s16(0,-1,0);
4085 MapBlock *b = getBlockNoCreate(p);
4086 b->updateMesh(daynight_ratio);
4087 //b->setMeshExpired(true);
4089 catch(InvalidPositionException &e){}
4091 v3s16 p = blockpos + v3s16(0,0,-1);
4092 MapBlock *b = getBlockNoCreate(p);
4093 b->updateMesh(daynight_ratio);
4094 //b->setMeshExpired(true);
4096 catch(InvalidPositionException &e){}
4101 Update mesh of block in which the node is, and if the node is at the
4102 leading edge, update the appropriate leading blocks too.
4104 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4112 v3s16 blockposes[4];
4113 for(u32 i=0; i<4; i++)
4115 v3s16 np = nodepos + dirs[i];
4116 blockposes[i] = getNodeBlockPos(np);
4117 // Don't update mesh of block if it has been done already
4118 bool already_updated = false;
4119 for(u32 j=0; j<i; j++)
4121 if(blockposes[j] == blockposes[i])
4123 already_updated = true;
4130 MapBlock *b = getBlockNoCreate(blockposes[i]);
4131 b->updateMesh(daynight_ratio);
4136 void ClientMap::PrintInfo(std::ostream &out)
4147 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4152 MapVoxelManipulator::~MapVoxelManipulator()
4154 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4158 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4160 TimeTaker timer1("emerge", &emerge_time);
4162 // Units of these are MapBlocks
4163 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4164 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4166 VoxelArea block_area_nodes
4167 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4169 addArea(block_area_nodes);
4171 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4172 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4173 for(s32 x=p_min.X; x<=p_max.X; x++)
4176 core::map<v3s16, bool>::Node *n;
4177 n = m_loaded_blocks.find(p);
4181 bool block_data_inexistent = false;
4184 TimeTaker timer1("emerge load", &emerge_load_time);
4186 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4187 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4189 a.print(infostream);
4190 infostream<<std::endl;*/
4192 MapBlock *block = m_map->getBlockNoCreate(p);
4193 if(block->isDummy())
4194 block_data_inexistent = true;
4196 block->copyTo(*this);
4198 catch(InvalidPositionException &e)
4200 block_data_inexistent = true;
4203 if(block_data_inexistent)
4205 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4206 // Fill with VOXELFLAG_INEXISTENT
4207 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4208 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4210 s32 i = m_area.index(a.MinEdge.X,y,z);
4211 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4215 m_loaded_blocks.insert(p, !block_data_inexistent);
4218 //infostream<<"emerge done"<<std::endl;
4222 SUGG: Add an option to only update eg. water and air nodes.
4223 This will make it interfere less with important stuff if
4226 void MapVoxelManipulator::blitBack
4227 (core::map<v3s16, MapBlock*> & modified_blocks)
4229 if(m_area.getExtent() == v3s16(0,0,0))
4232 //TimeTaker timer1("blitBack");
4234 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4235 <<m_loaded_blocks.size()<<std::endl;*/
4238 Initialize block cache
4240 v3s16 blockpos_last;
4241 MapBlock *block = NULL;
4242 bool block_checked_in_modified = false;
4244 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4245 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4246 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4250 u8 f = m_flags[m_area.index(p)];
4251 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4254 MapNode &n = m_data[m_area.index(p)];
4256 v3s16 blockpos = getNodeBlockPos(p);
4261 if(block == NULL || blockpos != blockpos_last){
4262 block = m_map->getBlockNoCreate(blockpos);
4263 blockpos_last = blockpos;
4264 block_checked_in_modified = false;
4267 // Calculate relative position in block
4268 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4270 // Don't continue if nothing has changed here
4271 if(block->getNode(relpos) == n)
4274 //m_map->setNode(m_area.MinEdge + p, n);
4275 block->setNode(relpos, n);
4278 Make sure block is in modified_blocks
4280 if(block_checked_in_modified == false)
4282 modified_blocks[blockpos] = block;
4283 block_checked_in_modified = true;
4286 catch(InvalidPositionException &e)
4292 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4293 MapVoxelManipulator(map),
4294 m_create_area(false)
4298 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4302 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4304 // Just create the area so that it can be pointed to
4305 VoxelManipulator::emerge(a, caller_id);
4308 void ManualMapVoxelManipulator::initialEmerge(
4309 v3s16 blockpos_min, v3s16 blockpos_max)
4311 TimeTaker timer1("initialEmerge", &emerge_time);
4313 // Units of these are MapBlocks
4314 v3s16 p_min = blockpos_min;
4315 v3s16 p_max = blockpos_max;
4317 VoxelArea block_area_nodes
4318 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4320 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4323 infostream<<"initialEmerge: area: ";
4324 block_area_nodes.print(infostream);
4325 infostream<<" ("<<size_MB<<"MB)";
4326 infostream<<std::endl;
4329 addArea(block_area_nodes);
4331 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4332 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4333 for(s32 x=p_min.X; x<=p_max.X; x++)
4336 core::map<v3s16, bool>::Node *n;
4337 n = m_loaded_blocks.find(p);
4341 bool block_data_inexistent = false;
4344 TimeTaker timer1("emerge load", &emerge_load_time);
4346 MapBlock *block = m_map->getBlockNoCreate(p);
4347 if(block->isDummy())
4348 block_data_inexistent = true;
4350 block->copyTo(*this);
4352 catch(InvalidPositionException &e)
4354 block_data_inexistent = true;
4357 if(block_data_inexistent)
4360 Mark area inexistent
4362 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4363 // Fill with VOXELFLAG_INEXISTENT
4364 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4365 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4367 s32 i = m_area.index(a.MinEdge.X,y,z);
4368 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4372 m_loaded_blocks.insert(p, !block_data_inexistent);
4376 void ManualMapVoxelManipulator::blitBackAll(
4377 core::map<v3s16, MapBlock*> * modified_blocks)
4379 if(m_area.getExtent() == v3s16(0,0,0))
4383 Copy data of all blocks
4385 for(core::map<v3s16, bool>::Iterator
4386 i = m_loaded_blocks.getIterator();
4387 i.atEnd() == false; i++)
4389 v3s16 p = i.getNode()->getKey();
4390 bool existed = i.getNode()->getValue();
4391 if(existed == false)
4393 // The Great Bug was found using this
4394 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4395 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4399 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4402 infostream<<"WARNING: "<<__FUNCTION_NAME
4403 <<": got NULL block "
4404 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4409 block->copyFrom(*this);
4412 modified_blocks->insert(p, block);