3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
30 #include "nodemetadata.h"
31 #include "content_mapnode.h"
33 #include <IMaterialRenderer.h>
39 SQLite format specification:
40 - Initially only replaces sectors/ and sectors2/
42 If map.sqlite does not exist in the save dir
43 or the block was not found in the database
44 the map will try to load from sectors folder.
45 In either case, map.sqlite will be created
46 and all future saves will save there.
48 Structure of map.sqlite:
59 Map::Map(std::ostream &dout):
63 /*m_sector_mutex.Init();
64 assert(m_sector_mutex.IsInitialized());*/
72 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
73 for(; i.atEnd() == false; i++)
75 MapSector *sector = i.getNode()->getValue();
80 void Map::addEventReceiver(MapEventReceiver *event_receiver)
82 m_event_receivers.insert(event_receiver, false);
85 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
87 if(m_event_receivers.find(event_receiver) == NULL)
89 m_event_receivers.remove(event_receiver);
92 void Map::dispatchEvent(MapEditEvent *event)
94 for(core::map<MapEventReceiver*, bool>::Iterator
95 i = m_event_receivers.getIterator();
96 i.atEnd()==false; i++)
98 MapEventReceiver* event_receiver = i.getNode()->getKey();
99 event_receiver->onMapEditEvent(event);
103 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
105 if(m_sector_cache != NULL && p == m_sector_cache_p){
106 MapSector * sector = m_sector_cache;
110 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
115 MapSector *sector = n->getValue();
117 // Cache the last result
118 m_sector_cache_p = p;
119 m_sector_cache = sector;
124 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
126 return getSectorNoGenerateNoExNoLock(p);
129 MapSector * Map::getSectorNoGenerate(v2s16 p)
131 MapSector *sector = getSectorNoGenerateNoEx(p);
133 throw InvalidPositionException();
138 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
140 v2s16 p2d(p3d.X, p3d.Z);
141 MapSector * sector = getSectorNoGenerateNoEx(p2d);
144 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
148 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
150 MapBlock *block = getBlockNoCreateNoEx(p3d);
152 throw InvalidPositionException();
156 bool Map::isNodeUnderground(v3s16 p)
158 v3s16 blockpos = getNodeBlockPos(p);
160 MapBlock * block = getBlockNoCreate(blockpos);
161 return block->getIsUnderground();
163 catch(InvalidPositionException &e)
169 bool Map::isValidPosition(v3s16 p)
171 v3s16 blockpos = getNodeBlockPos(p);
172 MapBlock *block = getBlockNoCreate(blockpos);
173 return (block != NULL);
176 // Returns a CONTENT_IGNORE node if not found
177 MapNode Map::getNodeNoEx(v3s16 p)
179 v3s16 blockpos = getNodeBlockPos(p);
180 MapBlock *block = getBlockNoCreateNoEx(blockpos);
182 return MapNode(CONTENT_IGNORE);
183 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
184 return block->getNodeNoCheck(relpos);
187 // throws InvalidPositionException if not found
188 MapNode Map::getNode(v3s16 p)
190 v3s16 blockpos = getNodeBlockPos(p);
191 MapBlock *block = getBlockNoCreateNoEx(blockpos);
193 throw InvalidPositionException();
194 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
195 return block->getNodeNoCheck(relpos);
198 // throws InvalidPositionException if not found
199 void Map::setNode(v3s16 p, MapNode & n)
201 v3s16 blockpos = getNodeBlockPos(p);
202 MapBlock *block = getBlockNoCreate(blockpos);
203 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
204 block->setNodeNoCheck(relpos, n);
209 Goes recursively through the neighbours of the node.
211 Alters only transparent nodes.
213 If the lighting of the neighbour is lower than the lighting of
214 the node was (before changing it to 0 at the step before), the
215 lighting of the neighbour is set to 0 and then the same stuff
216 repeats for the neighbour.
218 The ending nodes of the routine are stored in light_sources.
219 This is useful when a light is removed. In such case, this
220 routine can be called for the light node and then again for
221 light_sources to re-light the area without the removed light.
223 values of from_nodes are lighting values.
225 void Map::unspreadLight(enum LightBank bank,
226 core::map<v3s16, u8> & from_nodes,
227 core::map<v3s16, bool> & light_sources,
228 core::map<v3s16, MapBlock*> & modified_blocks)
231 v3s16(0,0,1), // back
233 v3s16(1,0,0), // right
234 v3s16(0,0,-1), // front
235 v3s16(0,-1,0), // bottom
236 v3s16(-1,0,0), // left
239 if(from_nodes.size() == 0)
242 u32 blockchangecount = 0;
244 core::map<v3s16, u8> unlighted_nodes;
245 core::map<v3s16, u8>::Iterator j;
246 j = from_nodes.getIterator();
249 Initialize block cache
252 MapBlock *block = NULL;
253 // Cache this a bit, too
254 bool block_checked_in_modified = false;
256 for(; j.atEnd() == false; j++)
258 v3s16 pos = j.getNode()->getKey();
259 v3s16 blockpos = getNodeBlockPos(pos);
261 // Only fetch a new block if the block position has changed
263 if(block == NULL || blockpos != blockpos_last){
264 block = getBlockNoCreate(blockpos);
265 blockpos_last = blockpos;
267 block_checked_in_modified = false;
271 catch(InvalidPositionException &e)
279 // Calculate relative position in block
280 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
282 // Get node straight from the block
283 MapNode n = block->getNode(relpos);
285 u8 oldlight = j.getNode()->getValue();
287 // Loop through 6 neighbors
288 for(u16 i=0; i<6; i++)
290 // Get the position of the neighbor node
291 v3s16 n2pos = pos + dirs[i];
293 // Get the block where the node is located
294 v3s16 blockpos = getNodeBlockPos(n2pos);
298 // Only fetch a new block if the block position has changed
300 if(block == NULL || blockpos != blockpos_last){
301 block = getBlockNoCreate(blockpos);
302 blockpos_last = blockpos;
304 block_checked_in_modified = false;
308 catch(InvalidPositionException &e)
313 // Calculate relative position in block
314 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
315 // Get node straight from the block
316 MapNode n2 = block->getNode(relpos);
318 bool changed = false;
320 //TODO: Optimize output by optimizing light_sources?
323 If the neighbor is dimmer than what was specified
324 as oldlight (the light of the previous node)
326 if(n2.getLight(bank) < oldlight)
329 And the neighbor is transparent and it has some light
331 if(n2.light_propagates() && n2.getLight(bank) != 0)
334 Set light to 0 and add to queue
337 u8 current_light = n2.getLight(bank);
338 n2.setLight(bank, 0);
339 block->setNode(relpos, n2);
341 unlighted_nodes.insert(n2pos, current_light);
345 Remove from light_sources if it is there
346 NOTE: This doesn't happen nearly at all
348 /*if(light_sources.find(n2pos))
350 infostream<<"Removed from light_sources"<<std::endl;
351 light_sources.remove(n2pos);
356 if(light_sources.find(n2pos) != NULL)
357 light_sources.remove(n2pos);*/
360 light_sources.insert(n2pos, true);
363 // Add to modified_blocks
364 if(changed == true && block_checked_in_modified == false)
366 // If the block is not found in modified_blocks, add.
367 if(modified_blocks.find(blockpos) == NULL)
369 modified_blocks.insert(blockpos, block);
371 block_checked_in_modified = true;
374 catch(InvalidPositionException &e)
381 /*infostream<<"unspreadLight(): Changed block "
382 <<blockchangecount<<" times"
383 <<" for "<<from_nodes.size()<<" nodes"
386 if(unlighted_nodes.size() > 0)
387 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
391 A single-node wrapper of the above
393 void Map::unLightNeighbors(enum LightBank bank,
394 v3s16 pos, u8 lightwas,
395 core::map<v3s16, bool> & light_sources,
396 core::map<v3s16, MapBlock*> & modified_blocks)
398 core::map<v3s16, u8> from_nodes;
399 from_nodes.insert(pos, lightwas);
401 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
405 Lights neighbors of from_nodes, collects all them and then
408 void Map::spreadLight(enum LightBank bank,
409 core::map<v3s16, bool> & from_nodes,
410 core::map<v3s16, MapBlock*> & modified_blocks)
412 const v3s16 dirs[6] = {
413 v3s16(0,0,1), // back
415 v3s16(1,0,0), // right
416 v3s16(0,0,-1), // front
417 v3s16(0,-1,0), // bottom
418 v3s16(-1,0,0), // left
421 if(from_nodes.size() == 0)
424 u32 blockchangecount = 0;
426 core::map<v3s16, bool> lighted_nodes;
427 core::map<v3s16, bool>::Iterator j;
428 j = from_nodes.getIterator();
431 Initialize block cache
434 MapBlock *block = NULL;
435 // Cache this a bit, too
436 bool block_checked_in_modified = false;
438 for(; j.atEnd() == false; j++)
439 //for(; j != from_nodes.end(); j++)
441 v3s16 pos = j.getNode()->getKey();
443 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
444 v3s16 blockpos = getNodeBlockPos(pos);
446 // Only fetch a new block if the block position has changed
448 if(block == NULL || blockpos != blockpos_last){
449 block = getBlockNoCreate(blockpos);
450 blockpos_last = blockpos;
452 block_checked_in_modified = false;
456 catch(InvalidPositionException &e)
464 // Calculate relative position in block
465 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
467 // Get node straight from the block
468 MapNode n = block->getNode(relpos);
470 u8 oldlight = n.getLight(bank);
471 u8 newlight = diminish_light(oldlight);
473 // Loop through 6 neighbors
474 for(u16 i=0; i<6; i++){
475 // Get the position of the neighbor node
476 v3s16 n2pos = pos + dirs[i];
478 // Get the block where the node is located
479 v3s16 blockpos = getNodeBlockPos(n2pos);
483 // Only fetch a new block if the block position has changed
485 if(block == NULL || blockpos != blockpos_last){
486 block = getBlockNoCreate(blockpos);
487 blockpos_last = blockpos;
489 block_checked_in_modified = false;
493 catch(InvalidPositionException &e)
498 // Calculate relative position in block
499 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
500 // Get node straight from the block
501 MapNode n2 = block->getNode(relpos);
503 bool changed = false;
505 If the neighbor is brighter than the current node,
506 add to list (it will light up this node on its turn)
508 if(n2.getLight(bank) > undiminish_light(oldlight))
510 lighted_nodes.insert(n2pos, true);
511 //lighted_nodes.push_back(n2pos);
515 If the neighbor is dimmer than how much light this node
516 would spread on it, add to list
518 if(n2.getLight(bank) < newlight)
520 if(n2.light_propagates())
522 n2.setLight(bank, newlight);
523 block->setNode(relpos, n2);
524 lighted_nodes.insert(n2pos, true);
525 //lighted_nodes.push_back(n2pos);
530 // Add to modified_blocks
531 if(changed == true && block_checked_in_modified == false)
533 // If the block is not found in modified_blocks, add.
534 if(modified_blocks.find(blockpos) == NULL)
536 modified_blocks.insert(blockpos, block);
538 block_checked_in_modified = true;
541 catch(InvalidPositionException &e)
548 /*infostream<<"spreadLight(): Changed block "
549 <<blockchangecount<<" times"
550 <<" for "<<from_nodes.size()<<" nodes"
553 if(lighted_nodes.size() > 0)
554 spreadLight(bank, lighted_nodes, modified_blocks);
558 A single-node source variation of the above.
560 void Map::lightNeighbors(enum LightBank bank,
562 core::map<v3s16, MapBlock*> & modified_blocks)
564 core::map<v3s16, bool> from_nodes;
565 from_nodes.insert(pos, true);
566 spreadLight(bank, from_nodes, modified_blocks);
569 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
572 v3s16(0,0,1), // back
574 v3s16(1,0,0), // right
575 v3s16(0,0,-1), // front
576 v3s16(0,-1,0), // bottom
577 v3s16(-1,0,0), // left
580 u8 brightest_light = 0;
581 v3s16 brightest_pos(0,0,0);
582 bool found_something = false;
584 // Loop through 6 neighbors
585 for(u16 i=0; i<6; i++){
586 // Get the position of the neighbor node
587 v3s16 n2pos = p + dirs[i];
592 catch(InvalidPositionException &e)
596 if(n2.getLight(bank) > brightest_light || found_something == false){
597 brightest_light = n2.getLight(bank);
598 brightest_pos = n2pos;
599 found_something = true;
603 if(found_something == false)
604 throw InvalidPositionException();
606 return brightest_pos;
610 Propagates sunlight down from a node.
611 Starting point gets sunlight.
613 Returns the lowest y value of where the sunlight went.
615 Mud is turned into grass in where the sunlight stops.
617 s16 Map::propagateSunlight(v3s16 start,
618 core::map<v3s16, MapBlock*> & modified_blocks)
623 v3s16 pos(start.X, y, start.Z);
625 v3s16 blockpos = getNodeBlockPos(pos);
628 block = getBlockNoCreate(blockpos);
630 catch(InvalidPositionException &e)
635 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
636 MapNode n = block->getNode(relpos);
638 if(n.sunlight_propagates())
640 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
641 block->setNode(relpos, n);
643 modified_blocks.insert(blockpos, block);
647 /*// Turn mud into grass
648 if(n.getContent() == CONTENT_MUD)
650 n.setContent(CONTENT_GRASS);
651 block->setNode(relpos, n);
652 modified_blocks.insert(blockpos, block);
655 // Sunlight goes no further
662 void Map::updateLighting(enum LightBank bank,
663 core::map<v3s16, MapBlock*> & a_blocks,
664 core::map<v3s16, MapBlock*> & modified_blocks)
666 /*m_dout<<DTIME<<"Map::updateLighting(): "
667 <<a_blocks.size()<<" blocks."<<std::endl;*/
669 //TimeTaker timer("updateLighting");
673 //u32 count_was = modified_blocks.size();
675 core::map<v3s16, MapBlock*> blocks_to_update;
677 core::map<v3s16, bool> light_sources;
679 core::map<v3s16, u8> unlight_from;
681 core::map<v3s16, MapBlock*>::Iterator i;
682 i = a_blocks.getIterator();
683 for(; i.atEnd() == false; i++)
685 MapBlock *block = i.getNode()->getValue();
689 // Don't bother with dummy blocks.
693 v3s16 pos = block->getPos();
694 modified_blocks.insert(pos, block);
696 blocks_to_update.insert(pos, block);
699 Clear all light from block
701 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
702 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
703 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
708 MapNode n = block->getNode(v3s16(x,y,z));
709 u8 oldlight = n.getLight(bank);
711 block->setNode(v3s16(x,y,z), n);
713 // Collect borders for unlighting
714 if(x==0 || x == MAP_BLOCKSIZE-1
715 || y==0 || y == MAP_BLOCKSIZE-1
716 || z==0 || z == MAP_BLOCKSIZE-1)
718 v3s16 p_map = p + v3s16(
721 MAP_BLOCKSIZE*pos.Z);
722 unlight_from.insert(p_map, oldlight);
725 catch(InvalidPositionException &e)
728 This would happen when dealing with a
732 infostream<<"updateLighting(): InvalidPositionException"
737 if(bank == LIGHTBANK_DAY)
739 bool bottom_valid = block->propagateSunlight(light_sources);
741 // If bottom is valid, we're done.
745 else if(bank == LIGHTBANK_NIGHT)
747 // For night lighting, sunlight is not propagated
752 // Invalid lighting bank
756 /*infostream<<"Bottom for sunlight-propagated block ("
757 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
760 // Bottom sunlight is not valid; get the block and loop to it
764 block = getBlockNoCreate(pos);
766 catch(InvalidPositionException &e)
775 Enable this to disable proper lighting for speeding up map
776 generation for testing or whatever
779 //if(g_settings->get(""))
781 core::map<v3s16, MapBlock*>::Iterator i;
782 i = blocks_to_update.getIterator();
783 for(; i.atEnd() == false; i++)
785 MapBlock *block = i.getNode()->getValue();
786 v3s16 p = block->getPos();
787 block->setLightingExpired(false);
795 TimeTaker timer("unspreadLight");
796 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
801 u32 diff = modified_blocks.size() - count_was;
802 count_was = modified_blocks.size();
803 infostream<<"unspreadLight modified "<<diff<<std::endl;
807 TimeTaker timer("spreadLight");
808 spreadLight(bank, light_sources, modified_blocks);
813 u32 diff = modified_blocks.size() - count_was;
814 count_was = modified_blocks.size();
815 infostream<<"spreadLight modified "<<diff<<std::endl;
820 //MapVoxelManipulator vmanip(this);
822 // Make a manual voxel manipulator and load all the blocks
823 // that touch the requested blocks
824 ManualMapVoxelManipulator vmanip(this);
825 core::map<v3s16, MapBlock*>::Iterator i;
826 i = blocks_to_update.getIterator();
827 for(; i.atEnd() == false; i++)
829 MapBlock *block = i.getNode()->getValue();
830 v3s16 p = block->getPos();
832 // Add all surrounding blocks
833 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
836 Add all surrounding blocks that have up-to-date lighting
837 NOTE: This doesn't quite do the job (not everything
838 appropriate is lighted)
840 /*for(s16 z=-1; z<=1; z++)
841 for(s16 y=-1; y<=1; y++)
842 for(s16 x=-1; x<=1; x++)
845 MapBlock *block = getBlockNoCreateNoEx(p);
850 if(block->getLightingExpired())
852 vmanip.initialEmerge(p, p);
855 // Lighting of block will be updated completely
856 block->setLightingExpired(false);
860 //TimeTaker timer("unSpreadLight");
861 vmanip.unspreadLight(bank, unlight_from, light_sources);
864 //TimeTaker timer("spreadLight");
865 vmanip.spreadLight(bank, light_sources);
868 //TimeTaker timer("blitBack");
869 vmanip.blitBack(modified_blocks);
871 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
875 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
878 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
879 core::map<v3s16, MapBlock*> & modified_blocks)
881 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
882 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
885 Update information about whether day and night light differ
887 for(core::map<v3s16, MapBlock*>::Iterator
888 i = modified_blocks.getIterator();
889 i.atEnd() == false; i++)
891 MapBlock *block = i.getNode()->getValue();
892 block->updateDayNightDiff();
898 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
899 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
902 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
903 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
906 From this node to nodes underneath:
907 If lighting is sunlight (1.0), unlight neighbours and
912 v3s16 toppos = p + v3s16(0,1,0);
913 v3s16 bottompos = p + v3s16(0,-1,0);
915 bool node_under_sunlight = true;
916 core::map<v3s16, bool> light_sources;
919 If there is a node at top and it doesn't have sunlight,
920 there has not been any sunlight going down.
922 Otherwise there probably is.
925 MapNode topnode = getNode(toppos);
927 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
928 node_under_sunlight = false;
930 catch(InvalidPositionException &e)
936 If the new node is solid and there is grass below, change it to mud
938 if(content_features(n).walkable == true)
941 MapNode bottomnode = getNode(bottompos);
943 if(bottomnode.getContent() == CONTENT_GRASS
944 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
946 bottomnode.setContent(CONTENT_MUD);
947 setNode(bottompos, bottomnode);
950 catch(InvalidPositionException &e)
958 If the new node is mud and it is under sunlight, change it
961 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
963 n.setContent(CONTENT_GRASS);
968 Remove all light that has come out of this node
971 enum LightBank banks[] =
976 for(s32 i=0; i<2; i++)
978 enum LightBank bank = banks[i];
980 u8 lightwas = getNode(p).getLight(bank);
982 // Add the block of the added node to modified_blocks
983 v3s16 blockpos = getNodeBlockPos(p);
984 MapBlock * block = getBlockNoCreate(blockpos);
985 assert(block != NULL);
986 modified_blocks.insert(blockpos, block);
988 assert(isValidPosition(p));
990 // Unlight neighbours of node.
991 // This means setting light of all consequent dimmer nodes
993 // This also collects the nodes at the border which will spread
994 // light again into this.
995 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1001 If node lets sunlight through and is under sunlight, it has
1004 if(node_under_sunlight && content_features(n).sunlight_propagates)
1006 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1010 Set the node on the map
1019 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1022 NodeMetadata *meta = meta_proto->clone();
1023 meta->setOwner(player_name);
1024 setNodeMetadata(p, meta);
1028 If node is under sunlight and doesn't let sunlight through,
1029 take all sunlighted nodes under it and clear light from them
1030 and from where the light has been spread.
1031 TODO: This could be optimized by mass-unlighting instead
1034 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1038 //m_dout<<DTIME<<"y="<<y<<std::endl;
1039 v3s16 n2pos(p.X, y, p.Z);
1043 n2 = getNode(n2pos);
1045 catch(InvalidPositionException &e)
1050 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1052 unLightNeighbors(LIGHTBANK_DAY,
1053 n2pos, n2.getLight(LIGHTBANK_DAY),
1054 light_sources, modified_blocks);
1055 n2.setLight(LIGHTBANK_DAY, 0);
1063 for(s32 i=0; i<2; i++)
1065 enum LightBank bank = banks[i];
1068 Spread light from all nodes that might be capable of doing so
1070 spreadLight(bank, light_sources, modified_blocks);
1074 Update information about whether day and night light differ
1076 for(core::map<v3s16, MapBlock*>::Iterator
1077 i = modified_blocks.getIterator();
1078 i.atEnd() == false; i++)
1080 MapBlock *block = i.getNode()->getValue();
1081 block->updateDayNightDiff();
1085 Add neighboring liquid nodes and the node itself if it is
1086 liquid (=water node was added) to transform queue.
1089 v3s16(0,0,0), // self
1090 v3s16(0,0,1), // back
1091 v3s16(0,1,0), // top
1092 v3s16(1,0,0), // right
1093 v3s16(0,0,-1), // front
1094 v3s16(0,-1,0), // bottom
1095 v3s16(-1,0,0), // left
1097 for(u16 i=0; i<7; i++)
1102 v3s16 p2 = p + dirs[i];
1104 MapNode n2 = getNode(p2);
1105 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1107 m_transforming_liquid.push_back(p2);
1110 }catch(InvalidPositionException &e)
1118 void Map::removeNodeAndUpdate(v3s16 p,
1119 core::map<v3s16, MapBlock*> &modified_blocks)
1121 /*PrintInfo(m_dout);
1122 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1123 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1125 bool node_under_sunlight = true;
1127 v3s16 toppos = p + v3s16(0,1,0);
1129 // Node will be replaced with this
1130 content_t replace_material = CONTENT_AIR;
1133 If there is a node at top and it doesn't have sunlight,
1134 there will be no sunlight going down.
1137 MapNode topnode = getNode(toppos);
1139 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1140 node_under_sunlight = false;
1142 catch(InvalidPositionException &e)
1146 core::map<v3s16, bool> light_sources;
1148 enum LightBank banks[] =
1153 for(s32 i=0; i<2; i++)
1155 enum LightBank bank = banks[i];
1158 Unlight neighbors (in case the node is a light source)
1160 unLightNeighbors(bank, p,
1161 getNode(p).getLight(bank),
1162 light_sources, modified_blocks);
1166 Remove node metadata
1169 removeNodeMetadata(p);
1173 This also clears the lighting.
1177 n.setContent(replace_material);
1180 for(s32 i=0; i<2; i++)
1182 enum LightBank bank = banks[i];
1185 Recalculate lighting
1187 spreadLight(bank, light_sources, modified_blocks);
1190 // Add the block of the removed node to modified_blocks
1191 v3s16 blockpos = getNodeBlockPos(p);
1192 MapBlock * block = getBlockNoCreate(blockpos);
1193 assert(block != NULL);
1194 modified_blocks.insert(blockpos, block);
1197 If the removed node was under sunlight, propagate the
1198 sunlight down from it and then light all neighbors
1199 of the propagated blocks.
1201 if(node_under_sunlight)
1203 s16 ybottom = propagateSunlight(p, modified_blocks);
1204 /*m_dout<<DTIME<<"Node was under sunlight. "
1205 "Propagating sunlight";
1206 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1208 for(; y >= ybottom; y--)
1210 v3s16 p2(p.X, y, p.Z);
1211 /*m_dout<<DTIME<<"lighting neighbors of node ("
1212 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1214 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1219 // Set the lighting of this node to 0
1220 // TODO: Is this needed? Lighting is cleared up there already.
1222 MapNode n = getNode(p);
1223 n.setLight(LIGHTBANK_DAY, 0);
1226 catch(InvalidPositionException &e)
1232 for(s32 i=0; i<2; i++)
1234 enum LightBank bank = banks[i];
1236 // Get the brightest neighbour node and propagate light from it
1237 v3s16 n2p = getBrightestNeighbour(bank, p);
1239 MapNode n2 = getNode(n2p);
1240 lightNeighbors(bank, n2p, modified_blocks);
1242 catch(InvalidPositionException &e)
1248 Update information about whether day and night light differ
1250 for(core::map<v3s16, MapBlock*>::Iterator
1251 i = modified_blocks.getIterator();
1252 i.atEnd() == false; i++)
1254 MapBlock *block = i.getNode()->getValue();
1255 block->updateDayNightDiff();
1259 Add neighboring liquid nodes and this node to transform queue.
1260 (it's vital for the node itself to get updated last.)
1263 v3s16(0,0,1), // back
1264 v3s16(0,1,0), // top
1265 v3s16(1,0,0), // right
1266 v3s16(0,0,-1), // front
1267 v3s16(0,-1,0), // bottom
1268 v3s16(-1,0,0), // left
1269 v3s16(0,0,0), // self
1271 for(u16 i=0; i<7; i++)
1276 v3s16 p2 = p + dirs[i];
1278 MapNode n2 = getNode(p2);
1279 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1281 m_transforming_liquid.push_back(p2);
1284 }catch(InvalidPositionException &e)
1290 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1293 event.type = MEET_ADDNODE;
1297 bool succeeded = true;
1299 core::map<v3s16, MapBlock*> modified_blocks;
1300 std::string st = std::string("");
1301 addNodeAndUpdate(p, n, modified_blocks, st);
1303 // Copy modified_blocks to event
1304 for(core::map<v3s16, MapBlock*>::Iterator
1305 i = modified_blocks.getIterator();
1306 i.atEnd()==false; i++)
1308 event.modified_blocks.insert(i.getNode()->getKey(), false);
1311 catch(InvalidPositionException &e){
1315 dispatchEvent(&event);
1320 bool Map::removeNodeWithEvent(v3s16 p)
1323 event.type = MEET_REMOVENODE;
1326 bool succeeded = true;
1328 core::map<v3s16, MapBlock*> modified_blocks;
1329 removeNodeAndUpdate(p, modified_blocks);
1331 // Copy modified_blocks to event
1332 for(core::map<v3s16, MapBlock*>::Iterator
1333 i = modified_blocks.getIterator();
1334 i.atEnd()==false; i++)
1336 event.modified_blocks.insert(i.getNode()->getKey(), false);
1339 catch(InvalidPositionException &e){
1343 dispatchEvent(&event);
1348 bool Map::dayNightDiffed(v3s16 blockpos)
1351 v3s16 p = blockpos + v3s16(0,0,0);
1352 MapBlock *b = getBlockNoCreate(p);
1353 if(b->dayNightDiffed())
1356 catch(InvalidPositionException &e){}
1359 v3s16 p = blockpos + v3s16(-1,0,0);
1360 MapBlock *b = getBlockNoCreate(p);
1361 if(b->dayNightDiffed())
1364 catch(InvalidPositionException &e){}
1366 v3s16 p = blockpos + v3s16(0,-1,0);
1367 MapBlock *b = getBlockNoCreate(p);
1368 if(b->dayNightDiffed())
1371 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(0,0,-1);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->dayNightDiffed())
1378 catch(InvalidPositionException &e){}
1381 v3s16 p = blockpos + v3s16(1,0,0);
1382 MapBlock *b = getBlockNoCreate(p);
1383 if(b->dayNightDiffed())
1386 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(0,1,0);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->dayNightDiffed())
1393 catch(InvalidPositionException &e){}
1395 v3s16 p = blockpos + v3s16(0,0,1);
1396 MapBlock *b = getBlockNoCreate(p);
1397 if(b->dayNightDiffed())
1400 catch(InvalidPositionException &e){}
1406 Updates usage timers
1408 void Map::timerUpdate(float dtime, float unload_timeout,
1409 core::list<v3s16> *unloaded_blocks)
1411 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1413 core::list<v2s16> sector_deletion_queue;
1414 u32 deleted_blocks_count = 0;
1415 u32 saved_blocks_count = 0;
1417 core::map<v2s16, MapSector*>::Iterator si;
1420 si = m_sectors.getIterator();
1421 for(; si.atEnd() == false; si++)
1423 MapSector *sector = si.getNode()->getValue();
1425 bool all_blocks_deleted = true;
1427 core::list<MapBlock*> blocks;
1428 sector->getBlocks(blocks);
1430 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1431 i != blocks.end(); i++)
1433 MapBlock *block = (*i);
1435 block->incrementUsageTimer(dtime);
1437 if(block->getUsageTimer() > unload_timeout)
1439 v3s16 p = block->getPos();
1442 if(block->getModified() != MOD_STATE_CLEAN
1443 && save_before_unloading)
1446 saved_blocks_count++;
1449 // Delete from memory
1450 sector->deleteBlock(block);
1453 unloaded_blocks->push_back(p);
1455 deleted_blocks_count++;
1459 all_blocks_deleted = false;
1463 if(all_blocks_deleted)
1465 sector_deletion_queue.push_back(si.getNode()->getKey());
1470 // Finally delete the empty sectors
1471 deleteSectors(sector_deletion_queue);
1473 if(deleted_blocks_count != 0)
1475 PrintInfo(infostream); // ServerMap/ClientMap:
1476 infostream<<"Unloaded "<<deleted_blocks_count
1477 <<" blocks from memory";
1478 if(save_before_unloading)
1479 infostream<<", of which "<<saved_blocks_count<<" were written";
1480 infostream<<"."<<std::endl;
1484 void Map::deleteSectors(core::list<v2s16> &list)
1486 core::list<v2s16>::Iterator j;
1487 for(j=list.begin(); j!=list.end(); j++)
1489 MapSector *sector = m_sectors[*j];
1490 // If sector is in sector cache, remove it from there
1491 if(m_sector_cache == sector)
1492 m_sector_cache = NULL;
1493 // Remove from map and delete
1494 m_sectors.remove(*j);
1500 void Map::unloadUnusedData(float timeout,
1501 core::list<v3s16> *deleted_blocks)
1503 core::list<v2s16> sector_deletion_queue;
1504 u32 deleted_blocks_count = 0;
1505 u32 saved_blocks_count = 0;
1507 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1508 for(; si.atEnd() == false; si++)
1510 MapSector *sector = si.getNode()->getValue();
1512 bool all_blocks_deleted = true;
1514 core::list<MapBlock*> blocks;
1515 sector->getBlocks(blocks);
1516 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1517 i != blocks.end(); i++)
1519 MapBlock *block = (*i);
1521 if(block->getUsageTimer() > timeout)
1524 if(block->getModified() != MOD_STATE_CLEAN)
1527 saved_blocks_count++;
1529 // Delete from memory
1530 sector->deleteBlock(block);
1531 deleted_blocks_count++;
1535 all_blocks_deleted = false;
1539 if(all_blocks_deleted)
1541 sector_deletion_queue.push_back(si.getNode()->getKey());
1545 deleteSectors(sector_deletion_queue);
1547 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1548 <<", of which "<<saved_blocks_count<<" were wr."
1551 //return sector_deletion_queue.getSize();
1552 //return deleted_blocks_count;
1556 void Map::PrintInfo(std::ostream &out)
1561 #define WATER_DROP_BOOST 4
1565 NEIGHBOR_SAME_LEVEL,
1568 struct NodeNeighbor {
1574 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1576 DSTACK(__FUNCTION_NAME);
1577 //TimeTaker timer("transformLiquids()");
1580 u32 initial_size = m_transforming_liquid.size();
1582 /*if(initial_size != 0)
1583 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1585 // list of nodes that due to viscosity have not reached their max level height
1586 UniqueQueue<v3s16> must_reflow;
1588 // List of MapBlocks that will require a lighting update (due to lava)
1589 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1591 while(m_transforming_liquid.size() != 0)
1593 // This should be done here so that it is done when continue is used
1594 if(loopcount >= initial_size * 3)
1599 Get a queued transforming liquid node
1601 v3s16 p0 = m_transforming_liquid.pop_front();
1603 MapNode n0 = getNodeNoEx(p0);
1606 Collect information about current node
1608 s8 liquid_level = -1;
1609 u8 liquid_kind = CONTENT_IGNORE;
1610 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1611 switch (liquid_type) {
1613 liquid_level = LIQUID_LEVEL_SOURCE;
1614 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1616 case LIQUID_FLOWING:
1617 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1618 liquid_kind = n0.getContent();
1621 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1622 // continue with the next node.
1623 if (n0.getContent() != CONTENT_AIR)
1625 liquid_kind = CONTENT_AIR;
1630 Collect information about the environment
1632 const v3s16 *dirs = g_6dirs;
1633 NodeNeighbor sources[6]; // surrounding sources
1634 int num_sources = 0;
1635 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1637 NodeNeighbor airs[6]; // surrounding air
1639 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1640 int num_neutrals = 0;
1641 bool flowing_down = false;
1642 for (u16 i = 0; i < 6; i++) {
1643 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1646 nt = NEIGHBOR_UPPER;
1649 nt = NEIGHBOR_LOWER;
1652 v3s16 npos = p0 + dirs[i];
1653 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1654 switch (content_features(nb.n.getContent()).liquid_type) {
1656 if (nb.n.getContent() == CONTENT_AIR) {
1657 airs[num_airs++] = nb;
1658 // if the current node is a water source the neighbor
1659 // should be enqueded for transformation regardless of whether the
1660 // current node changes or not.
1661 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1662 m_transforming_liquid.push_back(npos);
1663 // if the current node happens to be a flowing node, it will start to flow down here.
1664 if (nb.t == NEIGHBOR_LOWER) {
1665 flowing_down = true;
1668 neutrals[num_neutrals++] = nb;
1672 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1673 if (liquid_kind == CONTENT_AIR)
1674 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1675 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1676 neutrals[num_neutrals++] = nb;
1678 sources[num_sources++] = nb;
1681 case LIQUID_FLOWING:
1682 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1683 if (liquid_kind == CONTENT_AIR)
1684 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1685 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1686 neutrals[num_neutrals++] = nb;
1688 flows[num_flows++] = nb;
1689 if (nb.t == NEIGHBOR_LOWER)
1690 flowing_down = true;
1697 decide on the type (and possibly level) of the current node
1699 content_t new_node_content;
1700 s8 new_node_level = -1;
1701 s8 max_node_level = -1;
1702 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1703 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1704 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1705 // it's perfectly safe to use liquid_kind here to determine the new node content.
1706 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1707 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1708 // liquid_kind is set properly, see above
1709 new_node_content = liquid_kind;
1710 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1712 // no surrounding sources, so get the maximum level that can flow into this node
1713 for (u16 i = 0; i < num_flows; i++) {
1714 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1715 switch (flows[i].t) {
1716 case NEIGHBOR_UPPER:
1717 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1718 max_node_level = LIQUID_LEVEL_MAX;
1719 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1720 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1721 } else if (nb_liquid_level > max_node_level)
1722 max_node_level = nb_liquid_level;
1724 case NEIGHBOR_LOWER:
1726 case NEIGHBOR_SAME_LEVEL:
1727 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1728 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1729 max_node_level = nb_liquid_level - 1;
1735 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1736 if (viscosity > 1 && max_node_level != liquid_level) {
1737 // amount to gain, limited by viscosity
1738 // must be at least 1 in absolute value
1739 s8 level_inc = max_node_level - liquid_level;
1740 if (level_inc < -viscosity || level_inc > viscosity)
1741 new_node_level = liquid_level + level_inc/viscosity;
1742 else if (level_inc < 0)
1743 new_node_level = liquid_level - 1;
1744 else if (level_inc > 0)
1745 new_node_level = liquid_level + 1;
1746 if (new_node_level != max_node_level)
1747 must_reflow.push_back(p0);
1749 new_node_level = max_node_level;
1751 if (new_node_level >= 0)
1752 new_node_content = liquid_kind;
1754 new_node_content = CONTENT_AIR;
1759 check if anything has changed. if not, just continue with the next node.
1761 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1762 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1763 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1769 update the current node
1771 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1772 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1773 // set level to last 3 bits, flowing down bit to 4th bit
1774 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1776 // set the liquid level and flow bit to 0
1777 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1779 n0.setContent(new_node_content);
1781 v3s16 blockpos = getNodeBlockPos(p0);
1782 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1784 modified_blocks.insert(blockpos, block);
1785 // If node emits light, MapBlock requires lighting update
1786 if(content_features(n0).light_source != 0)
1787 lighting_modified_blocks[block->getPos()] = block;
1791 enqueue neighbors for update if neccessary
1793 switch (content_features(n0.getContent()).liquid_type) {
1795 case LIQUID_FLOWING:
1796 // make sure source flows into all neighboring nodes
1797 for (u16 i = 0; i < num_flows; i++)
1798 if (flows[i].t != NEIGHBOR_UPPER)
1799 m_transforming_liquid.push_back(flows[i].p);
1800 for (u16 i = 0; i < num_airs; i++)
1801 if (airs[i].t != NEIGHBOR_UPPER)
1802 m_transforming_liquid.push_back(airs[i].p);
1805 // this flow has turned to air; neighboring flows might need to do the same
1806 for (u16 i = 0; i < num_flows; i++)
1807 m_transforming_liquid.push_back(flows[i].p);
1811 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1812 while (must_reflow.size() > 0)
1813 m_transforming_liquid.push_back(must_reflow.pop_front());
1814 updateLighting(lighting_modified_blocks, modified_blocks);
1817 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1819 v3s16 blockpos = getNodeBlockPos(p);
1820 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1821 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1824 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1828 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1832 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1834 v3s16 blockpos = getNodeBlockPos(p);
1835 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1836 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1839 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1843 block->m_node_metadata.set(p_rel, meta);
1846 void Map::removeNodeMetadata(v3s16 p)
1848 v3s16 blockpos = getNodeBlockPos(p);
1849 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1850 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1853 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1857 block->m_node_metadata.remove(p_rel);
1860 void Map::nodeMetadataStep(float dtime,
1861 core::map<v3s16, MapBlock*> &changed_blocks)
1865 Currently there is no way to ensure that all the necessary
1866 blocks are loaded when this is run. (They might get unloaded)
1867 NOTE: ^- Actually, that might not be so. In a quick test it
1868 reloaded a block with a furnace when I walked back to it from
1871 core::map<v2s16, MapSector*>::Iterator si;
1872 si = m_sectors.getIterator();
1873 for(; si.atEnd() == false; si++)
1875 MapSector *sector = si.getNode()->getValue();
1876 core::list< MapBlock * > sectorblocks;
1877 sector->getBlocks(sectorblocks);
1878 core::list< MapBlock * >::Iterator i;
1879 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1881 MapBlock *block = *i;
1882 bool changed = block->m_node_metadata.step(dtime);
1884 changed_blocks[block->getPos()] = block;
1893 ServerMap::ServerMap(std::string savedir):
1896 m_map_metadata_changed(true),
1898 m_database_read(NULL),
1899 m_database_write(NULL)
1901 infostream<<__FUNCTION_NAME<<std::endl;
1903 //m_chunksize = 8; // Takes a few seconds
1905 if (g_settings->get("fixed_map_seed").empty())
1907 m_seed = (((u64)(myrand()%0xffff)<<0)
1908 + ((u64)(myrand()%0xffff)<<16)
1909 + ((u64)(myrand()%0xffff)<<32)
1910 + ((u64)(myrand()%0xffff)<<48));
1914 m_seed = g_settings->getU64("fixed_map_seed");
1918 Experimental and debug stuff
1925 Try to load map; if not found, create a new one.
1928 m_savedir = savedir;
1929 m_map_saving_enabled = false;
1933 // If directory exists, check contents and load if possible
1934 if(fs::PathExists(m_savedir))
1936 // If directory is empty, it is safe to save into it.
1937 if(fs::GetDirListing(m_savedir).size() == 0)
1939 infostream<<"Server: Empty save directory is valid."
1941 m_map_saving_enabled = true;
1946 // Load map metadata (seed, chunksize)
1949 catch(FileNotGoodException &e){
1950 infostream<<"WARNING: Could not load map metadata"
1951 //<<" Disabling chunk-based generator."
1957 // Load chunk metadata
1960 catch(FileNotGoodException &e){
1961 infostream<<"WARNING: Could not load chunk metadata."
1962 <<" Disabling chunk-based generator."
1967 /*infostream<<"Server: Successfully loaded chunk "
1968 "metadata and sector (0,0) from "<<savedir<<
1969 ", assuming valid save directory."
1972 infostream<<"Server: Successfully loaded map "
1973 <<"and chunk metadata from "<<savedir
1974 <<", assuming valid save directory."
1977 m_map_saving_enabled = true;
1978 // Map loaded, not creating new one
1982 // If directory doesn't exist, it is safe to save to it
1984 m_map_saving_enabled = true;
1987 catch(std::exception &e)
1989 infostream<<"WARNING: Server: Failed to load map from "<<savedir
1990 <<", exception: "<<e.what()<<std::endl;
1991 infostream<<"Please remove the map or fix it."<<std::endl;
1992 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
1995 infostream<<"Initializing new map."<<std::endl;
1997 // Create zero sector
1998 emergeSector(v2s16(0,0));
2000 // Initially write whole map
2004 ServerMap::~ServerMap()
2006 infostream<<__FUNCTION_NAME<<std::endl;
2010 if(m_map_saving_enabled)
2012 // Save only changed parts
2014 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2018 infostream<<"Server: map not saved"<<std::endl;
2021 catch(std::exception &e)
2023 infostream<<"Server: Failed to save map to "<<m_savedir
2024 <<", exception: "<<e.what()<<std::endl;
2028 Close database if it was opened
2031 sqlite3_finalize(m_database_read);
2032 if(m_database_write)
2033 sqlite3_finalize(m_database_write);
2035 sqlite3_close(m_database);
2041 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2042 for(; i.atEnd() == false; i++)
2044 MapChunk *chunk = i.getNode()->getValue();
2050 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2052 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2053 if(enable_mapgen_debug_info)
2054 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2055 <<blockpos.Z<<")"<<std::endl;
2057 // Do nothing if not inside limits (+-1 because of neighbors)
2058 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2059 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2065 data->no_op = false;
2066 data->seed = m_seed;
2067 data->blockpos = blockpos;
2070 Create the whole area of this and the neighboring blocks
2073 //TimeTaker timer("initBlockMake() create area");
2075 for(s16 x=-1; x<=1; x++)
2076 for(s16 z=-1; z<=1; z++)
2078 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2079 // Sector metadata is loaded from disk if not already loaded.
2080 ServerMapSector *sector = createSector(sectorpos);
2083 for(s16 y=-1; y<=1; y++)
2085 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2086 //MapBlock *block = createBlock(p);
2087 // 1) get from memory, 2) load from disk
2088 MapBlock *block = emergeBlock(p, false);
2089 // 3) create a blank one
2092 block = createBlock(p);
2095 Block gets sunlight if this is true.
2097 Refer to the map generator heuristics.
2099 bool ug = mapgen::block_is_underground(data->seed, p);
2100 block->setIsUnderground(ug);
2103 // Lighting will not be valid after make_chunk is called
2104 block->setLightingExpired(true);
2105 // Lighting will be calculated
2106 //block->setLightingExpired(false);
2112 Now we have a big empty area.
2114 Make a ManualMapVoxelManipulator that contains this and the
2118 // The area that contains this block and it's neighbors
2119 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2120 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2122 data->vmanip = new ManualMapVoxelManipulator(this);
2123 //data->vmanip->setMap(this);
2127 //TimeTaker timer("initBlockMake() initialEmerge");
2128 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2131 // Data is ready now.
2134 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2135 core::map<v3s16, MapBlock*> &changed_blocks)
2137 v3s16 blockpos = data->blockpos;
2138 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2139 <<blockpos.Z<<")"<<std::endl;*/
2143 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2147 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2149 /*infostream<<"Resulting vmanip:"<<std::endl;
2150 data->vmanip.print(infostream);*/
2153 Blit generated stuff to map
2154 NOTE: blitBackAll adds nearly everything to changed_blocks
2158 //TimeTaker timer("finishBlockMake() blitBackAll");
2159 data->vmanip->blitBackAll(&changed_blocks);
2162 if(enable_mapgen_debug_info)
2163 infostream<<"finishBlockMake: changed_blocks.size()="
2164 <<changed_blocks.size()<<std::endl;
2167 Copy transforming liquid information
2169 while(data->transforming_liquid.size() > 0)
2171 v3s16 p = data->transforming_liquid.pop_front();
2172 m_transforming_liquid.push_back(p);
2178 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2182 Set is_underground flag for lighting with sunlight.
2184 Refer to map generator heuristics.
2186 NOTE: This is done in initChunkMake
2188 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2192 Add sunlight to central block.
2193 This makes in-dark-spawning monsters to not flood the whole thing.
2194 Do not spread the light, though.
2196 /*core::map<v3s16, bool> light_sources;
2197 bool black_air_left = false;
2198 block->propagateSunlight(light_sources, true, &black_air_left);*/
2201 NOTE: Lighting and object adding shouldn't really be here, but
2202 lighting is a bit tricky to move properly to makeBlock.
2203 TODO: Do this the right way anyway, that is, move it to makeBlock.
2204 - There needs to be some way for makeBlock to report back if
2205 the lighting update is going further down because of the
2206 new block blocking light
2211 NOTE: This takes ~60ms, TODO: Investigate why
2214 TimeTaker t("finishBlockMake lighting update");
2216 core::map<v3s16, MapBlock*> lighting_update_blocks;
2219 lighting_update_blocks.insert(block->getPos(), block);
2224 v3s16 p = block->getPos()+v3s16(x,1,z);
2225 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2229 // All modified blocks
2230 // NOTE: Should this be done? If this is not done, then the lighting
2231 // of the others will be updated in a different place, one by one, i
2232 // think... or they might not? Well, at least they are left marked as
2233 // "lighting expired"; it seems that is not handled at all anywhere,
2234 // so enabling this will slow it down A LOT because otherwise it
2235 // would not do this at all. This causes the black trees.
2236 for(core::map<v3s16, MapBlock*>::Iterator
2237 i = changed_blocks.getIterator();
2238 i.atEnd() == false; i++)
2240 lighting_update_blocks.insert(i.getNode()->getKey(),
2241 i.getNode()->getValue());
2243 /*// Also force-add all the upmost blocks for proper sunlight
2244 for(s16 x=-1; x<=1; x++)
2245 for(s16 z=-1; z<=1; z++)
2247 v3s16 p = block->getPos()+v3s16(x,1,z);
2248 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2251 updateLighting(lighting_update_blocks, changed_blocks);
2254 Set lighting to non-expired state in all of them.
2255 This is cheating, but it is not fast enough if all of them
2256 would actually be updated.
2258 for(s16 x=-1; x<=1; x++)
2259 for(s16 y=-1; y<=1; y++)
2260 for(s16 z=-1; z<=1; z++)
2262 v3s16 p = block->getPos()+v3s16(x,y,z);
2263 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2266 if(enable_mapgen_debug_info == false)
2267 t.stop(true); // Hide output
2271 Add random objects to block
2273 mapgen::add_random_objects(block);
2276 Go through changed blocks
2278 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2279 i.atEnd() == false; i++)
2281 MapBlock *block = i.getNode()->getValue();
2284 Update day/night difference cache of the MapBlocks
2286 block->updateDayNightDiff();
2288 Set block as modified
2290 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2294 Set central block as generated
2296 block->setGenerated(true);
2299 Save changed parts of map
2300 NOTE: Will be saved later.
2304 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2305 <<blockpos.Z<<")"<<std::endl;*/
2307 if(enable_mapgen_debug_info)
2310 Analyze resulting blocks
2312 for(s16 x=-1; x<=1; x++)
2313 for(s16 y=-1; y<=1; y++)
2314 for(s16 z=-1; z<=1; z++)
2316 v3s16 p = block->getPos()+v3s16(x,y,z);
2317 MapBlock *block = getBlockNoCreateNoEx(p);
2319 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2320 infostream<<"Generated "<<spos<<": "
2321 <<analyze_block(block)<<std::endl;
2329 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2331 DSTACKF("%s: p2d=(%d,%d)",
2336 Check if it exists already in memory
2338 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2343 Try to load it from disk (with blocks)
2345 //if(loadSectorFull(p2d) == true)
2348 Try to load metadata from disk
2351 if(loadSectorMeta(p2d) == true)
2353 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2356 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2357 throw InvalidPositionException("");
2363 Do not create over-limit
2365 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2366 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2367 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2368 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2369 throw InvalidPositionException("createSector(): pos. over limit");
2372 Generate blank sector
2375 sector = new ServerMapSector(this, p2d);
2377 // Sector position on map in nodes
2378 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2383 m_sectors.insert(p2d, sector);
2389 This is a quick-hand function for calling makeBlock().
2391 MapBlock * ServerMap::generateBlock(
2393 core::map<v3s16, MapBlock*> &modified_blocks
2396 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2398 /*infostream<<"generateBlock(): "
2399 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2402 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2404 TimeTaker timer("generateBlock");
2406 //MapBlock *block = original_dummy;
2408 v2s16 p2d(p.X, p.Z);
2409 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2412 Do not generate over-limit
2414 if(blockpos_over_limit(p))
2416 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2417 throw InvalidPositionException("generateBlock(): pos. over limit");
2421 Create block make data
2423 mapgen::BlockMakeData data;
2424 initBlockMake(&data, p);
2430 TimeTaker t("mapgen::make_block()");
2431 mapgen::make_block(&data);
2433 if(enable_mapgen_debug_info == false)
2434 t.stop(true); // Hide output
2438 Blit data back on map, update lighting, add mobs and whatever this does
2440 finishBlockMake(&data, modified_blocks);
2445 MapBlock *block = getBlockNoCreateNoEx(p);
2453 bool erroneus_content = false;
2454 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2455 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2456 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2459 MapNode n = block->getNode(p);
2460 if(n.getContent() == CONTENT_IGNORE)
2462 infostream<<"CONTENT_IGNORE at "
2463 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2465 erroneus_content = true;
2469 if(erroneus_content)
2478 Generate a completely empty block
2482 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2483 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2485 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2489 n.setContent(CONTENT_AIR);
2491 n.setContent(CONTENT_STONE);
2492 block->setNode(v3s16(x0,y0,z0), n);
2498 if(enable_mapgen_debug_info == false)
2499 timer.stop(true); // Hide output
2504 MapBlock * ServerMap::createBlock(v3s16 p)
2506 DSTACKF("%s: p=(%d,%d,%d)",
2507 __FUNCTION_NAME, p.X, p.Y, p.Z);
2510 Do not create over-limit
2512 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2513 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2514 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2515 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2516 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2517 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2518 throw InvalidPositionException("createBlock(): pos. over limit");
2520 v2s16 p2d(p.X, p.Z);
2523 This will create or load a sector if not found in memory.
2524 If block exists on disk, it will be loaded.
2526 NOTE: On old save formats, this will be slow, as it generates
2527 lighting on blocks for them.
2529 ServerMapSector *sector;
2531 sector = (ServerMapSector*)createSector(p2d);
2532 assert(sector->getId() == MAPSECTOR_SERVER);
2534 catch(InvalidPositionException &e)
2536 infostream<<"createBlock: createSector() failed"<<std::endl;
2540 NOTE: This should not be done, or at least the exception
2541 should not be passed on as std::exception, because it
2542 won't be catched at all.
2544 /*catch(std::exception &e)
2546 infostream<<"createBlock: createSector() failed: "
2547 <<e.what()<<std::endl;
2552 Try to get a block from the sector
2555 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2558 if(block->isDummy())
2563 block = sector->createBlankBlock(block_y);
2567 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2569 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2571 p.X, p.Y, p.Z, allow_generate);
2574 MapBlock *block = getBlockNoCreateNoEx(p);
2575 if(block && block->isDummy() == false)
2580 MapBlock *block = loadBlock(p);
2587 core::map<v3s16, MapBlock*> modified_blocks;
2588 MapBlock *block = generateBlock(p, modified_blocks);
2592 event.type = MEET_OTHER;
2595 // Copy modified_blocks to event
2596 for(core::map<v3s16, MapBlock*>::Iterator
2597 i = modified_blocks.getIterator();
2598 i.atEnd()==false; i++)
2600 event.modified_blocks.insert(i.getNode()->getKey(), false);
2604 dispatchEvent(&event);
2615 Do not generate over-limit
2617 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2618 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2619 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2620 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2621 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2622 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2623 throw InvalidPositionException("emergeBlock(): pos. over limit");
2625 v2s16 p2d(p.X, p.Z);
2628 This will create or load a sector if not found in memory.
2629 If block exists on disk, it will be loaded.
2631 ServerMapSector *sector;
2633 sector = createSector(p2d);
2634 //sector = emergeSector(p2d, changed_blocks);
2636 catch(InvalidPositionException &e)
2638 infostream<<"emergeBlock: createSector() failed: "
2639 <<e.what()<<std::endl;
2640 infostream<<"Path to failed sector: "<<getSectorDir(p2d)
2642 <<"You could try to delete it."<<std::endl;
2645 catch(VersionMismatchException &e)
2647 infostream<<"emergeBlock: createSector() failed: "
2648 <<e.what()<<std::endl;
2649 infostream<<"Path to failed sector: "<<getSectorDir(p2d)
2651 <<"You could try to delete it."<<std::endl;
2656 Try to get a block from the sector
2659 bool does_not_exist = false;
2660 bool lighting_expired = false;
2661 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2663 // If not found, try loading from disk
2666 block = loadBlock(p);
2672 does_not_exist = true;
2674 else if(block->isDummy() == true)
2676 does_not_exist = true;
2678 else if(block->getLightingExpired())
2680 lighting_expired = true;
2685 //infostream<<"emergeBlock(): Returning already valid block"<<std::endl;
2690 If block was not found on disk and not going to generate a
2691 new one, make sure there is a dummy block in place.
2693 if(only_from_disk && (does_not_exist || lighting_expired))
2695 //infostream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2699 // Create dummy block
2700 block = new MapBlock(this, p, true);
2702 // Add block to sector
2703 sector->insertBlock(block);
2709 //infostream<<"Not found on disk, generating."<<std::endl;
2711 //TimeTaker("emergeBlock() generate");
2713 //infostream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2716 If the block doesn't exist, generate the block.
2720 block = generateBlock(p, block, sector, changed_blocks,
2721 lighting_invalidated_blocks);
2724 if(lighting_expired)
2726 lighting_invalidated_blocks.insert(p, block);
2731 Initially update sunlight
2734 core::map<v3s16, bool> light_sources;
2735 bool black_air_left = false;
2736 bool bottom_invalid =
2737 block->propagateSunlight(light_sources, true,
2740 // If sunlight didn't reach everywhere and part of block is
2741 // above ground, lighting has to be properly updated
2742 //if(black_air_left && some_part_underground)
2745 lighting_invalidated_blocks[block->getPos()] = block;
2750 lighting_invalidated_blocks[block->getPos()] = block;
2759 s16 ServerMap::findGroundLevel(v2s16 p2d)
2763 Uh, just do something random...
2765 // Find existing map from top to down
2768 v3s16 p(p2d.X, max, p2d.Y);
2769 for(; p.Y>min; p.Y--)
2771 MapNode n = getNodeNoEx(p);
2772 if(n.getContent() != CONTENT_IGNORE)
2777 // If this node is not air, go to plan b
2778 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2780 // Search existing walkable and return it
2781 for(; p.Y>min; p.Y--)
2783 MapNode n = getNodeNoEx(p);
2784 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2793 Determine from map generator noise functions
2796 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2799 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2800 //return (s16)level;
2803 void ServerMap::createDatabase() {
2806 e = sqlite3_exec(m_database,
2807 "CREATE TABLE IF NOT EXISTS `blocks` ("
2808 "`pos` INT NOT NULL PRIMARY KEY,"
2811 , NULL, NULL, NULL);
2812 if(e == SQLITE_ABORT)
2813 throw FileNotGoodException("Could not create database structure");
2815 infostream<<"Server: Database structure was created";
2818 void ServerMap::verifyDatabase() {
2823 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2824 bool needs_create = false;
2828 Open the database connection
2831 createDirs(m_savedir);
2833 if(!fs::PathExists(dbp))
2834 needs_create = true;
2836 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2837 if(d != SQLITE_OK) {
2838 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2839 throw FileNotGoodException("Cannot open database file");
2845 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2846 if(d != SQLITE_OK) {
2847 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2848 throw FileNotGoodException("Cannot prepare read statement");
2851 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2852 if(d != SQLITE_OK) {
2853 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2854 throw FileNotGoodException("Cannot prepare write statement");
2857 infostream<<"Server: Database opened"<<std::endl;
2861 bool ServerMap::loadFromFolders() {
2862 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2867 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2868 return (sqlite3_int64)pos.Z*16777216 +
2869 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2872 void ServerMap::createDirs(std::string path)
2874 if(fs::CreateAllDirs(path) == false)
2876 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2877 <<"\""<<path<<"\""<<std::endl;
2878 throw BaseException("ServerMap failed to create directory");
2882 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2888 snprintf(cc, 9, "%.4x%.4x",
2889 (unsigned int)pos.X&0xffff,
2890 (unsigned int)pos.Y&0xffff);
2892 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2894 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2895 (unsigned int)pos.X&0xfff,
2896 (unsigned int)pos.Y&0xfff);
2898 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2904 v2s16 ServerMap::getSectorPos(std::string dirname)
2908 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2909 assert(spos != std::string::npos);
2910 if(dirname.size() - spos == 8)
2913 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2915 else if(dirname.size() - spos == 3)
2918 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2919 // Sign-extend the 12 bit values up to 16 bits...
2920 if(x&0x800) x|=0xF000;
2921 if(y&0x800) y|=0xF000;
2928 v2s16 pos((s16)x, (s16)y);
2932 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2934 v2s16 p2d = getSectorPos(sectordir);
2936 if(blockfile.size() != 4){
2937 throw InvalidFilenameException("Invalid block filename");
2940 int r = sscanf(blockfile.c_str(), "%4x", &y);
2942 throw InvalidFilenameException("Invalid block filename");
2943 return v3s16(p2d.X, y, p2d.Y);
2946 std::string ServerMap::getBlockFilename(v3s16 p)
2949 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2953 void ServerMap::save(bool only_changed)
2955 DSTACK(__FUNCTION_NAME);
2956 if(m_map_saving_enabled == false)
2958 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2962 if(only_changed == false)
2963 infostream<<"ServerMap: Saving whole map, this can take time."
2966 if(only_changed == false || m_map_metadata_changed)
2971 u32 sector_meta_count = 0;
2972 u32 block_count = 0;
2973 u32 block_count_all = 0; // Number of blocks in memory
2976 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2977 for(; i.atEnd() == false; i++)
2979 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2980 assert(sector->getId() == MAPSECTOR_SERVER);
2982 if(sector->differs_from_disk || only_changed == false)
2984 saveSectorMeta(sector);
2985 sector_meta_count++;
2987 core::list<MapBlock*> blocks;
2988 sector->getBlocks(blocks);
2989 core::list<MapBlock*>::Iterator j;
2991 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2992 for(j=blocks.begin(); j!=blocks.end(); j++)
2994 MapBlock *block = *j;
2998 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2999 || only_changed == false)
3004 /*infostream<<"ServerMap: Written block ("
3005 <<block->getPos().X<<","
3006 <<block->getPos().Y<<","
3007 <<block->getPos().Z<<")"
3010 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3016 Only print if something happened or saved whole map
3018 if(only_changed == false || sector_meta_count != 0
3019 || block_count != 0)
3021 infostream<<"ServerMap: Written: "
3022 <<sector_meta_count<<" sector metadata files, "
3023 <<block_count<<" block files"
3024 <<", "<<block_count_all<<" blocks in memory."
3029 void ServerMap::saveMapMeta()
3031 DSTACK(__FUNCTION_NAME);
3033 infostream<<"ServerMap::saveMapMeta(): "
3037 createDirs(m_savedir);
3039 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3040 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3041 if(os.good() == false)
3043 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3044 <<"could not open"<<fullpath<<std::endl;
3045 throw FileNotGoodException("Cannot open chunk metadata");
3049 params.setU64("seed", m_seed);
3051 params.writeLines(os);
3053 os<<"[end_of_params]\n";
3055 m_map_metadata_changed = false;
3058 void ServerMap::loadMapMeta()
3060 DSTACK(__FUNCTION_NAME);
3062 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3065 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3066 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3067 if(is.good() == false)
3069 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3070 <<"could not open"<<fullpath<<std::endl;
3071 throw FileNotGoodException("Cannot open map metadata");
3079 throw SerializationError
3080 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3082 std::getline(is, line);
3083 std::string trimmedline = trim(line);
3084 if(trimmedline == "[end_of_params]")
3086 params.parseConfigLine(line);
3089 m_seed = params.getU64("seed");
3091 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3094 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3096 DSTACK(__FUNCTION_NAME);
3097 // Format used for writing
3098 u8 version = SER_FMT_VER_HIGHEST;
3100 v2s16 pos = sector->getPos();
3101 std::string dir = getSectorDir(pos);
3104 std::string fullpath = dir + DIR_DELIM + "meta";
3105 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3106 if(o.good() == false)
3107 throw FileNotGoodException("Cannot open sector metafile");
3109 sector->serialize(o, version);
3111 sector->differs_from_disk = false;
3114 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3116 DSTACK(__FUNCTION_NAME);
3118 v2s16 p2d = getSectorPos(sectordir);
3120 ServerMapSector *sector = NULL;
3122 std::string fullpath = sectordir + DIR_DELIM + "meta";
3123 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3124 if(is.good() == false)
3126 // If the directory exists anyway, it probably is in some old
3127 // format. Just go ahead and create the sector.
3128 if(fs::PathExists(sectordir))
3130 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3131 <<fullpath<<" doesn't exist but directory does."
3132 <<" Continuing with a sector with no metadata."
3134 sector = new ServerMapSector(this, p2d);
3135 m_sectors.insert(p2d, sector);
3139 throw FileNotGoodException("Cannot open sector metafile");
3144 sector = ServerMapSector::deSerialize
3145 (is, this, p2d, m_sectors);
3147 saveSectorMeta(sector);
3150 sector->differs_from_disk = false;
3155 bool ServerMap::loadSectorMeta(v2s16 p2d)
3157 DSTACK(__FUNCTION_NAME);
3159 MapSector *sector = NULL;
3161 // The directory layout we're going to load from.
3162 // 1 - original sectors/xxxxzzzz/
3163 // 2 - new sectors2/xxx/zzz/
3164 // If we load from anything but the latest structure, we will
3165 // immediately save to the new one, and remove the old.
3167 std::string sectordir1 = getSectorDir(p2d, 1);
3168 std::string sectordir;
3169 if(fs::PathExists(sectordir1))
3171 sectordir = sectordir1;
3176 sectordir = getSectorDir(p2d, 2);
3180 sector = loadSectorMeta(sectordir, loadlayout != 2);
3182 catch(InvalidFilenameException &e)
3186 catch(FileNotGoodException &e)
3190 catch(std::exception &e)
3199 bool ServerMap::loadSectorFull(v2s16 p2d)
3201 DSTACK(__FUNCTION_NAME);
3203 MapSector *sector = NULL;
3205 // The directory layout we're going to load from.
3206 // 1 - original sectors/xxxxzzzz/
3207 // 2 - new sectors2/xxx/zzz/
3208 // If we load from anything but the latest structure, we will
3209 // immediately save to the new one, and remove the old.
3211 std::string sectordir1 = getSectorDir(p2d, 1);
3212 std::string sectordir;
3213 if(fs::PathExists(sectordir1))
3215 sectordir = sectordir1;
3220 sectordir = getSectorDir(p2d, 2);
3224 sector = loadSectorMeta(sectordir, loadlayout != 2);
3226 catch(InvalidFilenameException &e)
3230 catch(FileNotGoodException &e)
3234 catch(std::exception &e)
3242 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3244 std::vector<fs::DirListNode>::iterator i2;
3245 for(i2=list2.begin(); i2!=list2.end(); i2++)
3251 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3253 catch(InvalidFilenameException &e)
3255 // This catches unknown crap in directory
3261 infostream<<"Sector converted to new layout - deleting "<<
3262 sectordir1<<std::endl;
3263 fs::RecursiveDelete(sectordir1);
3270 void ServerMap::beginSave() {
3272 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3273 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3276 void ServerMap::endSave() {
3278 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3279 infostream<<"WARNING: endSave() failed, map might not have saved.";
3282 void ServerMap::saveBlock(MapBlock *block)
3284 DSTACK(__FUNCTION_NAME);
3286 Dummy blocks are not written
3288 if(block->isDummy())
3290 /*v3s16 p = block->getPos();
3291 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3292 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3296 // Format used for writing
3297 u8 version = SER_FMT_VER_HIGHEST;
3299 v3s16 p3d = block->getPos();
3303 v2s16 p2d(p3d.X, p3d.Z);
3304 std::string sectordir = getSectorDir(p2d);
3306 createDirs(sectordir);
3308 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3309 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3310 if(o.good() == false)
3311 throw FileNotGoodException("Cannot open block data");
3314 [0] u8 serialization version
3320 std::ostringstream o(std::ios_base::binary);
3322 o.write((char*)&version, 1);
3325 block->serialize(o, version);
3327 // Write extra data stored on disk
3328 block->serializeDiskExtra(o, version);
3330 // Write block to database
3332 std::string tmp = o.str();
3333 const char *bytes = tmp.c_str();
3335 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3336 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3337 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3338 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3339 int written = sqlite3_step(m_database_write);
3340 if(written != SQLITE_DONE)
3341 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3342 <<sqlite3_errmsg(m_database)<<std::endl;
3343 // Make ready for later reuse
3344 sqlite3_reset(m_database_write);
3346 // We just wrote it to the disk so clear modified flag
3347 block->resetModified();
3350 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3352 DSTACK(__FUNCTION_NAME);
3354 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3357 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3358 if(is.good() == false)
3359 throw FileNotGoodException("Cannot open block file");
3361 v3s16 p3d = getBlockPos(sectordir, blockfile);
3362 v2s16 p2d(p3d.X, p3d.Z);
3364 assert(sector->getPos() == p2d);
3366 u8 version = SER_FMT_VER_INVALID;
3367 is.read((char*)&version, 1);
3370 throw SerializationError("ServerMap::loadBlock(): Failed"
3371 " to read MapBlock version");
3373 /*u32 block_size = MapBlock::serializedLength(version);
3374 SharedBuffer<u8> data(block_size);
3375 is.read((char*)*data, block_size);*/
3377 // This will always return a sector because we're the server
3378 //MapSector *sector = emergeSector(p2d);
3380 MapBlock *block = NULL;
3381 bool created_new = false;
3382 block = sector->getBlockNoCreateNoEx(p3d.Y);
3385 block = sector->createBlankBlockNoInsert(p3d.Y);
3390 block->deSerialize(is, version);
3392 // Read extra data stored on disk
3393 block->deSerializeDiskExtra(is, version);
3395 // If it's a new block, insert it to the map
3397 sector->insertBlock(block);
3400 Save blocks loaded in old format in new format
3403 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3407 // Should be in database now, so delete the old file
3408 fs::RecursiveDelete(fullpath);
3411 // We just loaded it from the disk, so it's up-to-date.
3412 block->resetModified();
3415 catch(SerializationError &e)
3417 infostream<<"WARNING: Invalid block data on disk "
3418 <<"fullpath="<<fullpath
3419 <<" (SerializationError). "
3420 <<"what()="<<e.what()
3422 //" Ignoring. A new one will be generated.
3425 // TODO: Backup file; name is in fullpath.
3429 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3431 DSTACK(__FUNCTION_NAME);
3434 std::istringstream is(*blob, std::ios_base::binary);
3436 u8 version = SER_FMT_VER_INVALID;
3437 is.read((char*)&version, 1);
3440 throw SerializationError("ServerMap::loadBlock(): Failed"
3441 " to read MapBlock version");
3443 /*u32 block_size = MapBlock::serializedLength(version);
3444 SharedBuffer<u8> data(block_size);
3445 is.read((char*)*data, block_size);*/
3447 // This will always return a sector because we're the server
3448 //MapSector *sector = emergeSector(p2d);
3450 MapBlock *block = NULL;
3451 bool created_new = false;
3452 block = sector->getBlockNoCreateNoEx(p3d.Y);
3455 block = sector->createBlankBlockNoInsert(p3d.Y);
3460 block->deSerialize(is, version);
3462 // Read extra data stored on disk
3463 block->deSerializeDiskExtra(is, version);
3465 // If it's a new block, insert it to the map
3467 sector->insertBlock(block);
3470 Save blocks loaded in old format in new format
3473 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3478 // We just loaded it from, so it's up-to-date.
3479 block->resetModified();
3482 catch(SerializationError &e)
3484 infostream<<"WARNING: Invalid block data in database "
3485 <<" (SerializationError). "
3486 <<"what()="<<e.what()
3488 //" Ignoring. A new one will be generated.
3491 // TODO: Copy to a backup database.
3495 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3497 DSTACK(__FUNCTION_NAME);
3499 v2s16 p2d(blockpos.X, blockpos.Z);
3501 if(!loadFromFolders()) {
3504 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3505 infostream<<"WARNING: Could not bind block position for load: "
3506 <<sqlite3_errmsg(m_database)<<std::endl;
3507 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3509 Make sure sector is loaded
3511 MapSector *sector = createSector(p2d);
3516 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3517 size_t len = sqlite3_column_bytes(m_database_read, 0);
3519 std::string datastr(data, len);
3521 loadBlock(&datastr, blockpos, sector, false);
3523 sqlite3_step(m_database_read);
3524 // We should never get more than 1 row, so ok to reset
3525 sqlite3_reset(m_database_read);
3527 return getBlockNoCreateNoEx(blockpos);
3529 sqlite3_reset(m_database_read);
3531 // Not found in database, try the files
3534 // The directory layout we're going to load from.
3535 // 1 - original sectors/xxxxzzzz/
3536 // 2 - new sectors2/xxx/zzz/
3537 // If we load from anything but the latest structure, we will
3538 // immediately save to the new one, and remove the old.
3540 std::string sectordir1 = getSectorDir(p2d, 1);
3541 std::string sectordir;
3542 if(fs::PathExists(sectordir1))
3544 sectordir = sectordir1;
3549 sectordir = getSectorDir(p2d, 2);
3553 Make sure sector is loaded
3555 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3559 sector = loadSectorMeta(sectordir, loadlayout != 2);
3561 catch(InvalidFilenameException &e)
3565 catch(FileNotGoodException &e)
3569 catch(std::exception &e)
3576 Make sure file exists
3579 std::string blockfilename = getBlockFilename(blockpos);
3580 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3584 Load block and save it to the database
3586 loadBlock(sectordir, blockfilename, sector, true);
3587 return getBlockNoCreateNoEx(blockpos);
3590 void ServerMap::PrintInfo(std::ostream &out)
3601 ClientMap::ClientMap(
3603 MapDrawControl &control,
3604 scene::ISceneNode* parent,
3605 scene::ISceneManager* mgr,
3609 scene::ISceneNode(parent, mgr, id),
3612 m_camera_position(0,0,0),
3613 m_camera_direction(0,0,1),
3616 m_camera_mutex.Init();
3617 assert(m_camera_mutex.IsInitialized());
3619 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3620 BS*1000000,BS*1000000,BS*1000000);
3623 ClientMap::~ClientMap()
3625 /*JMutexAutoLock lock(mesh_mutex);
3634 MapSector * ClientMap::emergeSector(v2s16 p2d)
3636 DSTACK(__FUNCTION_NAME);
3637 // Check that it doesn't exist already
3639 return getSectorNoGenerate(p2d);
3641 catch(InvalidPositionException &e)
3646 ClientMapSector *sector = new ClientMapSector(this, p2d);
3649 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3650 m_sectors.insert(p2d, sector);
3657 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3659 DSTACK(__FUNCTION_NAME);
3660 ClientMapSector *sector = NULL;
3662 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3664 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3668 sector = (ClientMapSector*)n->getValue();
3669 assert(sector->getId() == MAPSECTOR_CLIENT);
3673 sector = new ClientMapSector(this, p2d);
3675 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3676 m_sectors.insert(p2d, sector);
3680 sector->deSerialize(is);
3684 void ClientMap::OnRegisterSceneNode()
3688 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3689 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3692 ISceneNode::OnRegisterSceneNode();
3695 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3697 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3698 DSTACK(__FUNCTION_NAME);
3700 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3703 This is called two times per frame, reset on the non-transparent one
3705 if(pass == scene::ESNRP_SOLID)
3707 m_last_drawn_sectors.clear();
3711 Get time for measuring timeout.
3713 Measuring time is very useful for long delays when the
3714 machine is swapping a lot.
3716 int time1 = time(0);
3718 //u32 daynight_ratio = m_client->getDayNightRatio();
3720 m_camera_mutex.Lock();
3721 v3f camera_position = m_camera_position;
3722 v3f camera_direction = m_camera_direction;
3723 f32 camera_fov = m_camera_fov;
3724 m_camera_mutex.Unlock();
3727 Get all blocks and draw all visible ones
3730 v3s16 cam_pos_nodes(
3731 camera_position.X / BS,
3732 camera_position.Y / BS,
3733 camera_position.Z / BS);
3735 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3737 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3738 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3740 // Take a fair amount as we will be dropping more out later
3742 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3743 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3744 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3746 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3747 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3748 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3750 u32 vertex_count = 0;
3752 // For limiting number of mesh updates per frame
3753 u32 mesh_update_count = 0;
3755 u32 blocks_would_have_drawn = 0;
3756 u32 blocks_drawn = 0;
3758 int timecheck_counter = 0;
3759 core::map<v2s16, MapSector*>::Iterator si;
3760 si = m_sectors.getIterator();
3761 for(; si.atEnd() == false; si++)
3764 timecheck_counter++;
3765 if(timecheck_counter > 50)
3767 timecheck_counter = 0;
3768 int time2 = time(0);
3769 if(time2 > time1 + 4)
3771 infostream<<"ClientMap::renderMap(): "
3772 "Rendering takes ages, returning."
3779 MapSector *sector = si.getNode()->getValue();
3780 v2s16 sp = sector->getPos();
3782 if(m_control.range_all == false)
3784 if(sp.X < p_blocks_min.X
3785 || sp.X > p_blocks_max.X
3786 || sp.Y < p_blocks_min.Z
3787 || sp.Y > p_blocks_max.Z)
3791 core::list< MapBlock * > sectorblocks;
3792 sector->getBlocks(sectorblocks);
3798 u32 sector_blocks_drawn = 0;
3800 core::list< MapBlock * >::Iterator i;
3801 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3803 MapBlock *block = *i;
3806 Compare block position to camera position, skip
3807 if not seen on display
3810 float range = 100000 * BS;
3811 if(m_control.range_all == false)
3812 range = m_control.wanted_range * BS;
3815 if(isBlockInSight(block->getPos(), camera_position,
3816 camera_direction, camera_fov,
3817 range, &d) == false)
3822 // Okay, this block will be drawn. Reset usage timer.
3823 block->resetUsageTimer();
3825 // This is ugly (spherical distance limit?)
3826 /*if(m_control.range_all == false &&
3827 d - 0.5*BS*MAP_BLOCKSIZE > range)
3832 Update expired mesh (used for day/night change)
3834 It doesn't work exactly like it should now with the
3835 tasked mesh update but whatever.
3838 bool mesh_expired = false;
3841 JMutexAutoLock lock(block->mesh_mutex);
3843 mesh_expired = block->getMeshExpired();
3845 // Mesh has not been expired and there is no mesh:
3846 // block has no content
3847 if(block->mesh == NULL && mesh_expired == false)
3851 f32 faraway = BS*50;
3852 //f32 faraway = m_control.wanted_range * BS;
3855 This has to be done with the mesh_mutex unlocked
3857 // Pretty random but this should work somewhat nicely
3858 if(mesh_expired && (
3859 (mesh_update_count < 3
3860 && (d < faraway || mesh_update_count < 2)
3863 (m_control.range_all && mesh_update_count < 20)
3866 /*if(mesh_expired && mesh_update_count < 6
3867 && (d < faraway || mesh_update_count < 3))*/
3869 mesh_update_count++;
3871 // Mesh has been expired: generate new mesh
3872 //block->updateMesh(daynight_ratio);
3873 m_client->addUpdateMeshTask(block->getPos());
3875 mesh_expired = false;
3880 Draw the faces of the block
3883 JMutexAutoLock lock(block->mesh_mutex);
3885 scene::SMesh *mesh = block->mesh;
3890 blocks_would_have_drawn++;
3891 if(blocks_drawn >= m_control.wanted_max_blocks
3892 && m_control.range_all == false
3893 && d > m_control.wanted_min_range * BS)
3897 sector_blocks_drawn++;
3899 u32 c = mesh->getMeshBufferCount();
3901 for(u32 i=0; i<c; i++)
3903 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3904 const video::SMaterial& material = buf->getMaterial();
3905 video::IMaterialRenderer* rnd =
3906 driver->getMaterialRenderer(material.MaterialType);
3907 bool transparent = (rnd && rnd->isTransparent());
3908 // Render transparent on transparent pass and likewise.
3909 if(transparent == is_transparent_pass)
3912 This *shouldn't* hurt too much because Irrlicht
3913 doesn't change opengl textures if the old
3914 material is set again.
3916 driver->setMaterial(buf->getMaterial());
3917 driver->drawMeshBuffer(buf);
3918 vertex_count += buf->getVertexCount();
3922 } // foreach sectorblocks
3924 if(sector_blocks_drawn != 0)
3926 m_last_drawn_sectors[sp] = true;
3930 m_control.blocks_drawn = blocks_drawn;
3931 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3933 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3934 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3937 void ClientMap::renderPostFx()
3939 // Sadly ISceneManager has no "post effects" render pass, in that case we
3940 // could just register for that and handle it in renderMap().
3942 m_camera_mutex.Lock();
3943 v3f camera_position = m_camera_position;
3944 m_camera_mutex.Unlock();
3946 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
3948 // - If the player is in a solid node, make everything black.
3949 // - If the player is in liquid, draw a semi-transparent overlay.
3950 ContentFeatures& features = content_features(n);
3951 video::SColor post_effect_color = features.post_effect_color;
3952 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
3954 post_effect_color = video::SColor(255, 0, 0, 0);
3956 if (post_effect_color.getAlpha() != 0)
3958 // Draw a full-screen rectangle
3959 video::IVideoDriver* driver = SceneManager->getVideoDriver();
3960 v2u32 ss = driver->getScreenSize();
3961 core::rect<s32> rect(0,0, ss.X, ss.Y);
3962 driver->draw2DRectangle(post_effect_color, rect);
3966 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3967 core::map<v3s16, MapBlock*> *affected_blocks)
3969 bool changed = false;
3971 Add it to all blocks touching it
3974 v3s16(0,0,0), // this
3975 v3s16(0,0,1), // back
3976 v3s16(0,1,0), // top
3977 v3s16(1,0,0), // right
3978 v3s16(0,0,-1), // front
3979 v3s16(0,-1,0), // bottom
3980 v3s16(-1,0,0), // left
3982 for(u16 i=0; i<7; i++)
3984 v3s16 p2 = p + dirs[i];
3985 // Block position of neighbor (or requested) node
3986 v3s16 blockpos = getNodeBlockPos(p2);
3987 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3988 if(blockref == NULL)
3990 // Relative position of requested node
3991 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3992 if(blockref->setTempMod(relpos, mod))
3997 if(changed && affected_blocks!=NULL)
3999 for(u16 i=0; i<7; i++)
4001 v3s16 p2 = p + dirs[i];
4002 // Block position of neighbor (or requested) node
4003 v3s16 blockpos = getNodeBlockPos(p2);
4004 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4005 if(blockref == NULL)
4007 affected_blocks->insert(blockpos, blockref);
4013 bool ClientMap::clearTempMod(v3s16 p,
4014 core::map<v3s16, MapBlock*> *affected_blocks)
4016 bool changed = false;
4018 v3s16(0,0,0), // this
4019 v3s16(0,0,1), // back
4020 v3s16(0,1,0), // top
4021 v3s16(1,0,0), // right
4022 v3s16(0,0,-1), // front
4023 v3s16(0,-1,0), // bottom
4024 v3s16(-1,0,0), // left
4026 for(u16 i=0; i<7; i++)
4028 v3s16 p2 = p + dirs[i];
4029 // Block position of neighbor (or requested) node
4030 v3s16 blockpos = getNodeBlockPos(p2);
4031 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4032 if(blockref == NULL)
4034 // Relative position of requested node
4035 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4036 if(blockref->clearTempMod(relpos))
4041 if(changed && affected_blocks!=NULL)
4043 for(u16 i=0; i<7; i++)
4045 v3s16 p2 = p + dirs[i];
4046 // Block position of neighbor (or requested) node
4047 v3s16 blockpos = getNodeBlockPos(p2);
4048 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4049 if(blockref == NULL)
4051 affected_blocks->insert(blockpos, blockref);
4057 void ClientMap::expireMeshes(bool only_daynight_diffed)
4059 TimeTaker timer("expireMeshes()");
4061 core::map<v2s16, MapSector*>::Iterator si;
4062 si = m_sectors.getIterator();
4063 for(; si.atEnd() == false; si++)
4065 MapSector *sector = si.getNode()->getValue();
4067 core::list< MapBlock * > sectorblocks;
4068 sector->getBlocks(sectorblocks);
4070 core::list< MapBlock * >::Iterator i;
4071 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4073 MapBlock *block = *i;
4075 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4081 JMutexAutoLock lock(block->mesh_mutex);
4082 if(block->mesh != NULL)
4084 /*block->mesh->drop();
4085 block->mesh = NULL;*/
4086 block->setMeshExpired(true);
4093 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4095 assert(mapType() == MAPTYPE_CLIENT);
4098 v3s16 p = blockpos + v3s16(0,0,0);
4099 MapBlock *b = getBlockNoCreate(p);
4100 b->updateMesh(daynight_ratio);
4101 //b->setMeshExpired(true);
4103 catch(InvalidPositionException &e){}
4106 v3s16 p = blockpos + v3s16(-1,0,0);
4107 MapBlock *b = getBlockNoCreate(p);
4108 b->updateMesh(daynight_ratio);
4109 //b->setMeshExpired(true);
4111 catch(InvalidPositionException &e){}
4113 v3s16 p = blockpos + v3s16(0,-1,0);
4114 MapBlock *b = getBlockNoCreate(p);
4115 b->updateMesh(daynight_ratio);
4116 //b->setMeshExpired(true);
4118 catch(InvalidPositionException &e){}
4120 v3s16 p = blockpos + v3s16(0,0,-1);
4121 MapBlock *b = getBlockNoCreate(p);
4122 b->updateMesh(daynight_ratio);
4123 //b->setMeshExpired(true);
4125 catch(InvalidPositionException &e){}
4130 Update mesh of block in which the node is, and if the node is at the
4131 leading edge, update the appropriate leading blocks too.
4133 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4141 v3s16 blockposes[4];
4142 for(u32 i=0; i<4; i++)
4144 v3s16 np = nodepos + dirs[i];
4145 blockposes[i] = getNodeBlockPos(np);
4146 // Don't update mesh of block if it has been done already
4147 bool already_updated = false;
4148 for(u32 j=0; j<i; j++)
4150 if(blockposes[j] == blockposes[i])
4152 already_updated = true;
4159 MapBlock *b = getBlockNoCreate(blockposes[i]);
4160 b->updateMesh(daynight_ratio);
4165 void ClientMap::PrintInfo(std::ostream &out)
4176 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4181 MapVoxelManipulator::~MapVoxelManipulator()
4183 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4187 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4189 TimeTaker timer1("emerge", &emerge_time);
4191 // Units of these are MapBlocks
4192 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4193 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4195 VoxelArea block_area_nodes
4196 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4198 addArea(block_area_nodes);
4200 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4201 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4202 for(s32 x=p_min.X; x<=p_max.X; x++)
4205 core::map<v3s16, bool>::Node *n;
4206 n = m_loaded_blocks.find(p);
4210 bool block_data_inexistent = false;
4213 TimeTaker timer1("emerge load", &emerge_load_time);
4215 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4216 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4218 a.print(infostream);
4219 infostream<<std::endl;*/
4221 MapBlock *block = m_map->getBlockNoCreate(p);
4222 if(block->isDummy())
4223 block_data_inexistent = true;
4225 block->copyTo(*this);
4227 catch(InvalidPositionException &e)
4229 block_data_inexistent = true;
4232 if(block_data_inexistent)
4234 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4235 // Fill with VOXELFLAG_INEXISTENT
4236 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4237 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4239 s32 i = m_area.index(a.MinEdge.X,y,z);
4240 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4244 m_loaded_blocks.insert(p, !block_data_inexistent);
4247 //infostream<<"emerge done"<<std::endl;
4251 SUGG: Add an option to only update eg. water and air nodes.
4252 This will make it interfere less with important stuff if
4255 void MapVoxelManipulator::blitBack
4256 (core::map<v3s16, MapBlock*> & modified_blocks)
4258 if(m_area.getExtent() == v3s16(0,0,0))
4261 //TimeTaker timer1("blitBack");
4263 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4264 <<m_loaded_blocks.size()<<std::endl;*/
4267 Initialize block cache
4269 v3s16 blockpos_last;
4270 MapBlock *block = NULL;
4271 bool block_checked_in_modified = false;
4273 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4274 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4275 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4279 u8 f = m_flags[m_area.index(p)];
4280 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4283 MapNode &n = m_data[m_area.index(p)];
4285 v3s16 blockpos = getNodeBlockPos(p);
4290 if(block == NULL || blockpos != blockpos_last){
4291 block = m_map->getBlockNoCreate(blockpos);
4292 blockpos_last = blockpos;
4293 block_checked_in_modified = false;
4296 // Calculate relative position in block
4297 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4299 // Don't continue if nothing has changed here
4300 if(block->getNode(relpos) == n)
4303 //m_map->setNode(m_area.MinEdge + p, n);
4304 block->setNode(relpos, n);
4307 Make sure block is in modified_blocks
4309 if(block_checked_in_modified == false)
4311 modified_blocks[blockpos] = block;
4312 block_checked_in_modified = true;
4315 catch(InvalidPositionException &e)
4321 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4322 MapVoxelManipulator(map),
4323 m_create_area(false)
4327 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4331 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4333 // Just create the area so that it can be pointed to
4334 VoxelManipulator::emerge(a, caller_id);
4337 void ManualMapVoxelManipulator::initialEmerge(
4338 v3s16 blockpos_min, v3s16 blockpos_max)
4340 TimeTaker timer1("initialEmerge", &emerge_time);
4342 // Units of these are MapBlocks
4343 v3s16 p_min = blockpos_min;
4344 v3s16 p_max = blockpos_max;
4346 VoxelArea block_area_nodes
4347 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4349 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4352 infostream<<"initialEmerge: area: ";
4353 block_area_nodes.print(infostream);
4354 infostream<<" ("<<size_MB<<"MB)";
4355 infostream<<std::endl;
4358 addArea(block_area_nodes);
4360 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4361 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4362 for(s32 x=p_min.X; x<=p_max.X; x++)
4365 core::map<v3s16, bool>::Node *n;
4366 n = m_loaded_blocks.find(p);
4370 bool block_data_inexistent = false;
4373 TimeTaker timer1("emerge load", &emerge_load_time);
4375 MapBlock *block = m_map->getBlockNoCreate(p);
4376 if(block->isDummy())
4377 block_data_inexistent = true;
4379 block->copyTo(*this);
4381 catch(InvalidPositionException &e)
4383 block_data_inexistent = true;
4386 if(block_data_inexistent)
4389 Mark area inexistent
4391 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4392 // Fill with VOXELFLAG_INEXISTENT
4393 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4394 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4396 s32 i = m_area.index(a.MinEdge.X,y,z);
4397 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4401 m_loaded_blocks.insert(p, !block_data_inexistent);
4405 void ManualMapVoxelManipulator::blitBackAll(
4406 core::map<v3s16, MapBlock*> * modified_blocks)
4408 if(m_area.getExtent() == v3s16(0,0,0))
4412 Copy data of all blocks
4414 for(core::map<v3s16, bool>::Iterator
4415 i = m_loaded_blocks.getIterator();
4416 i.atEnd() == false; i++)
4418 v3s16 p = i.getNode()->getKey();
4419 bool existed = i.getNode()->getValue();
4420 if(existed == false)
4422 // The Great Bug was found using this
4423 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4424 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4428 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4431 infostream<<"WARNING: "<<__FUNCTION_NAME
4432 <<": got NULL block "
4433 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4438 block->copyFrom(*this);
4441 modified_blocks->insert(p, block);