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
3661 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3662 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3663 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3665 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3666 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3667 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3669 u32 vertex_count = 0;
3670 u32 meshbuffer_count = 0;
3672 // For limiting number of mesh updates per frame
3673 u32 mesh_update_count = 0;
3675 u32 blocks_would_have_drawn = 0;
3676 u32 blocks_drawn = 0;
3678 int timecheck_counter = 0;
3679 core::map<v2s16, MapSector*>::Iterator si;
3680 si = m_sectors.getIterator();
3681 for(; si.atEnd() == false; si++)
3684 timecheck_counter++;
3685 if(timecheck_counter > 50)
3687 timecheck_counter = 0;
3688 int time2 = time(0);
3689 if(time2 > time1 + 4)
3691 infostream<<"ClientMap::renderMap(): "
3692 "Rendering takes ages, returning."
3699 MapSector *sector = si.getNode()->getValue();
3700 v2s16 sp = sector->getPos();
3702 if(m_control.range_all == false)
3704 if(sp.X < p_blocks_min.X
3705 || sp.X > p_blocks_max.X
3706 || sp.Y < p_blocks_min.Z
3707 || sp.Y > p_blocks_max.Z)
3711 core::list< MapBlock * > sectorblocks;
3712 sector->getBlocks(sectorblocks);
3718 u32 sector_blocks_drawn = 0;
3720 core::list< MapBlock * >::Iterator i;
3721 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3723 MapBlock *block = *i;
3726 Compare block position to camera position, skip
3727 if not seen on display
3730 float range = 100000 * BS;
3731 if(m_control.range_all == false)
3732 range = m_control.wanted_range * BS;
3735 if(isBlockInSight(block->getPos(), camera_position,
3736 camera_direction, camera_fov,
3737 range, &d) == false)
3742 // Okay, this block will be drawn. Reset usage timer.
3743 block->resetUsageTimer();
3745 // This is ugly (spherical distance limit?)
3746 /*if(m_control.range_all == false &&
3747 d - 0.5*BS*MAP_BLOCKSIZE > range)
3752 Update expired mesh (used for day/night change)
3754 It doesn't work exactly like it should now with the
3755 tasked mesh update but whatever.
3758 bool mesh_expired = false;
3761 JMutexAutoLock lock(block->mesh_mutex);
3763 mesh_expired = block->getMeshExpired();
3765 // Mesh has not been expired and there is no mesh:
3766 // block has no content
3767 if(block->mesh == NULL && mesh_expired == false)
3771 f32 faraway = BS*50;
3772 //f32 faraway = m_control.wanted_range * BS;
3775 This has to be done with the mesh_mutex unlocked
3777 // Pretty random but this should work somewhat nicely
3778 if(mesh_expired && (
3779 (mesh_update_count < 3
3780 && (d < faraway || mesh_update_count < 2)
3783 (m_control.range_all && mesh_update_count < 20)
3786 /*if(mesh_expired && mesh_update_count < 6
3787 && (d < faraway || mesh_update_count < 3))*/
3789 mesh_update_count++;
3791 // Mesh has been expired: generate new mesh
3792 //block->updateMesh(daynight_ratio);
3793 m_client->addUpdateMeshTask(block->getPos());
3795 mesh_expired = false;
3800 Draw the faces of the block
3803 JMutexAutoLock lock(block->mesh_mutex);
3805 scene::SMesh *mesh = block->mesh;
3810 blocks_would_have_drawn++;
3811 if(blocks_drawn >= m_control.wanted_max_blocks
3812 && m_control.range_all == false
3813 && d > m_control.wanted_min_range * BS)
3817 sector_blocks_drawn++;
3819 u32 c = mesh->getMeshBufferCount();
3820 meshbuffer_count += c;
3822 for(u32 i=0; i<c; i++)
3824 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3825 const video::SMaterial& material = buf->getMaterial();
3826 video::IMaterialRenderer* rnd =
3827 driver->getMaterialRenderer(material.MaterialType);
3828 bool transparent = (rnd && rnd->isTransparent());
3829 // Render transparent on transparent pass and likewise.
3830 if(transparent == is_transparent_pass)
3833 This *shouldn't* hurt too much because Irrlicht
3834 doesn't change opengl textures if the old
3835 material is set again.
3837 driver->setMaterial(buf->getMaterial());
3838 driver->drawMeshBuffer(buf);
3839 vertex_count += buf->getVertexCount();
3843 } // foreach sectorblocks
3845 if(sector_blocks_drawn != 0)
3847 m_last_drawn_sectors[sp] = true;
3851 if(pass == scene::ESNRP_SOLID){
3852 g_profiler->avg("CM: blocks drawn on solid pass", blocks_drawn);
3853 g_profiler->avg("CM: vertices drawn on solid pass", vertex_count);
3854 if(blocks_drawn != 0)
3855 g_profiler->avg("CM: solid meshbuffers per block",
3856 (float)meshbuffer_count / (float)blocks_drawn);
3858 g_profiler->avg("CM: blocks drawn on transparent pass", blocks_drawn);
3859 g_profiler->avg("CM: vertices drawn on transparent pass", vertex_count);
3862 m_control.blocks_drawn = blocks_drawn;
3863 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3865 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3866 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3869 void ClientMap::renderPostFx()
3871 // Sadly ISceneManager has no "post effects" render pass, in that case we
3872 // could just register for that and handle it in renderMap().
3874 m_camera_mutex.Lock();
3875 v3f camera_position = m_camera_position;
3876 m_camera_mutex.Unlock();
3878 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
3880 // - If the player is in a solid node, make everything black.
3881 // - If the player is in liquid, draw a semi-transparent overlay.
3882 ContentFeatures& features = content_features(n);
3883 video::SColor post_effect_color = features.post_effect_color;
3884 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
3886 post_effect_color = video::SColor(255, 0, 0, 0);
3888 if (post_effect_color.getAlpha() != 0)
3890 // Draw a full-screen rectangle
3891 video::IVideoDriver* driver = SceneManager->getVideoDriver();
3892 v2u32 ss = driver->getScreenSize();
3893 core::rect<s32> rect(0,0, ss.X, ss.Y);
3894 driver->draw2DRectangle(post_effect_color, rect);
3898 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3899 core::map<v3s16, MapBlock*> *affected_blocks)
3901 bool changed = false;
3903 Add it to all blocks touching it
3906 v3s16(0,0,0), // this
3907 v3s16(0,0,1), // back
3908 v3s16(0,1,0), // top
3909 v3s16(1,0,0), // right
3910 v3s16(0,0,-1), // front
3911 v3s16(0,-1,0), // bottom
3912 v3s16(-1,0,0), // left
3914 for(u16 i=0; i<7; i++)
3916 v3s16 p2 = p + dirs[i];
3917 // Block position of neighbor (or requested) node
3918 v3s16 blockpos = getNodeBlockPos(p2);
3919 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3920 if(blockref == NULL)
3922 // Relative position of requested node
3923 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3924 if(blockref->setTempMod(relpos, mod))
3929 if(changed && affected_blocks!=NULL)
3931 for(u16 i=0; i<7; i++)
3933 v3s16 p2 = p + dirs[i];
3934 // Block position of neighbor (or requested) node
3935 v3s16 blockpos = getNodeBlockPos(p2);
3936 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3937 if(blockref == NULL)
3939 affected_blocks->insert(blockpos, blockref);
3945 bool ClientMap::clearTempMod(v3s16 p,
3946 core::map<v3s16, MapBlock*> *affected_blocks)
3948 bool changed = false;
3950 v3s16(0,0,0), // this
3951 v3s16(0,0,1), // back
3952 v3s16(0,1,0), // top
3953 v3s16(1,0,0), // right
3954 v3s16(0,0,-1), // front
3955 v3s16(0,-1,0), // bottom
3956 v3s16(-1,0,0), // left
3958 for(u16 i=0; i<7; i++)
3960 v3s16 p2 = p + dirs[i];
3961 // Block position of neighbor (or requested) node
3962 v3s16 blockpos = getNodeBlockPos(p2);
3963 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3964 if(blockref == NULL)
3966 // Relative position of requested node
3967 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3968 if(blockref->clearTempMod(relpos))
3973 if(changed && affected_blocks!=NULL)
3975 for(u16 i=0; i<7; i++)
3977 v3s16 p2 = p + dirs[i];
3978 // Block position of neighbor (or requested) node
3979 v3s16 blockpos = getNodeBlockPos(p2);
3980 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3981 if(blockref == NULL)
3983 affected_blocks->insert(blockpos, blockref);
3989 void ClientMap::expireMeshes(bool only_daynight_diffed)
3991 TimeTaker timer("expireMeshes()");
3993 core::map<v2s16, MapSector*>::Iterator si;
3994 si = m_sectors.getIterator();
3995 for(; si.atEnd() == false; si++)
3997 MapSector *sector = si.getNode()->getValue();
3999 core::list< MapBlock * > sectorblocks;
4000 sector->getBlocks(sectorblocks);
4002 core::list< MapBlock * >::Iterator i;
4003 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4005 MapBlock *block = *i;
4007 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4013 JMutexAutoLock lock(block->mesh_mutex);
4014 if(block->mesh != NULL)
4016 /*block->mesh->drop();
4017 block->mesh = NULL;*/
4018 block->setMeshExpired(true);
4025 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4027 assert(mapType() == MAPTYPE_CLIENT);
4030 v3s16 p = blockpos + v3s16(0,0,0);
4031 MapBlock *b = getBlockNoCreate(p);
4032 b->updateMesh(daynight_ratio);
4033 //b->setMeshExpired(true);
4035 catch(InvalidPositionException &e){}
4038 v3s16 p = blockpos + v3s16(-1,0,0);
4039 MapBlock *b = getBlockNoCreate(p);
4040 b->updateMesh(daynight_ratio);
4041 //b->setMeshExpired(true);
4043 catch(InvalidPositionException &e){}
4045 v3s16 p = blockpos + v3s16(0,-1,0);
4046 MapBlock *b = getBlockNoCreate(p);
4047 b->updateMesh(daynight_ratio);
4048 //b->setMeshExpired(true);
4050 catch(InvalidPositionException &e){}
4052 v3s16 p = blockpos + v3s16(0,0,-1);
4053 MapBlock *b = getBlockNoCreate(p);
4054 b->updateMesh(daynight_ratio);
4055 //b->setMeshExpired(true);
4057 catch(InvalidPositionException &e){}
4062 Update mesh of block in which the node is, and if the node is at the
4063 leading edge, update the appropriate leading blocks too.
4065 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4073 v3s16 blockposes[4];
4074 for(u32 i=0; i<4; i++)
4076 v3s16 np = nodepos + dirs[i];
4077 blockposes[i] = getNodeBlockPos(np);
4078 // Don't update mesh of block if it has been done already
4079 bool already_updated = false;
4080 for(u32 j=0; j<i; j++)
4082 if(blockposes[j] == blockposes[i])
4084 already_updated = true;
4091 MapBlock *b = getBlockNoCreate(blockposes[i]);
4092 b->updateMesh(daynight_ratio);
4097 void ClientMap::PrintInfo(std::ostream &out)
4108 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4113 MapVoxelManipulator::~MapVoxelManipulator()
4115 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4119 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4121 TimeTaker timer1("emerge", &emerge_time);
4123 // Units of these are MapBlocks
4124 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4125 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4127 VoxelArea block_area_nodes
4128 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4130 addArea(block_area_nodes);
4132 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4133 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4134 for(s32 x=p_min.X; x<=p_max.X; x++)
4137 core::map<v3s16, bool>::Node *n;
4138 n = m_loaded_blocks.find(p);
4142 bool block_data_inexistent = false;
4145 TimeTaker timer1("emerge load", &emerge_load_time);
4147 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4148 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4150 a.print(infostream);
4151 infostream<<std::endl;*/
4153 MapBlock *block = m_map->getBlockNoCreate(p);
4154 if(block->isDummy())
4155 block_data_inexistent = true;
4157 block->copyTo(*this);
4159 catch(InvalidPositionException &e)
4161 block_data_inexistent = true;
4164 if(block_data_inexistent)
4166 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4167 // Fill with VOXELFLAG_INEXISTENT
4168 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4169 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4171 s32 i = m_area.index(a.MinEdge.X,y,z);
4172 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4176 m_loaded_blocks.insert(p, !block_data_inexistent);
4179 //infostream<<"emerge done"<<std::endl;
4183 SUGG: Add an option to only update eg. water and air nodes.
4184 This will make it interfere less with important stuff if
4187 void MapVoxelManipulator::blitBack
4188 (core::map<v3s16, MapBlock*> & modified_blocks)
4190 if(m_area.getExtent() == v3s16(0,0,0))
4193 //TimeTaker timer1("blitBack");
4195 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4196 <<m_loaded_blocks.size()<<std::endl;*/
4199 Initialize block cache
4201 v3s16 blockpos_last;
4202 MapBlock *block = NULL;
4203 bool block_checked_in_modified = false;
4205 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4206 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4207 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4211 u8 f = m_flags[m_area.index(p)];
4212 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4215 MapNode &n = m_data[m_area.index(p)];
4217 v3s16 blockpos = getNodeBlockPos(p);
4222 if(block == NULL || blockpos != blockpos_last){
4223 block = m_map->getBlockNoCreate(blockpos);
4224 blockpos_last = blockpos;
4225 block_checked_in_modified = false;
4228 // Calculate relative position in block
4229 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4231 // Don't continue if nothing has changed here
4232 if(block->getNode(relpos) == n)
4235 //m_map->setNode(m_area.MinEdge + p, n);
4236 block->setNode(relpos, n);
4239 Make sure block is in modified_blocks
4241 if(block_checked_in_modified == false)
4243 modified_blocks[blockpos] = block;
4244 block_checked_in_modified = true;
4247 catch(InvalidPositionException &e)
4253 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4254 MapVoxelManipulator(map),
4255 m_create_area(false)
4259 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4263 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4265 // Just create the area so that it can be pointed to
4266 VoxelManipulator::emerge(a, caller_id);
4269 void ManualMapVoxelManipulator::initialEmerge(
4270 v3s16 blockpos_min, v3s16 blockpos_max)
4272 TimeTaker timer1("initialEmerge", &emerge_time);
4274 // Units of these are MapBlocks
4275 v3s16 p_min = blockpos_min;
4276 v3s16 p_max = blockpos_max;
4278 VoxelArea block_area_nodes
4279 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4281 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4284 infostream<<"initialEmerge: area: ";
4285 block_area_nodes.print(infostream);
4286 infostream<<" ("<<size_MB<<"MB)";
4287 infostream<<std::endl;
4290 addArea(block_area_nodes);
4292 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4293 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4294 for(s32 x=p_min.X; x<=p_max.X; x++)
4297 core::map<v3s16, bool>::Node *n;
4298 n = m_loaded_blocks.find(p);
4302 bool block_data_inexistent = false;
4305 TimeTaker timer1("emerge load", &emerge_load_time);
4307 MapBlock *block = m_map->getBlockNoCreate(p);
4308 if(block->isDummy())
4309 block_data_inexistent = true;
4311 block->copyTo(*this);
4313 catch(InvalidPositionException &e)
4315 block_data_inexistent = true;
4318 if(block_data_inexistent)
4321 Mark area inexistent
4323 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4324 // Fill with VOXELFLAG_INEXISTENT
4325 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4326 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4328 s32 i = m_area.index(a.MinEdge.X,y,z);
4329 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4333 m_loaded_blocks.insert(p, !block_data_inexistent);
4337 void ManualMapVoxelManipulator::blitBackAll(
4338 core::map<v3s16, MapBlock*> * modified_blocks)
4340 if(m_area.getExtent() == v3s16(0,0,0))
4344 Copy data of all blocks
4346 for(core::map<v3s16, bool>::Iterator
4347 i = m_loaded_blocks.getIterator();
4348 i.atEnd() == false; i++)
4350 v3s16 p = i.getNode()->getKey();
4351 bool existed = i.getNode()->getValue();
4352 if(existed == false)
4354 // The Great Bug was found using this
4355 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4356 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4360 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4363 infostream<<"WARNING: "<<__FUNCTION_NAME
4364 <<": got NULL block "
4365 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4370 block->copyFrom(*this);
4373 modified_blocks->insert(p, block);