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>
38 SQLite format specification:
39 - Initially only replaces sectors/ and sectors2/
41 If map.sqlite does not exist in the save dir
42 or the block was not found in the database
43 the map will try to load from sectors folder.
44 In either case, map.sqlite will be created
45 and all future saves will save there.
47 Structure of map.sqlite:
58 Map::Map(std::ostream &dout):
62 /*m_sector_mutex.Init();
63 assert(m_sector_mutex.IsInitialized());*/
71 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
72 for(; i.atEnd() == false; i++)
74 MapSector *sector = i.getNode()->getValue();
79 void Map::addEventReceiver(MapEventReceiver *event_receiver)
81 m_event_receivers.insert(event_receiver, false);
84 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
86 if(m_event_receivers.find(event_receiver) == NULL)
88 m_event_receivers.remove(event_receiver);
91 void Map::dispatchEvent(MapEditEvent *event)
93 for(core::map<MapEventReceiver*, bool>::Iterator
94 i = m_event_receivers.getIterator();
95 i.atEnd()==false; i++)
97 MapEventReceiver* event_receiver = i.getNode()->getKey();
98 event_receiver->onMapEditEvent(event);
102 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
104 if(m_sector_cache != NULL && p == m_sector_cache_p){
105 MapSector * sector = m_sector_cache;
109 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
114 MapSector *sector = n->getValue();
116 // Cache the last result
117 m_sector_cache_p = p;
118 m_sector_cache = sector;
123 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
125 return getSectorNoGenerateNoExNoLock(p);
128 MapSector * Map::getSectorNoGenerate(v2s16 p)
130 MapSector *sector = getSectorNoGenerateNoEx(p);
132 throw InvalidPositionException();
137 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
139 v2s16 p2d(p3d.X, p3d.Z);
140 MapSector * sector = getSectorNoGenerateNoEx(p2d);
143 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
147 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
149 MapBlock *block = getBlockNoCreateNoEx(p3d);
151 throw InvalidPositionException();
155 bool Map::isNodeUnderground(v3s16 p)
157 v3s16 blockpos = getNodeBlockPos(p);
159 MapBlock * block = getBlockNoCreate(blockpos);
160 return block->getIsUnderground();
162 catch(InvalidPositionException &e)
168 bool Map::isValidPosition(v3s16 p)
170 v3s16 blockpos = getNodeBlockPos(p);
171 MapBlock *block = getBlockNoCreate(blockpos);
172 return (block != NULL);
175 // Returns a CONTENT_IGNORE node if not found
176 MapNode Map::getNodeNoEx(v3s16 p)
178 v3s16 blockpos = getNodeBlockPos(p);
179 MapBlock *block = getBlockNoCreateNoEx(blockpos);
181 return MapNode(CONTENT_IGNORE);
182 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
183 return block->getNodeNoCheck(relpos);
186 // throws InvalidPositionException if not found
187 MapNode Map::getNode(v3s16 p)
189 v3s16 blockpos = getNodeBlockPos(p);
190 MapBlock *block = getBlockNoCreateNoEx(blockpos);
192 throw InvalidPositionException();
193 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
194 return block->getNodeNoCheck(relpos);
197 // throws InvalidPositionException if not found
198 void Map::setNode(v3s16 p, MapNode & n)
200 v3s16 blockpos = getNodeBlockPos(p);
201 MapBlock *block = getBlockNoCreate(blockpos);
202 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
203 block->setNodeNoCheck(relpos, n);
208 Goes recursively through the neighbours of the node.
210 Alters only transparent nodes.
212 If the lighting of the neighbour is lower than the lighting of
213 the node was (before changing it to 0 at the step before), the
214 lighting of the neighbour is set to 0 and then the same stuff
215 repeats for the neighbour.
217 The ending nodes of the routine are stored in light_sources.
218 This is useful when a light is removed. In such case, this
219 routine can be called for the light node and then again for
220 light_sources to re-light the area without the removed light.
222 values of from_nodes are lighting values.
224 void Map::unspreadLight(enum LightBank bank,
225 core::map<v3s16, u8> & from_nodes,
226 core::map<v3s16, bool> & light_sources,
227 core::map<v3s16, MapBlock*> & modified_blocks)
230 v3s16(0,0,1), // back
232 v3s16(1,0,0), // right
233 v3s16(0,0,-1), // front
234 v3s16(0,-1,0), // bottom
235 v3s16(-1,0,0), // left
238 if(from_nodes.size() == 0)
241 u32 blockchangecount = 0;
243 core::map<v3s16, u8> unlighted_nodes;
244 core::map<v3s16, u8>::Iterator j;
245 j = from_nodes.getIterator();
248 Initialize block cache
251 MapBlock *block = NULL;
252 // Cache this a bit, too
253 bool block_checked_in_modified = false;
255 for(; j.atEnd() == false; j++)
257 v3s16 pos = j.getNode()->getKey();
258 v3s16 blockpos = getNodeBlockPos(pos);
260 // Only fetch a new block if the block position has changed
262 if(block == NULL || blockpos != blockpos_last){
263 block = getBlockNoCreate(blockpos);
264 blockpos_last = blockpos;
266 block_checked_in_modified = false;
270 catch(InvalidPositionException &e)
278 // Calculate relative position in block
279 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
281 // Get node straight from the block
282 MapNode n = block->getNode(relpos);
284 u8 oldlight = j.getNode()->getValue();
286 // Loop through 6 neighbors
287 for(u16 i=0; i<6; i++)
289 // Get the position of the neighbor node
290 v3s16 n2pos = pos + dirs[i];
292 // Get the block where the node is located
293 v3s16 blockpos = getNodeBlockPos(n2pos);
297 // Only fetch a new block if the block position has changed
299 if(block == NULL || blockpos != blockpos_last){
300 block = getBlockNoCreate(blockpos);
301 blockpos_last = blockpos;
303 block_checked_in_modified = false;
307 catch(InvalidPositionException &e)
312 // Calculate relative position in block
313 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
314 // Get node straight from the block
315 MapNode n2 = block->getNode(relpos);
317 bool changed = false;
319 //TODO: Optimize output by optimizing light_sources?
322 If the neighbor is dimmer than what was specified
323 as oldlight (the light of the previous node)
325 if(n2.getLight(bank) < oldlight)
328 And the neighbor is transparent and it has some light
330 if(n2.light_propagates() && n2.getLight(bank) != 0)
333 Set light to 0 and add to queue
336 u8 current_light = n2.getLight(bank);
337 n2.setLight(bank, 0);
338 block->setNode(relpos, n2);
340 unlighted_nodes.insert(n2pos, current_light);
344 Remove from light_sources if it is there
345 NOTE: This doesn't happen nearly at all
347 /*if(light_sources.find(n2pos))
349 std::cout<<"Removed from light_sources"<<std::endl;
350 light_sources.remove(n2pos);
355 if(light_sources.find(n2pos) != NULL)
356 light_sources.remove(n2pos);*/
359 light_sources.insert(n2pos, true);
362 // Add to modified_blocks
363 if(changed == true && block_checked_in_modified == false)
365 // If the block is not found in modified_blocks, add.
366 if(modified_blocks.find(blockpos) == NULL)
368 modified_blocks.insert(blockpos, block);
370 block_checked_in_modified = true;
373 catch(InvalidPositionException &e)
380 /*dstream<<"unspreadLight(): Changed block "
381 <<blockchangecount<<" times"
382 <<" for "<<from_nodes.size()<<" nodes"
385 if(unlighted_nodes.size() > 0)
386 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
390 A single-node wrapper of the above
392 void Map::unLightNeighbors(enum LightBank bank,
393 v3s16 pos, u8 lightwas,
394 core::map<v3s16, bool> & light_sources,
395 core::map<v3s16, MapBlock*> & modified_blocks)
397 core::map<v3s16, u8> from_nodes;
398 from_nodes.insert(pos, lightwas);
400 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
404 Lights neighbors of from_nodes, collects all them and then
407 void Map::spreadLight(enum LightBank bank,
408 core::map<v3s16, bool> & from_nodes,
409 core::map<v3s16, MapBlock*> & modified_blocks)
411 const v3s16 dirs[6] = {
412 v3s16(0,0,1), // back
414 v3s16(1,0,0), // right
415 v3s16(0,0,-1), // front
416 v3s16(0,-1,0), // bottom
417 v3s16(-1,0,0), // left
420 if(from_nodes.size() == 0)
423 u32 blockchangecount = 0;
425 core::map<v3s16, bool> lighted_nodes;
426 core::map<v3s16, bool>::Iterator j;
427 j = from_nodes.getIterator();
430 Initialize block cache
433 MapBlock *block = NULL;
434 // Cache this a bit, too
435 bool block_checked_in_modified = false;
437 for(; j.atEnd() == false; j++)
438 //for(; j != from_nodes.end(); j++)
440 v3s16 pos = j.getNode()->getKey();
442 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
443 v3s16 blockpos = getNodeBlockPos(pos);
445 // Only fetch a new block if the block position has changed
447 if(block == NULL || blockpos != blockpos_last){
448 block = getBlockNoCreate(blockpos);
449 blockpos_last = blockpos;
451 block_checked_in_modified = false;
455 catch(InvalidPositionException &e)
463 // Calculate relative position in block
464 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
466 // Get node straight from the block
467 MapNode n = block->getNode(relpos);
469 u8 oldlight = n.getLight(bank);
470 u8 newlight = diminish_light(oldlight);
472 // Loop through 6 neighbors
473 for(u16 i=0; i<6; i++){
474 // Get the position of the neighbor node
475 v3s16 n2pos = pos + dirs[i];
477 // Get the block where the node is located
478 v3s16 blockpos = getNodeBlockPos(n2pos);
482 // Only fetch a new block if the block position has changed
484 if(block == NULL || blockpos != blockpos_last){
485 block = getBlockNoCreate(blockpos);
486 blockpos_last = blockpos;
488 block_checked_in_modified = false;
492 catch(InvalidPositionException &e)
497 // Calculate relative position in block
498 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
499 // Get node straight from the block
500 MapNode n2 = block->getNode(relpos);
502 bool changed = false;
504 If the neighbor is brighter than the current node,
505 add to list (it will light up this node on its turn)
507 if(n2.getLight(bank) > undiminish_light(oldlight))
509 lighted_nodes.insert(n2pos, true);
510 //lighted_nodes.push_back(n2pos);
514 If the neighbor is dimmer than how much light this node
515 would spread on it, add to list
517 if(n2.getLight(bank) < newlight)
519 if(n2.light_propagates())
521 n2.setLight(bank, newlight);
522 block->setNode(relpos, n2);
523 lighted_nodes.insert(n2pos, true);
524 //lighted_nodes.push_back(n2pos);
529 // Add to modified_blocks
530 if(changed == true && block_checked_in_modified == false)
532 // If the block is not found in modified_blocks, add.
533 if(modified_blocks.find(blockpos) == NULL)
535 modified_blocks.insert(blockpos, block);
537 block_checked_in_modified = true;
540 catch(InvalidPositionException &e)
547 /*dstream<<"spreadLight(): Changed block "
548 <<blockchangecount<<" times"
549 <<" for "<<from_nodes.size()<<" nodes"
552 if(lighted_nodes.size() > 0)
553 spreadLight(bank, lighted_nodes, modified_blocks);
557 A single-node source variation of the above.
559 void Map::lightNeighbors(enum LightBank bank,
561 core::map<v3s16, MapBlock*> & modified_blocks)
563 core::map<v3s16, bool> from_nodes;
564 from_nodes.insert(pos, true);
565 spreadLight(bank, from_nodes, modified_blocks);
568 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
571 v3s16(0,0,1), // back
573 v3s16(1,0,0), // right
574 v3s16(0,0,-1), // front
575 v3s16(0,-1,0), // bottom
576 v3s16(-1,0,0), // left
579 u8 brightest_light = 0;
580 v3s16 brightest_pos(0,0,0);
581 bool found_something = false;
583 // Loop through 6 neighbors
584 for(u16 i=0; i<6; i++){
585 // Get the position of the neighbor node
586 v3s16 n2pos = p + dirs[i];
591 catch(InvalidPositionException &e)
595 if(n2.getLight(bank) > brightest_light || found_something == false){
596 brightest_light = n2.getLight(bank);
597 brightest_pos = n2pos;
598 found_something = true;
602 if(found_something == false)
603 throw InvalidPositionException();
605 return brightest_pos;
609 Propagates sunlight down from a node.
610 Starting point gets sunlight.
612 Returns the lowest y value of where the sunlight went.
614 Mud is turned into grass in where the sunlight stops.
616 s16 Map::propagateSunlight(v3s16 start,
617 core::map<v3s16, MapBlock*> & modified_blocks)
622 v3s16 pos(start.X, y, start.Z);
624 v3s16 blockpos = getNodeBlockPos(pos);
627 block = getBlockNoCreate(blockpos);
629 catch(InvalidPositionException &e)
634 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
635 MapNode n = block->getNode(relpos);
637 if(n.sunlight_propagates())
639 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
640 block->setNode(relpos, n);
642 modified_blocks.insert(blockpos, block);
646 /*// Turn mud into grass
647 if(n.getContent() == CONTENT_MUD)
649 n.setContent(CONTENT_GRASS);
650 block->setNode(relpos, n);
651 modified_blocks.insert(blockpos, block);
654 // Sunlight goes no further
661 void Map::updateLighting(enum LightBank bank,
662 core::map<v3s16, MapBlock*> & a_blocks,
663 core::map<v3s16, MapBlock*> & modified_blocks)
665 /*m_dout<<DTIME<<"Map::updateLighting(): "
666 <<a_blocks.size()<<" blocks."<<std::endl;*/
668 //TimeTaker timer("updateLighting");
672 //u32 count_was = modified_blocks.size();
674 core::map<v3s16, MapBlock*> blocks_to_update;
676 core::map<v3s16, bool> light_sources;
678 core::map<v3s16, u8> unlight_from;
680 core::map<v3s16, MapBlock*>::Iterator i;
681 i = a_blocks.getIterator();
682 for(; i.atEnd() == false; i++)
684 MapBlock *block = i.getNode()->getValue();
688 // Don't bother with dummy blocks.
692 v3s16 pos = block->getPos();
693 modified_blocks.insert(pos, block);
695 blocks_to_update.insert(pos, block);
698 Clear all light from block
700 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
701 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
702 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
707 MapNode n = block->getNode(v3s16(x,y,z));
708 u8 oldlight = n.getLight(bank);
710 block->setNode(v3s16(x,y,z), n);
712 // Collect borders for unlighting
713 if(x==0 || x == MAP_BLOCKSIZE-1
714 || y==0 || y == MAP_BLOCKSIZE-1
715 || z==0 || z == MAP_BLOCKSIZE-1)
717 v3s16 p_map = p + v3s16(
720 MAP_BLOCKSIZE*pos.Z);
721 unlight_from.insert(p_map, oldlight);
724 catch(InvalidPositionException &e)
727 This would happen when dealing with a
731 dstream<<"updateLighting(): InvalidPositionException"
736 if(bank == LIGHTBANK_DAY)
738 bool bottom_valid = block->propagateSunlight(light_sources);
740 // If bottom is valid, we're done.
744 else if(bank == LIGHTBANK_NIGHT)
746 // For night lighting, sunlight is not propagated
751 // Invalid lighting bank
755 /*dstream<<"Bottom for sunlight-propagated block ("
756 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
759 // Bottom sunlight is not valid; get the block and loop to it
763 block = getBlockNoCreate(pos);
765 catch(InvalidPositionException &e)
774 Enable this to disable proper lighting for speeding up map
775 generation for testing or whatever
778 //if(g_settings->get(""))
780 core::map<v3s16, MapBlock*>::Iterator i;
781 i = blocks_to_update.getIterator();
782 for(; i.atEnd() == false; i++)
784 MapBlock *block = i.getNode()->getValue();
785 v3s16 p = block->getPos();
786 block->setLightingExpired(false);
794 TimeTaker timer("unspreadLight");
795 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
800 u32 diff = modified_blocks.size() - count_was;
801 count_was = modified_blocks.size();
802 dstream<<"unspreadLight modified "<<diff<<std::endl;
806 TimeTaker timer("spreadLight");
807 spreadLight(bank, light_sources, modified_blocks);
812 u32 diff = modified_blocks.size() - count_was;
813 count_was = modified_blocks.size();
814 dstream<<"spreadLight modified "<<diff<<std::endl;
819 //MapVoxelManipulator vmanip(this);
821 // Make a manual voxel manipulator and load all the blocks
822 // that touch the requested blocks
823 ManualMapVoxelManipulator vmanip(this);
824 core::map<v3s16, MapBlock*>::Iterator i;
825 i = blocks_to_update.getIterator();
826 for(; i.atEnd() == false; i++)
828 MapBlock *block = i.getNode()->getValue();
829 v3s16 p = block->getPos();
831 // Add all surrounding blocks
832 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
835 Add all surrounding blocks that have up-to-date lighting
836 NOTE: This doesn't quite do the job (not everything
837 appropriate is lighted)
839 /*for(s16 z=-1; z<=1; z++)
840 for(s16 y=-1; y<=1; y++)
841 for(s16 x=-1; x<=1; x++)
844 MapBlock *block = getBlockNoCreateNoEx(p);
849 if(block->getLightingExpired())
851 vmanip.initialEmerge(p, p);
854 // Lighting of block will be updated completely
855 block->setLightingExpired(false);
859 //TimeTaker timer("unSpreadLight");
860 vmanip.unspreadLight(bank, unlight_from, light_sources);
863 //TimeTaker timer("spreadLight");
864 vmanip.spreadLight(bank, light_sources);
867 //TimeTaker timer("blitBack");
868 vmanip.blitBack(modified_blocks);
870 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
874 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
877 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
878 core::map<v3s16, MapBlock*> & modified_blocks)
880 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
881 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
884 Update information about whether day and night light differ
886 for(core::map<v3s16, MapBlock*>::Iterator
887 i = modified_blocks.getIterator();
888 i.atEnd() == false; i++)
890 MapBlock *block = i.getNode()->getValue();
891 block->updateDayNightDiff();
897 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
898 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
901 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
902 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
905 From this node to nodes underneath:
906 If lighting is sunlight (1.0), unlight neighbours and
911 v3s16 toppos = p + v3s16(0,1,0);
912 v3s16 bottompos = p + v3s16(0,-1,0);
914 bool node_under_sunlight = true;
915 core::map<v3s16, bool> light_sources;
918 If there is a node at top and it doesn't have sunlight,
919 there has not been any sunlight going down.
921 Otherwise there probably is.
924 MapNode topnode = getNode(toppos);
926 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
927 node_under_sunlight = false;
929 catch(InvalidPositionException &e)
935 If the new node is solid and there is grass below, change it to mud
937 if(content_features(n).walkable == true)
940 MapNode bottomnode = getNode(bottompos);
942 if(bottomnode.getContent() == CONTENT_GRASS
943 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
945 bottomnode.setContent(CONTENT_MUD);
946 setNode(bottompos, bottomnode);
949 catch(InvalidPositionException &e)
957 If the new node is mud and it is under sunlight, change it
960 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
962 n.setContent(CONTENT_GRASS);
967 Remove all light that has come out of this node
970 enum LightBank banks[] =
975 for(s32 i=0; i<2; i++)
977 enum LightBank bank = banks[i];
979 u8 lightwas = getNode(p).getLight(bank);
981 // Add the block of the added node to modified_blocks
982 v3s16 blockpos = getNodeBlockPos(p);
983 MapBlock * block = getBlockNoCreate(blockpos);
984 assert(block != NULL);
985 modified_blocks.insert(blockpos, block);
987 assert(isValidPosition(p));
989 // Unlight neighbours of node.
990 // This means setting light of all consequent dimmer nodes
992 // This also collects the nodes at the border which will spread
993 // light again into this.
994 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1000 If node lets sunlight through and is under sunlight, it has
1003 if(node_under_sunlight && content_features(n).sunlight_propagates)
1005 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1009 Set the node on the map
1018 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1021 NodeMetadata *meta = meta_proto->clone();
1022 meta->setOwner(player_name);
1023 setNodeMetadata(p, meta);
1027 If node is under sunlight and doesn't let sunlight through,
1028 take all sunlighted nodes under it and clear light from them
1029 and from where the light has been spread.
1030 TODO: This could be optimized by mass-unlighting instead
1033 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1037 //m_dout<<DTIME<<"y="<<y<<std::endl;
1038 v3s16 n2pos(p.X, y, p.Z);
1042 n2 = getNode(n2pos);
1044 catch(InvalidPositionException &e)
1049 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1051 unLightNeighbors(LIGHTBANK_DAY,
1052 n2pos, n2.getLight(LIGHTBANK_DAY),
1053 light_sources, modified_blocks);
1054 n2.setLight(LIGHTBANK_DAY, 0);
1062 for(s32 i=0; i<2; i++)
1064 enum LightBank bank = banks[i];
1067 Spread light from all nodes that might be capable of doing so
1069 spreadLight(bank, light_sources, modified_blocks);
1073 Update information about whether day and night light differ
1075 for(core::map<v3s16, MapBlock*>::Iterator
1076 i = modified_blocks.getIterator();
1077 i.atEnd() == false; i++)
1079 MapBlock *block = i.getNode()->getValue();
1080 block->updateDayNightDiff();
1084 Add neighboring liquid nodes and the node itself if it is
1085 liquid (=water node was added) to transform queue.
1088 v3s16(0,0,0), // self
1089 v3s16(0,0,1), // back
1090 v3s16(0,1,0), // top
1091 v3s16(1,0,0), // right
1092 v3s16(0,0,-1), // front
1093 v3s16(0,-1,0), // bottom
1094 v3s16(-1,0,0), // left
1096 for(u16 i=0; i<7; i++)
1101 v3s16 p2 = p + dirs[i];
1103 MapNode n2 = getNode(p2);
1104 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1106 m_transforming_liquid.push_back(p2);
1109 }catch(InvalidPositionException &e)
1117 void Map::removeNodeAndUpdate(v3s16 p,
1118 core::map<v3s16, MapBlock*> &modified_blocks)
1120 /*PrintInfo(m_dout);
1121 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1122 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1124 bool node_under_sunlight = true;
1126 v3s16 toppos = p + v3s16(0,1,0);
1128 // Node will be replaced with this
1129 content_t replace_material = CONTENT_AIR;
1132 If there is a node at top and it doesn't have sunlight,
1133 there will be no sunlight going down.
1136 MapNode topnode = getNode(toppos);
1138 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1139 node_under_sunlight = false;
1141 catch(InvalidPositionException &e)
1145 core::map<v3s16, bool> light_sources;
1147 enum LightBank banks[] =
1152 for(s32 i=0; i<2; i++)
1154 enum LightBank bank = banks[i];
1157 Unlight neighbors (in case the node is a light source)
1159 unLightNeighbors(bank, p,
1160 getNode(p).getLight(bank),
1161 light_sources, modified_blocks);
1165 Remove node metadata
1168 removeNodeMetadata(p);
1172 This also clears the lighting.
1176 n.setContent(replace_material);
1179 for(s32 i=0; i<2; i++)
1181 enum LightBank bank = banks[i];
1184 Recalculate lighting
1186 spreadLight(bank, light_sources, modified_blocks);
1189 // Add the block of the removed node to modified_blocks
1190 v3s16 blockpos = getNodeBlockPos(p);
1191 MapBlock * block = getBlockNoCreate(blockpos);
1192 assert(block != NULL);
1193 modified_blocks.insert(blockpos, block);
1196 If the removed node was under sunlight, propagate the
1197 sunlight down from it and then light all neighbors
1198 of the propagated blocks.
1200 if(node_under_sunlight)
1202 s16 ybottom = propagateSunlight(p, modified_blocks);
1203 /*m_dout<<DTIME<<"Node was under sunlight. "
1204 "Propagating sunlight";
1205 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1207 for(; y >= ybottom; y--)
1209 v3s16 p2(p.X, y, p.Z);
1210 /*m_dout<<DTIME<<"lighting neighbors of node ("
1211 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1213 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1218 // Set the lighting of this node to 0
1219 // TODO: Is this needed? Lighting is cleared up there already.
1221 MapNode n = getNode(p);
1222 n.setLight(LIGHTBANK_DAY, 0);
1225 catch(InvalidPositionException &e)
1231 for(s32 i=0; i<2; i++)
1233 enum LightBank bank = banks[i];
1235 // Get the brightest neighbour node and propagate light from it
1236 v3s16 n2p = getBrightestNeighbour(bank, p);
1238 MapNode n2 = getNode(n2p);
1239 lightNeighbors(bank, n2p, modified_blocks);
1241 catch(InvalidPositionException &e)
1247 Update information about whether day and night light differ
1249 for(core::map<v3s16, MapBlock*>::Iterator
1250 i = modified_blocks.getIterator();
1251 i.atEnd() == false; i++)
1253 MapBlock *block = i.getNode()->getValue();
1254 block->updateDayNightDiff();
1258 Add neighboring liquid nodes and this node to transform queue.
1259 (it's vital for the node itself to get updated last.)
1262 v3s16(0,0,1), // back
1263 v3s16(0,1,0), // top
1264 v3s16(1,0,0), // right
1265 v3s16(0,0,-1), // front
1266 v3s16(0,-1,0), // bottom
1267 v3s16(-1,0,0), // left
1268 v3s16(0,0,0), // self
1270 for(u16 i=0; i<7; i++)
1275 v3s16 p2 = p + dirs[i];
1277 MapNode n2 = getNode(p2);
1278 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1280 m_transforming_liquid.push_back(p2);
1283 }catch(InvalidPositionException &e)
1289 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1292 event.type = MEET_ADDNODE;
1296 bool succeeded = true;
1298 core::map<v3s16, MapBlock*> modified_blocks;
1299 std::string st = std::string("");
1300 addNodeAndUpdate(p, n, modified_blocks, st);
1302 // Copy modified_blocks to event
1303 for(core::map<v3s16, MapBlock*>::Iterator
1304 i = modified_blocks.getIterator();
1305 i.atEnd()==false; i++)
1307 event.modified_blocks.insert(i.getNode()->getKey(), false);
1310 catch(InvalidPositionException &e){
1314 dispatchEvent(&event);
1319 bool Map::removeNodeWithEvent(v3s16 p)
1322 event.type = MEET_REMOVENODE;
1325 bool succeeded = true;
1327 core::map<v3s16, MapBlock*> modified_blocks;
1328 removeNodeAndUpdate(p, modified_blocks);
1330 // Copy modified_blocks to event
1331 for(core::map<v3s16, MapBlock*>::Iterator
1332 i = modified_blocks.getIterator();
1333 i.atEnd()==false; i++)
1335 event.modified_blocks.insert(i.getNode()->getKey(), false);
1338 catch(InvalidPositionException &e){
1342 dispatchEvent(&event);
1347 bool Map::dayNightDiffed(v3s16 blockpos)
1350 v3s16 p = blockpos + v3s16(0,0,0);
1351 MapBlock *b = getBlockNoCreate(p);
1352 if(b->dayNightDiffed())
1355 catch(InvalidPositionException &e){}
1358 v3s16 p = blockpos + v3s16(-1,0,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->dayNightDiffed())
1363 catch(InvalidPositionException &e){}
1365 v3s16 p = blockpos + v3s16(0,-1,0);
1366 MapBlock *b = getBlockNoCreate(p);
1367 if(b->dayNightDiffed())
1370 catch(InvalidPositionException &e){}
1372 v3s16 p = blockpos + v3s16(0,0,-1);
1373 MapBlock *b = getBlockNoCreate(p);
1374 if(b->dayNightDiffed())
1377 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(1,0,0);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->dayNightDiffed())
1385 catch(InvalidPositionException &e){}
1387 v3s16 p = blockpos + v3s16(0,1,0);
1388 MapBlock *b = getBlockNoCreate(p);
1389 if(b->dayNightDiffed())
1392 catch(InvalidPositionException &e){}
1394 v3s16 p = blockpos + v3s16(0,0,1);
1395 MapBlock *b = getBlockNoCreate(p);
1396 if(b->dayNightDiffed())
1399 catch(InvalidPositionException &e){}
1405 Updates usage timers
1407 void Map::timerUpdate(float dtime, float unload_timeout,
1408 core::list<v3s16> *unloaded_blocks)
1410 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1412 core::list<v2s16> sector_deletion_queue;
1413 u32 deleted_blocks_count = 0;
1414 u32 saved_blocks_count = 0;
1416 core::map<v2s16, MapSector*>::Iterator si;
1419 si = m_sectors.getIterator();
1420 for(; si.atEnd() == false; si++)
1422 MapSector *sector = si.getNode()->getValue();
1424 bool all_blocks_deleted = true;
1426 core::list<MapBlock*> blocks;
1427 sector->getBlocks(blocks);
1429 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1430 i != blocks.end(); i++)
1432 MapBlock *block = (*i);
1434 block->incrementUsageTimer(dtime);
1436 if(block->getUsageTimer() > unload_timeout)
1438 v3s16 p = block->getPos();
1441 if(block->getModified() != MOD_STATE_CLEAN
1442 && save_before_unloading)
1445 saved_blocks_count++;
1448 // Delete from memory
1449 sector->deleteBlock(block);
1452 unloaded_blocks->push_back(p);
1454 deleted_blocks_count++;
1458 all_blocks_deleted = false;
1462 if(all_blocks_deleted)
1464 sector_deletion_queue.push_back(si.getNode()->getKey());
1469 // Finally delete the empty sectors
1470 deleteSectors(sector_deletion_queue);
1472 if(deleted_blocks_count != 0)
1474 PrintInfo(dstream); // ServerMap/ClientMap:
1475 dstream<<"Unloaded "<<deleted_blocks_count
1476 <<" blocks from memory";
1477 if(save_before_unloading)
1478 dstream<<", of which "<<saved_blocks_count<<" were written";
1479 dstream<<"."<<std::endl;
1483 void Map::deleteSectors(core::list<v2s16> &list)
1485 core::list<v2s16>::Iterator j;
1486 for(j=list.begin(); j!=list.end(); j++)
1488 MapSector *sector = m_sectors[*j];
1489 // If sector is in sector cache, remove it from there
1490 if(m_sector_cache == sector)
1491 m_sector_cache = NULL;
1492 // Remove from map and delete
1493 m_sectors.remove(*j);
1499 void Map::unloadUnusedData(float timeout,
1500 core::list<v3s16> *deleted_blocks)
1502 core::list<v2s16> sector_deletion_queue;
1503 u32 deleted_blocks_count = 0;
1504 u32 saved_blocks_count = 0;
1506 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1507 for(; si.atEnd() == false; si++)
1509 MapSector *sector = si.getNode()->getValue();
1511 bool all_blocks_deleted = true;
1513 core::list<MapBlock*> blocks;
1514 sector->getBlocks(blocks);
1515 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1516 i != blocks.end(); i++)
1518 MapBlock *block = (*i);
1520 if(block->getUsageTimer() > timeout)
1523 if(block->getModified() != MOD_STATE_CLEAN)
1526 saved_blocks_count++;
1528 // Delete from memory
1529 sector->deleteBlock(block);
1530 deleted_blocks_count++;
1534 all_blocks_deleted = false;
1538 if(all_blocks_deleted)
1540 sector_deletion_queue.push_back(si.getNode()->getKey());
1544 deleteSectors(sector_deletion_queue);
1546 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1547 <<", of which "<<saved_blocks_count<<" were wr."
1550 //return sector_deletion_queue.getSize();
1551 //return deleted_blocks_count;
1555 void Map::PrintInfo(std::ostream &out)
1560 #define WATER_DROP_BOOST 4
1564 NEIGHBOR_SAME_LEVEL,
1567 struct NodeNeighbor {
1573 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1575 DSTACK(__FUNCTION_NAME);
1576 //TimeTaker timer("transformLiquids()");
1579 u32 initial_size = m_transforming_liquid.size();
1581 /*if(initial_size != 0)
1582 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1584 // list of nodes that due to viscosity have not reached their max level height
1585 UniqueQueue<v3s16> must_reflow;
1587 // List of MapBlocks that will require a lighting update (due to lava)
1588 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1590 while(m_transforming_liquid.size() != 0)
1592 // This should be done here so that it is done when continue is used
1593 if(loopcount >= initial_size * 3)
1598 Get a queued transforming liquid node
1600 v3s16 p0 = m_transforming_liquid.pop_front();
1602 MapNode n0 = getNodeNoEx(p0);
1605 Collect information about current node
1607 s8 liquid_level = -1;
1608 u8 liquid_kind = CONTENT_IGNORE;
1609 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1610 switch (liquid_type) {
1612 liquid_level = LIQUID_LEVEL_SOURCE;
1613 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1615 case LIQUID_FLOWING:
1616 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1617 liquid_kind = n0.getContent();
1620 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1621 // continue with the next node.
1622 if (n0.getContent() != CONTENT_AIR)
1624 liquid_kind = CONTENT_AIR;
1629 Collect information about the environment
1631 const v3s16 *dirs = g_6dirs;
1632 NodeNeighbor sources[6]; // surrounding sources
1633 int num_sources = 0;
1634 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1636 NodeNeighbor airs[6]; // surrounding air
1638 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1639 int num_neutrals = 0;
1640 bool flowing_down = false;
1641 for (u16 i = 0; i < 6; i++) {
1642 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1645 nt = NEIGHBOR_UPPER;
1648 nt = NEIGHBOR_LOWER;
1651 v3s16 npos = p0 + dirs[i];
1652 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1653 switch (content_features(nb.n.getContent()).liquid_type) {
1655 if (nb.n.getContent() == CONTENT_AIR) {
1656 airs[num_airs++] = nb;
1657 // if the current node is a water source the neighbor
1658 // should be enqueded for transformation regardless of whether the
1659 // current node changes or not.
1660 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1661 m_transforming_liquid.push_back(npos);
1662 // if the current node happens to be a flowing node, it will start to flow down here.
1663 if (nb.t == NEIGHBOR_LOWER) {
1664 flowing_down = true;
1667 neutrals[num_neutrals++] = nb;
1671 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1672 if (liquid_kind == CONTENT_AIR)
1673 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1674 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1675 neutrals[num_neutrals++] = nb;
1677 sources[num_sources++] = nb;
1680 case LIQUID_FLOWING:
1681 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1682 if (liquid_kind == CONTENT_AIR)
1683 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1684 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1685 neutrals[num_neutrals++] = nb;
1687 flows[num_flows++] = nb;
1688 if (nb.t == NEIGHBOR_LOWER)
1689 flowing_down = true;
1696 decide on the type (and possibly level) of the current node
1698 content_t new_node_content;
1699 s8 new_node_level = -1;
1700 s8 max_node_level = -1;
1701 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1702 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1703 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1704 // it's perfectly safe to use liquid_kind here to determine the new node content.
1705 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1706 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1707 // liquid_kind is set properly, see above
1708 new_node_content = liquid_kind;
1709 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1711 // no surrounding sources, so get the maximum level that can flow into this node
1712 for (u16 i = 0; i < num_flows; i++) {
1713 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1714 switch (flows[i].t) {
1715 case NEIGHBOR_UPPER:
1716 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1717 max_node_level = LIQUID_LEVEL_MAX;
1718 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1719 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1720 } else if (nb_liquid_level > max_node_level)
1721 max_node_level = nb_liquid_level;
1723 case NEIGHBOR_LOWER:
1725 case NEIGHBOR_SAME_LEVEL:
1726 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1727 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1728 max_node_level = nb_liquid_level - 1;
1734 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1735 if (viscosity > 1 && max_node_level != liquid_level) {
1736 // amount to gain, limited by viscosity
1737 // must be at least 1 in absolute value
1738 s8 level_inc = max_node_level - liquid_level;
1739 if (level_inc < -viscosity || level_inc > viscosity)
1740 new_node_level = liquid_level + level_inc/viscosity;
1741 else if (level_inc < 0)
1742 new_node_level = liquid_level - 1;
1743 else if (level_inc > 0)
1744 new_node_level = liquid_level + 1;
1745 if (new_node_level != max_node_level)
1746 must_reflow.push_back(p0);
1748 new_node_level = max_node_level;
1750 if (new_node_level >= 0)
1751 new_node_content = liquid_kind;
1753 new_node_content = CONTENT_AIR;
1758 check if anything has changed. if not, just continue with the next node.
1760 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1761 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1762 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1768 update the current node
1770 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1771 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1772 // set level to last 3 bits, flowing down bit to 4th bit
1773 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1775 // set the liquid level and flow bit to 0
1776 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1778 n0.setContent(new_node_content);
1780 v3s16 blockpos = getNodeBlockPos(p0);
1781 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1783 modified_blocks.insert(blockpos, block);
1784 // If node emits light, MapBlock requires lighting update
1785 if(content_features(n0).light_source != 0)
1786 lighting_modified_blocks[block->getPos()] = block;
1790 enqueue neighbors for update if neccessary
1792 switch (content_features(n0.getContent()).liquid_type) {
1794 case LIQUID_FLOWING:
1795 // make sure source flows into all neighboring nodes
1796 for (u16 i = 0; i < num_flows; i++)
1797 if (flows[i].t != NEIGHBOR_UPPER)
1798 m_transforming_liquid.push_back(flows[i].p);
1799 for (u16 i = 0; i < num_airs; i++)
1800 if (airs[i].t != NEIGHBOR_UPPER)
1801 m_transforming_liquid.push_back(airs[i].p);
1804 // this flow has turned to air; neighboring flows might need to do the same
1805 for (u16 i = 0; i < num_flows; i++)
1806 m_transforming_liquid.push_back(flows[i].p);
1810 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1811 while (must_reflow.size() > 0)
1812 m_transforming_liquid.push_back(must_reflow.pop_front());
1813 updateLighting(lighting_modified_blocks, modified_blocks);
1816 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1818 v3s16 blockpos = getNodeBlockPos(p);
1819 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1820 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1823 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1827 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1831 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1833 v3s16 blockpos = getNodeBlockPos(p);
1834 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1835 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1838 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1842 block->m_node_metadata.set(p_rel, meta);
1845 void Map::removeNodeMetadata(v3s16 p)
1847 v3s16 blockpos = getNodeBlockPos(p);
1848 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1849 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1852 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1856 block->m_node_metadata.remove(p_rel);
1859 void Map::nodeMetadataStep(float dtime,
1860 core::map<v3s16, MapBlock*> &changed_blocks)
1864 Currently there is no way to ensure that all the necessary
1865 blocks are loaded when this is run. (They might get unloaded)
1866 NOTE: ^- Actually, that might not be so. In a quick test it
1867 reloaded a block with a furnace when I walked back to it from
1870 core::map<v2s16, MapSector*>::Iterator si;
1871 si = m_sectors.getIterator();
1872 for(; si.atEnd() == false; si++)
1874 MapSector *sector = si.getNode()->getValue();
1875 core::list< MapBlock * > sectorblocks;
1876 sector->getBlocks(sectorblocks);
1877 core::list< MapBlock * >::Iterator i;
1878 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1880 MapBlock *block = *i;
1881 bool changed = block->m_node_metadata.step(dtime);
1883 changed_blocks[block->getPos()] = block;
1892 ServerMap::ServerMap(std::string savedir):
1895 m_map_metadata_changed(true),
1897 m_database_read(NULL),
1898 m_database_write(NULL)
1900 dstream<<__FUNCTION_NAME<<std::endl;
1902 //m_chunksize = 8; // Takes a few seconds
1904 if (g_settings->get("fixed_map_seed").empty())
1906 m_seed = (((u64)(myrand()%0xffff)<<0)
1907 + ((u64)(myrand()%0xffff)<<16)
1908 + ((u64)(myrand()%0xffff)<<32)
1909 + ((u64)(myrand()%0xffff)<<48));
1913 m_seed = g_settings->getU64("fixed_map_seed");
1917 Experimental and debug stuff
1924 Try to load map; if not found, create a new one.
1927 m_savedir = savedir;
1928 m_map_saving_enabled = false;
1932 // If directory exists, check contents and load if possible
1933 if(fs::PathExists(m_savedir))
1935 // If directory is empty, it is safe to save into it.
1936 if(fs::GetDirListing(m_savedir).size() == 0)
1938 dstream<<DTIME<<"Server: Empty save directory is valid."
1940 m_map_saving_enabled = true;
1945 // Load map metadata (seed, chunksize)
1948 catch(FileNotGoodException &e){
1949 dstream<<DTIME<<"WARNING: Could not load map metadata"
1950 //<<" Disabling chunk-based generator."
1956 // Load chunk metadata
1959 catch(FileNotGoodException &e){
1960 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1961 <<" Disabling chunk-based generator."
1966 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1967 "metadata and sector (0,0) from "<<savedir<<
1968 ", assuming valid save directory."
1971 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1972 <<"and chunk metadata from "<<savedir
1973 <<", assuming valid save directory."
1976 m_map_saving_enabled = true;
1977 // Map loaded, not creating new one
1981 // If directory doesn't exist, it is safe to save to it
1983 m_map_saving_enabled = true;
1986 catch(std::exception &e)
1988 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1989 <<", exception: "<<e.what()<<std::endl;
1990 dstream<<"Please remove the map or fix it."<<std::endl;
1991 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1994 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1996 // Create zero sector
1997 emergeSector(v2s16(0,0));
1999 // Initially write whole map
2003 ServerMap::~ServerMap()
2005 dstream<<__FUNCTION_NAME<<std::endl;
2009 if(m_map_saving_enabled)
2011 // Save only changed parts
2013 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2017 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2020 catch(std::exception &e)
2022 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2023 <<", exception: "<<e.what()<<std::endl;
2027 Close database if it was opened
2030 sqlite3_finalize(m_database_read);
2031 if(m_database_write)
2032 sqlite3_finalize(m_database_write);
2034 sqlite3_close(m_database);
2040 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2041 for(; i.atEnd() == false; i++)
2043 MapChunk *chunk = i.getNode()->getValue();
2049 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2051 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2052 if(enable_mapgen_debug_info)
2053 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2054 <<blockpos.Z<<")"<<std::endl;
2056 // Do nothing if not inside limits (+-1 because of neighbors)
2057 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2058 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2064 data->no_op = false;
2065 data->seed = m_seed;
2066 data->blockpos = blockpos;
2069 Create the whole area of this and the neighboring blocks
2072 //TimeTaker timer("initBlockMake() create area");
2074 for(s16 x=-1; x<=1; x++)
2075 for(s16 z=-1; z<=1; z++)
2077 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2078 // Sector metadata is loaded from disk if not already loaded.
2079 ServerMapSector *sector = createSector(sectorpos);
2082 for(s16 y=-1; y<=1; y++)
2084 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2085 //MapBlock *block = createBlock(p);
2086 // 1) get from memory, 2) load from disk
2087 MapBlock *block = emergeBlock(p, false);
2088 // 3) create a blank one
2091 block = createBlock(p);
2094 Block gets sunlight if this is true.
2096 Refer to the map generator heuristics.
2098 bool ug = mapgen::block_is_underground(data->seed, p);
2099 block->setIsUnderground(ug);
2102 // Lighting will not be valid after make_chunk is called
2103 block->setLightingExpired(true);
2104 // Lighting will be calculated
2105 //block->setLightingExpired(false);
2111 Now we have a big empty area.
2113 Make a ManualMapVoxelManipulator that contains this and the
2117 // The area that contains this block and it's neighbors
2118 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2119 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2121 data->vmanip = new ManualMapVoxelManipulator(this);
2122 //data->vmanip->setMap(this);
2126 //TimeTaker timer("initBlockMake() initialEmerge");
2127 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2130 // Data is ready now.
2133 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2134 core::map<v3s16, MapBlock*> &changed_blocks)
2136 v3s16 blockpos = data->blockpos;
2137 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2138 <<blockpos.Z<<")"<<std::endl;*/
2142 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2146 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2148 /*dstream<<"Resulting vmanip:"<<std::endl;
2149 data->vmanip.print(dstream);*/
2152 Blit generated stuff to map
2153 NOTE: blitBackAll adds nearly everything to changed_blocks
2157 //TimeTaker timer("finishBlockMake() blitBackAll");
2158 data->vmanip->blitBackAll(&changed_blocks);
2161 if(enable_mapgen_debug_info)
2162 dstream<<"finishBlockMake: changed_blocks.size()="
2163 <<changed_blocks.size()<<std::endl;
2166 Copy transforming liquid information
2168 while(data->transforming_liquid.size() > 0)
2170 v3s16 p = data->transforming_liquid.pop_front();
2171 m_transforming_liquid.push_back(p);
2177 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2181 Set is_underground flag for lighting with sunlight.
2183 Refer to map generator heuristics.
2185 NOTE: This is done in initChunkMake
2187 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2191 Add sunlight to central block.
2192 This makes in-dark-spawning monsters to not flood the whole thing.
2193 Do not spread the light, though.
2195 /*core::map<v3s16, bool> light_sources;
2196 bool black_air_left = false;
2197 block->propagateSunlight(light_sources, true, &black_air_left);*/
2200 NOTE: Lighting and object adding shouldn't really be here, but
2201 lighting is a bit tricky to move properly to makeBlock.
2202 TODO: Do this the right way anyway, that is, move it to makeBlock.
2203 - There needs to be some way for makeBlock to report back if
2204 the lighting update is going further down because of the
2205 new block blocking light
2210 NOTE: This takes ~60ms, TODO: Investigate why
2213 TimeTaker t("finishBlockMake lighting update");
2215 core::map<v3s16, MapBlock*> lighting_update_blocks;
2218 lighting_update_blocks.insert(block->getPos(), block);
2223 v3s16 p = block->getPos()+v3s16(x,1,z);
2224 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2228 // All modified blocks
2229 // NOTE: Should this be done? If this is not done, then the lighting
2230 // of the others will be updated in a different place, one by one, i
2231 // think... or they might not? Well, at least they are left marked as
2232 // "lighting expired"; it seems that is not handled at all anywhere,
2233 // so enabling this will slow it down A LOT because otherwise it
2234 // would not do this at all. This causes the black trees.
2235 for(core::map<v3s16, MapBlock*>::Iterator
2236 i = changed_blocks.getIterator();
2237 i.atEnd() == false; i++)
2239 lighting_update_blocks.insert(i.getNode()->getKey(),
2240 i.getNode()->getValue());
2242 /*// Also force-add all the upmost blocks for proper sunlight
2243 for(s16 x=-1; x<=1; x++)
2244 for(s16 z=-1; z<=1; z++)
2246 v3s16 p = block->getPos()+v3s16(x,1,z);
2247 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2250 updateLighting(lighting_update_blocks, changed_blocks);
2253 Set lighting to non-expired state in all of them.
2254 This is cheating, but it is not fast enough if all of them
2255 would actually be updated.
2257 for(s16 x=-1; x<=1; x++)
2258 for(s16 y=-1; y<=1; y++)
2259 for(s16 z=-1; z<=1; z++)
2261 v3s16 p = block->getPos()+v3s16(x,y,z);
2262 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2265 if(enable_mapgen_debug_info == false)
2266 t.stop(true); // Hide output
2270 Add random objects to block
2272 mapgen::add_random_objects(block);
2275 Go through changed blocks
2277 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2278 i.atEnd() == false; i++)
2280 MapBlock *block = i.getNode()->getValue();
2283 Update day/night difference cache of the MapBlocks
2285 block->updateDayNightDiff();
2287 Set block as modified
2289 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2293 Set central block as generated
2295 block->setGenerated(true);
2298 Save changed parts of map
2299 NOTE: Will be saved later.
2303 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2304 <<blockpos.Z<<")"<<std::endl;*/
2306 if(enable_mapgen_debug_info)
2309 Analyze resulting blocks
2311 for(s16 x=-1; x<=1; x++)
2312 for(s16 y=-1; y<=1; y++)
2313 for(s16 z=-1; z<=1; z++)
2315 v3s16 p = block->getPos()+v3s16(x,y,z);
2316 MapBlock *block = getBlockNoCreateNoEx(p);
2318 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2319 dstream<<"Generated "<<spos<<": "
2320 <<analyze_block(block)<<std::endl;
2328 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2330 DSTACKF("%s: p2d=(%d,%d)",
2335 Check if it exists already in memory
2337 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2342 Try to load it from disk (with blocks)
2344 //if(loadSectorFull(p2d) == true)
2347 Try to load metadata from disk
2350 if(loadSectorMeta(p2d) == true)
2352 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2355 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2356 throw InvalidPositionException("");
2362 Do not create over-limit
2364 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2365 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2366 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2367 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2368 throw InvalidPositionException("createSector(): pos. over limit");
2371 Generate blank sector
2374 sector = new ServerMapSector(this, p2d);
2376 // Sector position on map in nodes
2377 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2382 m_sectors.insert(p2d, sector);
2388 This is a quick-hand function for calling makeBlock().
2390 MapBlock * ServerMap::generateBlock(
2392 core::map<v3s16, MapBlock*> &modified_blocks
2395 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2397 /*dstream<<"generateBlock(): "
2398 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2401 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2403 TimeTaker timer("generateBlock");
2405 //MapBlock *block = original_dummy;
2407 v2s16 p2d(p.X, p.Z);
2408 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2411 Do not generate over-limit
2413 if(blockpos_over_limit(p))
2415 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2416 throw InvalidPositionException("generateBlock(): pos. over limit");
2420 Create block make data
2422 mapgen::BlockMakeData data;
2423 initBlockMake(&data, p);
2429 TimeTaker t("mapgen::make_block()");
2430 mapgen::make_block(&data);
2432 if(enable_mapgen_debug_info == false)
2433 t.stop(true); // Hide output
2437 Blit data back on map, update lighting, add mobs and whatever this does
2439 finishBlockMake(&data, modified_blocks);
2444 MapBlock *block = getBlockNoCreateNoEx(p);
2452 bool erroneus_content = false;
2453 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2454 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2455 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2458 MapNode n = block->getNode(p);
2459 if(n.getContent() == CONTENT_IGNORE)
2461 dstream<<"CONTENT_IGNORE at "
2462 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2464 erroneus_content = true;
2468 if(erroneus_content)
2477 Generate a completely empty block
2481 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2482 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2484 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2488 n.setContent(CONTENT_AIR);
2490 n.setContent(CONTENT_STONE);
2491 block->setNode(v3s16(x0,y0,z0), n);
2497 if(enable_mapgen_debug_info == false)
2498 timer.stop(true); // Hide output
2503 MapBlock * ServerMap::createBlock(v3s16 p)
2505 DSTACKF("%s: p=(%d,%d,%d)",
2506 __FUNCTION_NAME, p.X, p.Y, p.Z);
2509 Do not create over-limit
2511 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2512 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2513 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2514 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2515 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2516 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2517 throw InvalidPositionException("createBlock(): pos. over limit");
2519 v2s16 p2d(p.X, p.Z);
2522 This will create or load a sector if not found in memory.
2523 If block exists on disk, it will be loaded.
2525 NOTE: On old save formats, this will be slow, as it generates
2526 lighting on blocks for them.
2528 ServerMapSector *sector;
2530 sector = (ServerMapSector*)createSector(p2d);
2531 assert(sector->getId() == MAPSECTOR_SERVER);
2533 catch(InvalidPositionException &e)
2535 dstream<<"createBlock: createSector() failed"<<std::endl;
2539 NOTE: This should not be done, or at least the exception
2540 should not be passed on as std::exception, because it
2541 won't be catched at all.
2543 /*catch(std::exception &e)
2545 dstream<<"createBlock: createSector() failed: "
2546 <<e.what()<<std::endl;
2551 Try to get a block from the sector
2554 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2557 if(block->isDummy())
2562 block = sector->createBlankBlock(block_y);
2566 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2568 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2570 p.X, p.Y, p.Z, allow_generate);
2573 MapBlock *block = getBlockNoCreateNoEx(p);
2574 if(block && block->isDummy() == false)
2579 MapBlock *block = loadBlock(p);
2586 core::map<v3s16, MapBlock*> modified_blocks;
2587 MapBlock *block = generateBlock(p, modified_blocks);
2591 event.type = MEET_OTHER;
2594 // Copy modified_blocks to event
2595 for(core::map<v3s16, MapBlock*>::Iterator
2596 i = modified_blocks.getIterator();
2597 i.atEnd()==false; i++)
2599 event.modified_blocks.insert(i.getNode()->getKey(), false);
2603 dispatchEvent(&event);
2614 Do not generate over-limit
2616 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2617 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2618 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2619 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2620 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2621 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2622 throw InvalidPositionException("emergeBlock(): pos. over limit");
2624 v2s16 p2d(p.X, p.Z);
2627 This will create or load a sector if not found in memory.
2628 If block exists on disk, it will be loaded.
2630 ServerMapSector *sector;
2632 sector = createSector(p2d);
2633 //sector = emergeSector(p2d, changed_blocks);
2635 catch(InvalidPositionException &e)
2637 dstream<<"emergeBlock: createSector() failed: "
2638 <<e.what()<<std::endl;
2639 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2641 <<"You could try to delete it."<<std::endl;
2644 catch(VersionMismatchException &e)
2646 dstream<<"emergeBlock: createSector() failed: "
2647 <<e.what()<<std::endl;
2648 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2650 <<"You could try to delete it."<<std::endl;
2655 Try to get a block from the sector
2658 bool does_not_exist = false;
2659 bool lighting_expired = false;
2660 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2662 // If not found, try loading from disk
2665 block = loadBlock(p);
2671 does_not_exist = true;
2673 else if(block->isDummy() == true)
2675 does_not_exist = true;
2677 else if(block->getLightingExpired())
2679 lighting_expired = true;
2684 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2689 If block was not found on disk and not going to generate a
2690 new one, make sure there is a dummy block in place.
2692 if(only_from_disk && (does_not_exist || lighting_expired))
2694 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2698 // Create dummy block
2699 block = new MapBlock(this, p, true);
2701 // Add block to sector
2702 sector->insertBlock(block);
2708 //dstream<<"Not found on disk, generating."<<std::endl;
2710 //TimeTaker("emergeBlock() generate");
2712 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2715 If the block doesn't exist, generate the block.
2719 block = generateBlock(p, block, sector, changed_blocks,
2720 lighting_invalidated_blocks);
2723 if(lighting_expired)
2725 lighting_invalidated_blocks.insert(p, block);
2730 Initially update sunlight
2733 core::map<v3s16, bool> light_sources;
2734 bool black_air_left = false;
2735 bool bottom_invalid =
2736 block->propagateSunlight(light_sources, true,
2739 // If sunlight didn't reach everywhere and part of block is
2740 // above ground, lighting has to be properly updated
2741 //if(black_air_left && some_part_underground)
2744 lighting_invalidated_blocks[block->getPos()] = block;
2749 lighting_invalidated_blocks[block->getPos()] = block;
2758 s16 ServerMap::findGroundLevel(v2s16 p2d)
2762 Uh, just do something random...
2764 // Find existing map from top to down
2767 v3s16 p(p2d.X, max, p2d.Y);
2768 for(; p.Y>min; p.Y--)
2770 MapNode n = getNodeNoEx(p);
2771 if(n.getContent() != CONTENT_IGNORE)
2776 // If this node is not air, go to plan b
2777 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2779 // Search existing walkable and return it
2780 for(; p.Y>min; p.Y--)
2782 MapNode n = getNodeNoEx(p);
2783 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2792 Determine from map generator noise functions
2795 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2798 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2799 //return (s16)level;
2802 void ServerMap::createDatabase() {
2805 e = sqlite3_exec(m_database,
2806 "CREATE TABLE IF NOT EXISTS `blocks` ("
2807 "`pos` INT NOT NULL PRIMARY KEY,"
2810 , NULL, NULL, NULL);
2811 if(e == SQLITE_ABORT)
2812 throw FileNotGoodException("Could not create database structure");
2814 dstream<<"Server: Database structure was created";
2817 void ServerMap::verifyDatabase() {
2822 std::string dbp = m_savedir + "/map.sqlite";
2823 bool needs_create = false;
2827 Open the database connection
2830 createDirs(m_savedir);
2832 if(!fs::PathExists(dbp))
2833 needs_create = true;
2835 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2836 if(d != SQLITE_OK) {
2837 dstream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2838 throw FileNotGoodException("Cannot open database file");
2844 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2845 if(d != SQLITE_OK) {
2846 dstream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2847 throw FileNotGoodException("Cannot prepare read statement");
2850 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2851 if(d != SQLITE_OK) {
2852 dstream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2853 throw FileNotGoodException("Cannot prepare write statement");
2856 dstream<<"Server: Database opened"<<std::endl;
2860 bool ServerMap::loadFromFolders() {
2861 if(!m_database && !fs::PathExists(m_savedir + "/map.sqlite"))
2866 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2867 return (sqlite3_int64)pos.Z*16777216 +
2868 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2871 void ServerMap::createDirs(std::string path)
2873 if(fs::CreateAllDirs(path) == false)
2875 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2876 <<"\""<<path<<"\""<<std::endl;
2877 throw BaseException("ServerMap failed to create directory");
2881 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2887 snprintf(cc, 9, "%.4x%.4x",
2888 (unsigned int)pos.X&0xffff,
2889 (unsigned int)pos.Y&0xffff);
2891 return m_savedir + "/sectors/" + cc;
2893 snprintf(cc, 9, "%.3x/%.3x",
2894 (unsigned int)pos.X&0xfff,
2895 (unsigned int)pos.Y&0xfff);
2897 return m_savedir + "/sectors2/" + cc;
2903 v2s16 ServerMap::getSectorPos(std::string dirname)
2907 size_t spos = dirname.rfind('/') + 1;
2908 assert(spos != std::string::npos);
2909 if(dirname.size() - spos == 8)
2912 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2914 else if(dirname.size() - spos == 3)
2917 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2918 // Sign-extend the 12 bit values up to 16 bits...
2919 if(x&0x800) x|=0xF000;
2920 if(y&0x800) y|=0xF000;
2927 v2s16 pos((s16)x, (s16)y);
2931 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2933 v2s16 p2d = getSectorPos(sectordir);
2935 if(blockfile.size() != 4){
2936 throw InvalidFilenameException("Invalid block filename");
2939 int r = sscanf(blockfile.c_str(), "%4x", &y);
2941 throw InvalidFilenameException("Invalid block filename");
2942 return v3s16(p2d.X, y, p2d.Y);
2945 std::string ServerMap::getBlockFilename(v3s16 p)
2948 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2952 void ServerMap::save(bool only_changed)
2954 DSTACK(__FUNCTION_NAME);
2955 if(m_map_saving_enabled == false)
2957 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2961 if(only_changed == false)
2962 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2965 if(only_changed == false || m_map_metadata_changed)
2970 u32 sector_meta_count = 0;
2971 u32 block_count = 0;
2972 u32 block_count_all = 0; // Number of blocks in memory
2975 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2976 for(; i.atEnd() == false; i++)
2978 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2979 assert(sector->getId() == MAPSECTOR_SERVER);
2981 if(sector->differs_from_disk || only_changed == false)
2983 saveSectorMeta(sector);
2984 sector_meta_count++;
2986 core::list<MapBlock*> blocks;
2987 sector->getBlocks(blocks);
2988 core::list<MapBlock*>::Iterator j;
2990 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2991 for(j=blocks.begin(); j!=blocks.end(); j++)
2993 MapBlock *block = *j;
2997 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2998 || only_changed == false)
3003 /*dstream<<"ServerMap: Written block ("
3004 <<block->getPos().X<<","
3005 <<block->getPos().Y<<","
3006 <<block->getPos().Z<<")"
3009 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3015 Only print if something happened or saved whole map
3017 if(only_changed == false || sector_meta_count != 0
3018 || block_count != 0)
3020 dstream<<DTIME<<"ServerMap: Written: "
3021 <<sector_meta_count<<" sector metadata files, "
3022 <<block_count<<" block files"
3023 <<", "<<block_count_all<<" blocks in memory."
3028 void ServerMap::saveMapMeta()
3030 DSTACK(__FUNCTION_NAME);
3032 dstream<<"INFO: ServerMap::saveMapMeta(): "
3036 createDirs(m_savedir);
3038 std::string fullpath = m_savedir + "/map_meta.txt";
3039 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3040 if(os.good() == false)
3042 dstream<<"ERROR: ServerMap::saveMapMeta(): "
3043 <<"could not open"<<fullpath<<std::endl;
3044 throw FileNotGoodException("Cannot open chunk metadata");
3048 params.setU64("seed", m_seed);
3050 params.writeLines(os);
3052 os<<"[end_of_params]\n";
3054 m_map_metadata_changed = false;
3057 void ServerMap::loadMapMeta()
3059 DSTACK(__FUNCTION_NAME);
3061 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
3064 std::string fullpath = m_savedir + "/map_meta.txt";
3065 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3066 if(is.good() == false)
3068 dstream<<"ERROR: ServerMap::loadMapMeta(): "
3069 <<"could not open"<<fullpath<<std::endl;
3070 throw FileNotGoodException("Cannot open map metadata");
3078 throw SerializationError
3079 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3081 std::getline(is, line);
3082 std::string trimmedline = trim(line);
3083 if(trimmedline == "[end_of_params]")
3085 params.parseConfigLine(line);
3088 m_seed = params.getU64("seed");
3090 dstream<<"INFO: ServerMap::loadMapMeta(): "
3095 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3097 DSTACK(__FUNCTION_NAME);
3098 // Format used for writing
3099 u8 version = SER_FMT_VER_HIGHEST;
3101 v2s16 pos = sector->getPos();
3102 std::string dir = getSectorDir(pos);
3105 std::string fullpath = dir + "/meta";
3106 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3107 if(o.good() == false)
3108 throw FileNotGoodException("Cannot open sector metafile");
3110 sector->serialize(o, version);
3112 sector->differs_from_disk = false;
3115 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3117 DSTACK(__FUNCTION_NAME);
3119 v2s16 p2d = getSectorPos(sectordir);
3121 ServerMapSector *sector = NULL;
3123 std::string fullpath = sectordir + "/meta";
3124 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3125 if(is.good() == false)
3127 // If the directory exists anyway, it probably is in some old
3128 // format. Just go ahead and create the sector.
3129 if(fs::PathExists(sectordir))
3131 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3132 <<fullpath<<" doesn't exist but directory does."
3133 <<" Continuing with a sector with no metadata."
3135 sector = new ServerMapSector(this, p2d);
3136 m_sectors.insert(p2d, sector);
3140 throw FileNotGoodException("Cannot open sector metafile");
3145 sector = ServerMapSector::deSerialize
3146 (is, this, p2d, m_sectors);
3148 saveSectorMeta(sector);
3151 sector->differs_from_disk = false;
3156 bool ServerMap::loadSectorMeta(v2s16 p2d)
3158 DSTACK(__FUNCTION_NAME);
3160 MapSector *sector = NULL;
3162 // The directory layout we're going to load from.
3163 // 1 - original sectors/xxxxzzzz/
3164 // 2 - new sectors2/xxx/zzz/
3165 // If we load from anything but the latest structure, we will
3166 // immediately save to the new one, and remove the old.
3168 std::string sectordir1 = getSectorDir(p2d, 1);
3169 std::string sectordir;
3170 if(fs::PathExists(sectordir1))
3172 sectordir = sectordir1;
3177 sectordir = getSectorDir(p2d, 2);
3181 sector = loadSectorMeta(sectordir, loadlayout != 2);
3183 catch(InvalidFilenameException &e)
3187 catch(FileNotGoodException &e)
3191 catch(std::exception &e)
3200 bool ServerMap::loadSectorFull(v2s16 p2d)
3202 DSTACK(__FUNCTION_NAME);
3204 MapSector *sector = NULL;
3206 // The directory layout we're going to load from.
3207 // 1 - original sectors/xxxxzzzz/
3208 // 2 - new sectors2/xxx/zzz/
3209 // If we load from anything but the latest structure, we will
3210 // immediately save to the new one, and remove the old.
3212 std::string sectordir1 = getSectorDir(p2d, 1);
3213 std::string sectordir;
3214 if(fs::PathExists(sectordir1))
3216 sectordir = sectordir1;
3221 sectordir = getSectorDir(p2d, 2);
3225 sector = loadSectorMeta(sectordir, loadlayout != 2);
3227 catch(InvalidFilenameException &e)
3231 catch(FileNotGoodException &e)
3235 catch(std::exception &e)
3243 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3245 std::vector<fs::DirListNode>::iterator i2;
3246 for(i2=list2.begin(); i2!=list2.end(); i2++)
3252 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3254 catch(InvalidFilenameException &e)
3256 // This catches unknown crap in directory
3262 dstream<<"Sector converted to new layout - deleting "<<
3263 sectordir1<<std::endl;
3264 fs::RecursiveDelete(sectordir1);
3271 void ServerMap::beginSave() {
3273 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3274 dstream<<"WARNING: beginSave() failed, saving might be slow.";
3277 void ServerMap::endSave() {
3279 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3280 dstream<<"WARNING: endSave() failed, map might not have saved.";
3283 void ServerMap::saveBlock(MapBlock *block)
3285 DSTACK(__FUNCTION_NAME);
3287 Dummy blocks are not written
3289 if(block->isDummy())
3291 /*v3s16 p = block->getPos();
3292 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3293 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3297 // Format used for writing
3298 u8 version = SER_FMT_VER_HIGHEST;
3300 v3s16 p3d = block->getPos();
3304 v2s16 p2d(p3d.X, p3d.Z);
3305 std::string sectordir = getSectorDir(p2d);
3307 createDirs(sectordir);
3309 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3310 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3311 if(o.good() == false)
3312 throw FileNotGoodException("Cannot open block data");
3315 [0] u8 serialization version
3321 std::ostringstream o(std::ios_base::binary);
3323 o.write((char*)&version, 1);
3326 block->serialize(o, version);
3328 // Write extra data stored on disk
3329 block->serializeDiskExtra(o, version);
3331 // Write block to database
3333 std::string tmp = o.str();
3334 const char *bytes = tmp.c_str();
3336 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3337 dstream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3338 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3339 dstream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3340 int written = sqlite3_step(m_database_write);
3341 if(written != SQLITE_DONE)
3342 dstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3343 <<sqlite3_errmsg(m_database)<<std::endl;
3344 // Make ready for later reuse
3345 sqlite3_reset(m_database_write);
3347 // We just wrote it to the disk so clear modified flag
3348 block->resetModified();
3351 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3353 DSTACK(__FUNCTION_NAME);
3355 std::string fullpath = sectordir+"/"+blockfile;
3358 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3359 if(is.good() == false)
3360 throw FileNotGoodException("Cannot open block file");
3362 v3s16 p3d = getBlockPos(sectordir, blockfile);
3363 v2s16 p2d(p3d.X, p3d.Z);
3365 assert(sector->getPos() == p2d);
3367 u8 version = SER_FMT_VER_INVALID;
3368 is.read((char*)&version, 1);
3371 throw SerializationError("ServerMap::loadBlock(): Failed"
3372 " to read MapBlock version");
3374 /*u32 block_size = MapBlock::serializedLength(version);
3375 SharedBuffer<u8> data(block_size);
3376 is.read((char*)*data, block_size);*/
3378 // This will always return a sector because we're the server
3379 //MapSector *sector = emergeSector(p2d);
3381 MapBlock *block = NULL;
3382 bool created_new = false;
3383 block = sector->getBlockNoCreateNoEx(p3d.Y);
3386 block = sector->createBlankBlockNoInsert(p3d.Y);
3391 block->deSerialize(is, version);
3393 // Read extra data stored on disk
3394 block->deSerializeDiskExtra(is, version);
3396 // If it's a new block, insert it to the map
3398 sector->insertBlock(block);
3401 Save blocks loaded in old format in new format
3404 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3408 // Should be in database now, so delete the old file
3409 fs::RecursiveDelete(fullpath);
3412 // We just loaded it from the disk, so it's up-to-date.
3413 block->resetModified();
3416 catch(SerializationError &e)
3418 dstream<<"WARNING: Invalid block data on disk "
3419 <<"fullpath="<<fullpath
3420 <<" (SerializationError). "
3421 <<"what()="<<e.what()
3423 //" Ignoring. A new one will be generated.
3426 // TODO: Backup file; name is in fullpath.
3430 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3432 DSTACK(__FUNCTION_NAME);
3435 std::istringstream is(*blob, std::ios_base::binary);
3437 u8 version = SER_FMT_VER_INVALID;
3438 is.read((char*)&version, 1);
3441 throw SerializationError("ServerMap::loadBlock(): Failed"
3442 " to read MapBlock version");
3444 /*u32 block_size = MapBlock::serializedLength(version);
3445 SharedBuffer<u8> data(block_size);
3446 is.read((char*)*data, block_size);*/
3448 // This will always return a sector because we're the server
3449 //MapSector *sector = emergeSector(p2d);
3451 MapBlock *block = NULL;
3452 bool created_new = false;
3453 block = sector->getBlockNoCreateNoEx(p3d.Y);
3456 block = sector->createBlankBlockNoInsert(p3d.Y);
3461 block->deSerialize(is, version);
3463 // Read extra data stored on disk
3464 block->deSerializeDiskExtra(is, version);
3466 // If it's a new block, insert it to the map
3468 sector->insertBlock(block);
3471 Save blocks loaded in old format in new format
3474 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3479 // We just loaded it from, so it's up-to-date.
3480 block->resetModified();
3483 catch(SerializationError &e)
3485 dstream<<"WARNING: Invalid block data in database "
3486 <<" (SerializationError). "
3487 <<"what()="<<e.what()
3489 //" Ignoring. A new one will be generated.
3492 // TODO: Copy to a backup database.
3496 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3498 DSTACK(__FUNCTION_NAME);
3500 v2s16 p2d(blockpos.X, blockpos.Z);
3502 if(!loadFromFolders()) {
3505 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3506 dstream<<"WARNING: Could not bind block position for load: "
3507 <<sqlite3_errmsg(m_database)<<std::endl;
3508 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3510 Make sure sector is loaded
3512 MapSector *sector = createSector(p2d);
3517 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3518 size_t len = sqlite3_column_bytes(m_database_read, 0);
3520 std::string datastr(data, len);
3522 loadBlock(&datastr, blockpos, sector, false);
3524 sqlite3_step(m_database_read);
3525 // We should never get more than 1 row, so ok to reset
3526 sqlite3_reset(m_database_read);
3528 return getBlockNoCreateNoEx(blockpos);
3530 sqlite3_reset(m_database_read);
3532 // Not found in database, try the files
3535 // The directory layout we're going to load from.
3536 // 1 - original sectors/xxxxzzzz/
3537 // 2 - new sectors2/xxx/zzz/
3538 // If we load from anything but the latest structure, we will
3539 // immediately save to the new one, and remove the old.
3541 std::string sectordir1 = getSectorDir(p2d, 1);
3542 std::string sectordir;
3543 if(fs::PathExists(sectordir1))
3545 sectordir = sectordir1;
3550 sectordir = getSectorDir(p2d, 2);
3554 Make sure sector is loaded
3556 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3560 sector = loadSectorMeta(sectordir, loadlayout != 2);
3562 catch(InvalidFilenameException &e)
3566 catch(FileNotGoodException &e)
3570 catch(std::exception &e)
3577 Make sure file exists
3580 std::string blockfilename = getBlockFilename(blockpos);
3581 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3585 Load block and save it to the database
3587 loadBlock(sectordir, blockfilename, sector, true);
3588 return getBlockNoCreateNoEx(blockpos);
3591 void ServerMap::PrintInfo(std::ostream &out)
3602 ClientMap::ClientMap(
3604 MapDrawControl &control,
3605 scene::ISceneNode* parent,
3606 scene::ISceneManager* mgr,
3610 scene::ISceneNode(parent, mgr, id),
3613 m_camera_position(0,0,0),
3614 m_camera_direction(0,0,1),
3617 m_camera_mutex.Init();
3618 assert(m_camera_mutex.IsInitialized());
3620 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3621 BS*1000000,BS*1000000,BS*1000000);
3624 ClientMap::~ClientMap()
3626 /*JMutexAutoLock lock(mesh_mutex);
3635 MapSector * ClientMap::emergeSector(v2s16 p2d)
3637 DSTACK(__FUNCTION_NAME);
3638 // Check that it doesn't exist already
3640 return getSectorNoGenerate(p2d);
3642 catch(InvalidPositionException &e)
3647 ClientMapSector *sector = new ClientMapSector(this, p2d);
3650 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3651 m_sectors.insert(p2d, sector);
3658 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3660 DSTACK(__FUNCTION_NAME);
3661 ClientMapSector *sector = NULL;
3663 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3665 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3669 sector = (ClientMapSector*)n->getValue();
3670 assert(sector->getId() == MAPSECTOR_CLIENT);
3674 sector = new ClientMapSector(this, p2d);
3676 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3677 m_sectors.insert(p2d, sector);
3681 sector->deSerialize(is);
3685 void ClientMap::OnRegisterSceneNode()
3689 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3690 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3693 ISceneNode::OnRegisterSceneNode();
3696 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3698 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3699 DSTACK(__FUNCTION_NAME);
3701 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3704 This is called two times per frame, reset on the non-transparent one
3706 if(pass == scene::ESNRP_SOLID)
3708 m_last_drawn_sectors.clear();
3712 Get time for measuring timeout.
3714 Measuring time is very useful for long delays when the
3715 machine is swapping a lot.
3717 int time1 = time(0);
3719 //u32 daynight_ratio = m_client->getDayNightRatio();
3721 m_camera_mutex.Lock();
3722 v3f camera_position = m_camera_position;
3723 v3f camera_direction = m_camera_direction;
3724 f32 camera_fov = m_camera_fov;
3725 m_camera_mutex.Unlock();
3728 Get all blocks and draw all visible ones
3731 v3s16 cam_pos_nodes(
3732 camera_position.X / BS,
3733 camera_position.Y / BS,
3734 camera_position.Z / BS);
3736 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3738 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3739 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3741 // Take a fair amount as we will be dropping more out later
3743 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3744 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3745 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3747 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3748 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3749 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3751 u32 vertex_count = 0;
3753 // For limiting number of mesh updates per frame
3754 u32 mesh_update_count = 0;
3756 u32 blocks_would_have_drawn = 0;
3757 u32 blocks_drawn = 0;
3759 int timecheck_counter = 0;
3760 core::map<v2s16, MapSector*>::Iterator si;
3761 si = m_sectors.getIterator();
3762 for(; si.atEnd() == false; si++)
3765 timecheck_counter++;
3766 if(timecheck_counter > 50)
3768 timecheck_counter = 0;
3769 int time2 = time(0);
3770 if(time2 > time1 + 4)
3772 dstream<<"ClientMap::renderMap(): "
3773 "Rendering takes ages, returning."
3780 MapSector *sector = si.getNode()->getValue();
3781 v2s16 sp = sector->getPos();
3783 if(m_control.range_all == false)
3785 if(sp.X < p_blocks_min.X
3786 || sp.X > p_blocks_max.X
3787 || sp.Y < p_blocks_min.Z
3788 || sp.Y > p_blocks_max.Z)
3792 core::list< MapBlock * > sectorblocks;
3793 sector->getBlocks(sectorblocks);
3799 u32 sector_blocks_drawn = 0;
3801 core::list< MapBlock * >::Iterator i;
3802 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3804 MapBlock *block = *i;
3807 Compare block position to camera position, skip
3808 if not seen on display
3811 float range = 100000 * BS;
3812 if(m_control.range_all == false)
3813 range = m_control.wanted_range * BS;
3816 if(isBlockInSight(block->getPos(), camera_position,
3817 camera_direction, camera_fov,
3818 range, &d) == false)
3823 // Okay, this block will be drawn. Reset usage timer.
3824 block->resetUsageTimer();
3826 // This is ugly (spherical distance limit?)
3827 /*if(m_control.range_all == false &&
3828 d - 0.5*BS*MAP_BLOCKSIZE > range)
3833 Update expired mesh (used for day/night change)
3835 It doesn't work exactly like it should now with the
3836 tasked mesh update but whatever.
3839 bool mesh_expired = false;
3842 JMutexAutoLock lock(block->mesh_mutex);
3844 mesh_expired = block->getMeshExpired();
3846 // Mesh has not been expired and there is no mesh:
3847 // block has no content
3848 if(block->mesh == NULL && mesh_expired == false)
3852 f32 faraway = BS*50;
3853 //f32 faraway = m_control.wanted_range * BS;
3856 This has to be done with the mesh_mutex unlocked
3858 // Pretty random but this should work somewhat nicely
3859 if(mesh_expired && (
3860 (mesh_update_count < 3
3861 && (d < faraway || mesh_update_count < 2)
3864 (m_control.range_all && mesh_update_count < 20)
3867 /*if(mesh_expired && mesh_update_count < 6
3868 && (d < faraway || mesh_update_count < 3))*/
3870 mesh_update_count++;
3872 // Mesh has been expired: generate new mesh
3873 //block->updateMesh(daynight_ratio);
3874 m_client->addUpdateMeshTask(block->getPos());
3876 mesh_expired = false;
3881 Draw the faces of the block
3884 JMutexAutoLock lock(block->mesh_mutex);
3886 scene::SMesh *mesh = block->mesh;
3891 blocks_would_have_drawn++;
3892 if(blocks_drawn >= m_control.wanted_max_blocks
3893 && m_control.range_all == false
3894 && d > m_control.wanted_min_range * BS)
3898 sector_blocks_drawn++;
3900 u32 c = mesh->getMeshBufferCount();
3902 for(u32 i=0; i<c; i++)
3904 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3905 const video::SMaterial& material = buf->getMaterial();
3906 video::IMaterialRenderer* rnd =
3907 driver->getMaterialRenderer(material.MaterialType);
3908 bool transparent = (rnd && rnd->isTransparent());
3909 // Render transparent on transparent pass and likewise.
3910 if(transparent == is_transparent_pass)
3913 This *shouldn't* hurt too much because Irrlicht
3914 doesn't change opengl textures if the old
3915 material is set again.
3917 driver->setMaterial(buf->getMaterial());
3918 driver->drawMeshBuffer(buf);
3919 vertex_count += buf->getVertexCount();
3923 } // foreach sectorblocks
3925 if(sector_blocks_drawn != 0)
3927 m_last_drawn_sectors[sp] = true;
3931 m_control.blocks_drawn = blocks_drawn;
3932 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3934 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3935 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3938 void ClientMap::renderPostFx()
3940 // Sadly ISceneManager has no "post effects" render pass, in that case we
3941 // could just register for that and handle it in renderMap().
3943 m_camera_mutex.Lock();
3944 v3f camera_position = m_camera_position;
3945 m_camera_mutex.Unlock();
3947 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
3949 // - If the player is in a solid node, make everything black.
3950 // - If the player is in liquid, draw a semi-transparent overlay.
3951 ContentFeatures& features = content_features(n);
3952 video::SColor post_effect_color = features.post_effect_color;
3953 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
3955 post_effect_color = video::SColor(255, 0, 0, 0);
3957 if (post_effect_color.getAlpha() != 0)
3959 // Draw a full-screen rectangle
3960 video::IVideoDriver* driver = SceneManager->getVideoDriver();
3961 v2u32 ss = driver->getScreenSize();
3962 core::rect<s32> rect(0,0, ss.X, ss.Y);
3963 driver->draw2DRectangle(post_effect_color, rect);
3967 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3968 core::map<v3s16, MapBlock*> *affected_blocks)
3970 bool changed = false;
3972 Add it to all blocks touching it
3975 v3s16(0,0,0), // this
3976 v3s16(0,0,1), // back
3977 v3s16(0,1,0), // top
3978 v3s16(1,0,0), // right
3979 v3s16(0,0,-1), // front
3980 v3s16(0,-1,0), // bottom
3981 v3s16(-1,0,0), // left
3983 for(u16 i=0; i<7; i++)
3985 v3s16 p2 = p + dirs[i];
3986 // Block position of neighbor (or requested) node
3987 v3s16 blockpos = getNodeBlockPos(p2);
3988 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3989 if(blockref == NULL)
3991 // Relative position of requested node
3992 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3993 if(blockref->setTempMod(relpos, mod))
3998 if(changed && affected_blocks!=NULL)
4000 for(u16 i=0; i<7; i++)
4002 v3s16 p2 = p + dirs[i];
4003 // Block position of neighbor (or requested) node
4004 v3s16 blockpos = getNodeBlockPos(p2);
4005 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4006 if(blockref == NULL)
4008 affected_blocks->insert(blockpos, blockref);
4014 bool ClientMap::clearTempMod(v3s16 p,
4015 core::map<v3s16, MapBlock*> *affected_blocks)
4017 bool changed = false;
4019 v3s16(0,0,0), // this
4020 v3s16(0,0,1), // back
4021 v3s16(0,1,0), // top
4022 v3s16(1,0,0), // right
4023 v3s16(0,0,-1), // front
4024 v3s16(0,-1,0), // bottom
4025 v3s16(-1,0,0), // left
4027 for(u16 i=0; i<7; i++)
4029 v3s16 p2 = p + dirs[i];
4030 // Block position of neighbor (or requested) node
4031 v3s16 blockpos = getNodeBlockPos(p2);
4032 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4033 if(blockref == NULL)
4035 // Relative position of requested node
4036 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4037 if(blockref->clearTempMod(relpos))
4042 if(changed && affected_blocks!=NULL)
4044 for(u16 i=0; i<7; i++)
4046 v3s16 p2 = p + dirs[i];
4047 // Block position of neighbor (or requested) node
4048 v3s16 blockpos = getNodeBlockPos(p2);
4049 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4050 if(blockref == NULL)
4052 affected_blocks->insert(blockpos, blockref);
4058 void ClientMap::expireMeshes(bool only_daynight_diffed)
4060 TimeTaker timer("expireMeshes()");
4062 core::map<v2s16, MapSector*>::Iterator si;
4063 si = m_sectors.getIterator();
4064 for(; si.atEnd() == false; si++)
4066 MapSector *sector = si.getNode()->getValue();
4068 core::list< MapBlock * > sectorblocks;
4069 sector->getBlocks(sectorblocks);
4071 core::list< MapBlock * >::Iterator i;
4072 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4074 MapBlock *block = *i;
4076 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4082 JMutexAutoLock lock(block->mesh_mutex);
4083 if(block->mesh != NULL)
4085 /*block->mesh->drop();
4086 block->mesh = NULL;*/
4087 block->setMeshExpired(true);
4094 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4096 assert(mapType() == MAPTYPE_CLIENT);
4099 v3s16 p = blockpos + v3s16(0,0,0);
4100 MapBlock *b = getBlockNoCreate(p);
4101 b->updateMesh(daynight_ratio);
4102 //b->setMeshExpired(true);
4104 catch(InvalidPositionException &e){}
4107 v3s16 p = blockpos + v3s16(-1,0,0);
4108 MapBlock *b = getBlockNoCreate(p);
4109 b->updateMesh(daynight_ratio);
4110 //b->setMeshExpired(true);
4112 catch(InvalidPositionException &e){}
4114 v3s16 p = blockpos + v3s16(0,-1,0);
4115 MapBlock *b = getBlockNoCreate(p);
4116 b->updateMesh(daynight_ratio);
4117 //b->setMeshExpired(true);
4119 catch(InvalidPositionException &e){}
4121 v3s16 p = blockpos + v3s16(0,0,-1);
4122 MapBlock *b = getBlockNoCreate(p);
4123 b->updateMesh(daynight_ratio);
4124 //b->setMeshExpired(true);
4126 catch(InvalidPositionException &e){}
4131 Update mesh of block in which the node is, and if the node is at the
4132 leading edge, update the appropriate leading blocks too.
4134 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4142 v3s16 blockposes[4];
4143 for(u32 i=0; i<4; i++)
4145 v3s16 np = nodepos + dirs[i];
4146 blockposes[i] = getNodeBlockPos(np);
4147 // Don't update mesh of block if it has been done already
4148 bool already_updated = false;
4149 for(u32 j=0; j<i; j++)
4151 if(blockposes[j] == blockposes[i])
4153 already_updated = true;
4160 MapBlock *b = getBlockNoCreate(blockposes[i]);
4161 b->updateMesh(daynight_ratio);
4166 void ClientMap::PrintInfo(std::ostream &out)
4177 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4182 MapVoxelManipulator::~MapVoxelManipulator()
4184 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4188 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4190 TimeTaker timer1("emerge", &emerge_time);
4192 // Units of these are MapBlocks
4193 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4194 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4196 VoxelArea block_area_nodes
4197 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4199 addArea(block_area_nodes);
4201 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4202 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4203 for(s32 x=p_min.X; x<=p_max.X; x++)
4206 core::map<v3s16, bool>::Node *n;
4207 n = m_loaded_blocks.find(p);
4211 bool block_data_inexistent = false;
4214 TimeTaker timer1("emerge load", &emerge_load_time);
4216 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4217 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4220 dstream<<std::endl;*/
4222 MapBlock *block = m_map->getBlockNoCreate(p);
4223 if(block->isDummy())
4224 block_data_inexistent = true;
4226 block->copyTo(*this);
4228 catch(InvalidPositionException &e)
4230 block_data_inexistent = true;
4233 if(block_data_inexistent)
4235 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4236 // Fill with VOXELFLAG_INEXISTENT
4237 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4238 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4240 s32 i = m_area.index(a.MinEdge.X,y,z);
4241 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4245 m_loaded_blocks.insert(p, !block_data_inexistent);
4248 //dstream<<"emerge done"<<std::endl;
4252 SUGG: Add an option to only update eg. water and air nodes.
4253 This will make it interfere less with important stuff if
4256 void MapVoxelManipulator::blitBack
4257 (core::map<v3s16, MapBlock*> & modified_blocks)
4259 if(m_area.getExtent() == v3s16(0,0,0))
4262 //TimeTaker timer1("blitBack");
4264 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4265 <<m_loaded_blocks.size()<<std::endl;*/
4268 Initialize block cache
4270 v3s16 blockpos_last;
4271 MapBlock *block = NULL;
4272 bool block_checked_in_modified = false;
4274 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4275 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4276 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4280 u8 f = m_flags[m_area.index(p)];
4281 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4284 MapNode &n = m_data[m_area.index(p)];
4286 v3s16 blockpos = getNodeBlockPos(p);
4291 if(block == NULL || blockpos != blockpos_last){
4292 block = m_map->getBlockNoCreate(blockpos);
4293 blockpos_last = blockpos;
4294 block_checked_in_modified = false;
4297 // Calculate relative position in block
4298 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4300 // Don't continue if nothing has changed here
4301 if(block->getNode(relpos) == n)
4304 //m_map->setNode(m_area.MinEdge + p, n);
4305 block->setNode(relpos, n);
4308 Make sure block is in modified_blocks
4310 if(block_checked_in_modified == false)
4312 modified_blocks[blockpos] = block;
4313 block_checked_in_modified = true;
4316 catch(InvalidPositionException &e)
4322 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4323 MapVoxelManipulator(map),
4324 m_create_area(false)
4328 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4332 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4334 // Just create the area so that it can be pointed to
4335 VoxelManipulator::emerge(a, caller_id);
4338 void ManualMapVoxelManipulator::initialEmerge(
4339 v3s16 blockpos_min, v3s16 blockpos_max)
4341 TimeTaker timer1("initialEmerge", &emerge_time);
4343 // Units of these are MapBlocks
4344 v3s16 p_min = blockpos_min;
4345 v3s16 p_max = blockpos_max;
4347 VoxelArea block_area_nodes
4348 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4350 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4353 dstream<<"initialEmerge: area: ";
4354 block_area_nodes.print(dstream);
4355 dstream<<" ("<<size_MB<<"MB)";
4359 addArea(block_area_nodes);
4361 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4362 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4363 for(s32 x=p_min.X; x<=p_max.X; x++)
4366 core::map<v3s16, bool>::Node *n;
4367 n = m_loaded_blocks.find(p);
4371 bool block_data_inexistent = false;
4374 TimeTaker timer1("emerge load", &emerge_load_time);
4376 MapBlock *block = m_map->getBlockNoCreate(p);
4377 if(block->isDummy())
4378 block_data_inexistent = true;
4380 block->copyTo(*this);
4382 catch(InvalidPositionException &e)
4384 block_data_inexistent = true;
4387 if(block_data_inexistent)
4390 Mark area inexistent
4392 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4393 // Fill with VOXELFLAG_INEXISTENT
4394 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4395 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4397 s32 i = m_area.index(a.MinEdge.X,y,z);
4398 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4402 m_loaded_blocks.insert(p, !block_data_inexistent);
4406 void ManualMapVoxelManipulator::blitBackAll(
4407 core::map<v3s16, MapBlock*> * modified_blocks)
4409 if(m_area.getExtent() == v3s16(0,0,0))
4413 Copy data of all blocks
4415 for(core::map<v3s16, bool>::Iterator
4416 i = m_loaded_blocks.getIterator();
4417 i.atEnd() == false; i++)
4419 v3s16 p = i.getNode()->getKey();
4420 bool existed = i.getNode()->getValue();
4421 if(existed == false)
4423 // The Great Bug was found using this
4424 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4425 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4429 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4432 dstream<<"WARNING: "<<__FUNCTION_NAME
4433 <<": got NULL block "
4434 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4439 block->copyFrom(*this);
4442 modified_blocks->insert(p, block);